diff --git a/404.html b/404.html index 814d568c44..0f0858721e 100644 --- a/404.html +++ b/404.html @@ -1,5 +1,5 @@ -404 Page not found | Community Health Toolkit -

Not found

Oops! This page doesn't exist. Try going back to the home page.

\ No newline at end of file +

Not found

Oops! This page doesn't exist. Try going back to the home page.

\ No newline at end of file diff --git a/apps/concepts/access/index.html b/apps/concepts/access/index.html index 4cce1e6a6f..81bfa6e91e 100644 --- a/apps/concepts/access/index.html +++ b/apps/concepts/access/index.html @@ -1,9 +1,9 @@ -Accessing CHT Apps | Community Health Toolkit +Accessing CHT Apps | Community Health Toolkit

Accessing CHT Apps

Starting up your digital health apps

Apps built with the Core Framework run on most modern computers with the newest versions of Google Chrome or Mozilla Firefox.

Hardware & Software Requirements

Hardware procurement, ownership, and management is the responsibility of each implementing organization. We strongly urge all organizations to procure hardware locally to ensure ease of replacement, repair, sustainability, and hardware support when needed.

Accessing on Desktop

On desktop devices, there is no need to download anything. Simply go to a web browser and type in your unique URL, for example:

{{projectname}}.app.medicmobile.org

Accessing on Mobile

The app also runs with an app on Android phones and tablets. It works best on devices running version 5.1 or later with at least 8 GB of internal memory (16 GB for supervisors) and minimum 1 GB RAM.

Downloading and Launching

To download your app on a mobile device, first navigate to the Google Play Store. From there, click on the search icon in the upper right, and type in the custom name of your health app or project. Make sure the app shown is the correct one and then select it. Then, click on the “Install” button to begin the download.

Once the download is complete, you can access your app via an app icon in your applications menu. Note that the icon, as well as the app name displayed, is customizable by the organization or project.

Login

When accessing your app for the very first time, a login page is displayed. Users enter a username and password that grant access to their customized app experience.

On mobile devices, the app generally stays logged in after initial setup so that CHW users don’t have to type in their credentials each day.

On desktop devices, the user must login again if they close the app tab or browser window.

Users may log out by going to the options menu available in the top right corner of the app.

See Also: Navigating CHT Apps

Animated image showing the 'Enable login via SMS link' check box being clicked and 'password' and 'confirm password' fields being hidden

When creating users, the admin has the option to enable a user to login in by simply clicking a link sent via SMS. When the token login link is clicked and the app is not installed on the user’s phone, it will open in their default browser. If no gateway is set up on the CHT server, the message may be sent via another messaging app. The link is only valid for 24 hours and can only be used once to log in. This ensures the link is used only by the intended recipient. By clicking the magic link, the user is logged into their project’s instance directly, bypassing the need to manually enter a username and password.

With token login, the password is never known by the admin or the user because the password is changed to a random string after every successful token login. If the user needs to login again, they need to contact the admin so that the admin can either send a new magic link or switch their account back to using a manual login and password.

See Also: Remote Onboarding and Training

See Also: .token_login

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/concepts/care-guides/index.html b/apps/concepts/care-guides/index.html index 9efe89326a..31ec68fa61 100644 --- a/apps/concepts/care-guides/index.html +++ b/apps/concepts/care-guides/index.html @@ -1,9 +1,9 @@ -Care Guides | Community Health Toolkit +Care Guides | Community Health Toolkit

Care Guides

Taking health workers through care protocols and providing decision support

Care Guides

Forms are used to build “Care Guides” that take health workers through care protocols and provide decision support for their interactions with patients. App designers can use the basic form building functionality in a variety of ways.

Care Guides also allow CHWs to register new families and people, assess a sick child, and enroll a new pregnancy into an antenatal care schedule. Care Guides can be located in many parts of your app, including the Tasks, People, and Reports tabs.

Care Guides provided in the CHT’s Reference Applications can be configured for your app, or a new Care Guide can be written from scratch. Some configuration is probably necessary due to different local requirements, and government protocols.

Functionality

Care Guides consists of questions grouped into pages. They are capable of presenting many different types of questions, skip logic, images, and videos. Validation rules can require certain questions to be answered or restrict answers to a specified type or range.

It’s possible to reference previous information that was submitted about the person or household from within the care guide. The interaction can also conclude with a summary that includes assessment results, treatment recommendations, and referral info.

Care Guides can include images for instructional purposes and can access a user’s camera to take a photo if needed.

Summary

After all of the required questions have been answered, a summary page can be displayed.

Here, health workers can review the information they entered, receive instructions for treatment, care, and referrals, and relay detailed education to the patient.

Examples

  • While a health worker is going through the form during the care visit, you can include a family planning question only if the person who the form is about is a woman and not pregnant.
  • You can include on-the-spot conversational prompts and advice for the CHW based on how they answer questions in the form. For instance, if a CHW answers “yes” to the question about a woman’s interest in family planning, text can automatically appear to provide information on her options.
  • An image showing how to read a rapid test can be displayed within a form, to help health workers to correctly interpret their test results.

CHT Applications > + Create project issue

Care Guides

Taking health workers through care protocols and providing decision support

Care Guides

Forms are used to build “Care Guides” that take health workers through care protocols and provide decision support for their interactions with patients. App designers can use the basic form building functionality in a variety of ways.

Care Guides also allow CHWs to register new families and people, assess a sick child, and enroll a new pregnancy into an antenatal care schedule. Care Guides can be located in many parts of your app, including the Tasks, People, and Reports tabs.

Care Guides provided in the CHT’s Reference Applications can be configured for your app, or a new Care Guide can be written from scratch. Some configuration is probably necessary due to different local requirements, and government protocols.

Functionality

Care Guides consists of questions grouped into pages. They are capable of presenting many different types of questions, skip logic, images, and videos. Validation rules can require certain questions to be answered or restrict answers to a specified type or range.

It’s possible to reference previous information that was submitted about the person or household from within the care guide. The interaction can also conclude with a summary that includes assessment results, treatment recommendations, and referral info.

Care Guides can include images for instructional purposes and can access a user’s camera to take a photo if needed.

Summary

After all of the required questions have been answered, a summary page can be displayed.

Here, health workers can review the information they entered, receive instructions for treatment, care, and referrals, and relay detailed education to the patient.

Examples

  • While a health worker is going through the form during the care visit, you can include a family planning question only if the person who the form is about is a woman and not pregnant.
  • You can include on-the-spot conversational prompts and advice for the CHW based on how they answer questions in the form. For instance, if a CHW answers “yes” to the question about a woman’s interest in family planning, text can automatically appear to provide information on her options.
  • An image showing how to read a rapid test can be displayed within a form, to help health workers to correctly interpret their test results.

CHT Applications > Concepts > Forms

Building block for all CHT apps

CHT Applications > Features > @@ -309,7 +309,8 @@ Reference > contact-summary.templated.js : Care Guides

Contact Pages: Customizing the fields, cards, and actions on profile pages

-

Last modified 16.09.2020: concepts (aec5b1b3)
\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/concepts/forms/index.html b/apps/concepts/forms/index.html index 06092ec5ed..74ad2a27ff 100644 --- a/apps/concepts/forms/index.html +++ b/apps/concepts/forms/index.html @@ -1,9 +1,9 @@ -Forms | Community Health Toolkit +Forms | Community Health Toolkit

Forms

Building block for all CHT apps

Forms are a building block of all CHT apps. They are used when creating or editing contacts, and when completing a care guide or survey within the app. Forms are also used to interpret SMS interactions with the CHT.

When a completed form is submitted, it is treated as a Report in the app. All reports can be viewed in the Reports tab by those with the proper access within the hierarchy.

There are four different types of forms:

  • Contact Forms: used to create and edit contacts. Defined as CHT-enhanced XForms.
  • App Forms: serve as actions within the app, such as a task or an action. Defined as CHT-enhanced XForms.
  • Collect Forms: used to render forms in Medic Collect. Defined as ODK XForms and need a corresponding JSON form to receive data in CHT.
  • JSON Forms: used for data coming from external channels such as SMS, or via interoperability with other tools. Defined according to a JavaScript Object Notation schema.

Forms that can be completed in the app are built using a CHT-enhanced version of ODK XForms notation – a XML definition of the structure and format for a set of questions. Since writing raw XML can be tedious, the XLSForm standard is commonly used to define forms. The cht-conf command line tool can be used to convert to the XForm format and include the form in a CHT application. The instructions on this site assume some knowledge of XLSForm.


CHT Applications > + Create project issue

Forms

Building block for all CHT apps

Forms are a building block of all CHT apps. They are used when creating or editing contacts, and when completing a care guide or survey within the app. Forms are also used to interpret SMS interactions with the CHT.

When a completed form is submitted, it is treated as a Report in the app. All reports can be viewed in the Reports tab by those with the proper access within the hierarchy.

There are four different types of forms:

  • Contact Forms: used to create and edit contacts. Defined as CHT-enhanced XForms.
  • App Forms: serve as actions within the app, such as a task or an action. Defined as CHT-enhanced XForms.
  • Collect Forms: used to render forms in Medic Collect. Defined as ODK XForms and need a corresponding JSON form to receive data in CHT.
  • JSON Forms: used for data coming from external channels such as SMS, or via interoperability with other tools. Defined according to a JavaScript Object Notation schema.

Forms that can be completed in the app are built using a CHT-enhanced version of ODK XForms notation – a XML definition of the structure and format for a set of questions. Since writing raw XML can be tedious, the XLSForm standard is commonly used to define forms. The cht-conf command line tool can be used to convert to the XForm format and include the form in a CHT application. The instructions on this site assume some knowledge of XLSForm.


CHT Applications > Concepts > Care Guides

Taking health workers through care protocols and providing decision support

CHT Applications > Quick Guides > @@ -310,7 +310,8 @@ Reference > app_settings.json > .forms

JSON Forms: Instructions and schema for defining JSON forms used for handling reports from SMS and external tools

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/concepts/hierarchy/index.html b/apps/concepts/hierarchy/index.html index 4fc42bff6a..909e3e4a22 100644 --- a/apps/concepts/hierarchy/index.html +++ b/apps/concepts/hierarchy/index.html @@ -1,9 +1,9 @@ -Configurable Hierarchies | Community Health Toolkit +Configurable Hierarchies | Community Health Toolkit

Configurable Hierarchies

Organizing people and places, and their relationship to one-another

The Core Framework requires a hierarchy to organize the information in the app. It is often based on the hierarchy of a health system within a particular geographic region.

Large deployment sites often need three or more levels of place hierarchy, while some small sites need fewer than three levels. For this reason, the Core Framework’s information hierarchies are configurable to meet a users needs.

A user logging into their app will see a custom set of people, tasks, reports, and analytics based on the hierarchy level that they belong to. This allows appropriate data sharing based on the role of the user in the health system.

The information hierarchy is configured in the administration console. The hierarchy levels can be given different titles depending on a particular health system’s program or reporting structures.

See Also: Defining Hierarchy

Places

This is an example of a simple hierarchy that includes a CHW Supervisor area, CHW area, and families as levels which serve as “places” or units of organizing people.

flowchart TB
+ Create project issue

Configurable Hierarchies

Organizing people and places, and their relationship to one-another

The Core Framework requires a hierarchy to organize the information in the app. It is often based on the hierarchy of a health system within a particular geographic region.

Large deployment sites often need three or more levels of place hierarchy, while some small sites need fewer than three levels. For this reason, the Core Framework’s information hierarchies are configurable to meet a users needs.

A user logging into their app will see a custom set of people, tasks, reports, and analytics based on the hierarchy level that they belong to. This allows appropriate data sharing based on the role of the user in the health system.

The information hierarchy is configured in the administration console. The hierarchy levels can be given different titles depending on a particular health system’s program or reporting structures.

See Also: Defining Hierarchy

Places

This is an example of a simple hierarchy that includes a CHW Supervisor area, CHW area, and families as levels which serve as “places” or units of organizing people.

flowchart TB
 linkStyle default stroke-width:1px,stroke:lightgrey
 
 classDef none fill:none,stroke:none
@@ -382,7 +382,63 @@
 Reference >
 app_settings.json >
 .contact_types

Hierarchy: Setting the types of places and people in the hierarchy

-

\ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/concepts/index.html b/apps/concepts/index.html index 2d2c2ecf5a..4842ed8d8e 100644 --- a/apps/concepts/index.html +++ b/apps/concepts/index.html @@ -1,9 +1,9 @@ -Concepts | Community Health Toolkit +Concepts | Community Health Toolkit

Concepts

Basic concepts that will help you understand how CHT applications are built

CHT Applications support health workers with activities in their community. Health workers are guided through tasks — such as screening for high-risk pregnancies or diagnosing and providing treatments for children — and get real-time indicators for progress towards their goals. These workflows are highly flexible and developed using a combination of JavaScript, JSON, and XForms.

The JavaScript and JSON portions allow for full control of tasks, targets, and contact profiles, as well as other application settings. Forms can live in many parts of the app including the Tasks, People, and Reports pages. The XForms portion:

  • extends the ODK XForm notation
  • is used to define forms
  • allows CHWs to register new families and people
  • allows CHWs to complete surveys, care guides, and decision support (e.g., when assessing a sick child, registering a new pregnancy for an antenatal care visit)

Accessing CHT Apps

Starting up your digital health apps

Navigating CHT Apps

Browsing your digital health apps

Forms

Building block for all CHT apps

Care Guides

Taking health workers through care protocols and providing decision support

Building Workflows

Building connections between people, actions, and data systems

Configurable Hierarchies

Organizing people and places, and their relationship to one-another

Users

Defining the user roles and their permissions

Interoperability

Exchanging information between the CHT Core and other health systems

Prerequisites for App Development

Tools and background skills that are helpful for developing CHT apps

-

Last modified 15.12.2020: apply reviewer suggestion (078f0797)
\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/concepts/index.xml b/apps/concepts/index.xml index 4e09427396..1bd8331423 100644 --- a/apps/concepts/index.xml +++ b/apps/concepts/index.xml @@ -1,414 +1,12 @@ -Community Health Toolkit – Conceptshttps://docs.communityhealthtoolkit.org/apps/concepts/Recent content in Concepts on Community Health ToolkitHugo -- gohugo.ioenApps: Accessing CHT Appshttps://docs.communityhealthtoolkit.org/apps/concepts/access/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/concepts/access/ -<p>Apps built with the Core Framework run on most modern computers with the newest versions of Google Chrome or Mozilla Firefox.</p> -<h2 id="hardware--software-requirements">Hardware &amp; Software Requirements</h2> -<p>Hardware procurement, ownership, and management is the responsibility of each implementing organization. We strongly urge all organizations to procure hardware locally to ensure ease of replacement, repair, sustainability, and hardware support when needed.</p> -<h2 id="accessing-on-desktop">Accessing on Desktop</h2> -<p>On desktop devices, there is no need to download anything. Simply go to a web browser and type in your unique URL, for example:</p> -<p><code>{{projectname}}.app.medicmobile.org</code></p> -<h2 id="accessing-on-mobile">Accessing on Mobile</h2> -<p>The app also runs with an app on Android phones and tablets. It works best on devices running version 5.1 or later with at least 8 GB of internal memory (16 GB for supervisors) and minimum 1 GB RAM.</p> -<h3 id="downloading-and-launching">Downloading and Launching</h3> -<p>To download your app on a mobile device, first navigate to the Google Play Store. From there, click on the search icon in the upper right, and type in the custom name of your health app or project. Make sure the app shown is the correct one and then select it. Then, click on the “Install” button to begin the download.</p> -<p>Once the download is complete, you can access your app via an app icon in your applications menu. Note that the icon, as well as the app name displayed, is customizable by the organization or project.</p> -<figure class="left col-3 col-lg-3"><a href="playstore.png"> -<img src="playstore.png"/> </a> -</figure> -<figure class="left col-3 col-lg-3"><a href="search-results.png"> -<img src="search-results.png"/> </a> -</figure> -<figure class="left col-3 col-lg-3"><a href="install.png"> -<img src="install.png"/> </a> -</figure> -<figure class="left col-3 col-lg-3"><a href="siaya.png"> -<img src="siaya.png"/> </a> -</figure> -<h2 id="login">Login</h2> -<p>When accessing your app for the very first time, a login page is displayed. Users enter a username and password that grant access to their customized app experience.</p> -<p>On mobile devices, the app generally stays logged in after initial setup so that CHW users don’t have to type in their credentials each day.</p> -<p>On desktop devices, the user must login again if they close the app tab or browser window.</p> -<p>Users may log out by going to the options menu available in the top right corner of the app.</p> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/concepts/navigation/">Navigating CHT Apps</a></p> -<figure class="left col-3 col-lg-3"><a href="login-mobile.png"> -<img src="login-mobile.png"/> </a> -</figure> -<figure class="left col-9 col-lg-9"><a href="login-desktop.png"> -<img src="login-desktop.png"/> </a> -</figure> -<h2 id="magic-links-for-logging-in-token-login">Magic Links for Logging In (Token Login)</h2> -<figure class="right col-6 col-lg-9"> -<img src="enable.token.login.gif" -alt="Animated image showing the &#39;Enable login via SMS link&#39; check box being clicked and &#39;password&#39; and &#39;confirm password&#39; fields being hidden"/> -</figure> -<p>When creating users, the admin has the option to enable a user to login in by simply clicking a link sent via SMS. When the token login link is clicked and the app is not installed on the user&rsquo;s phone, it will open in their default browser. If no gateway is set up on the CHT server, the message may be sent via another messaging app. The link is only valid for 24 hours and can only be used once to log in. This ensures the link is used only by the intended recipient. By clicking the magic link, the user is logged into their project&rsquo;s instance directly, bypassing the need to manually enter a username and password.</p> -<p>With token login, the password is never known by the admin or the user because the password is changed to a random string after every successful token login. If the user needs to login again, they need to contact the admin so that the admin can either send a new magic link or switch their account back to using a manual login and password.</p> -<p> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/examples/training/">Remote Onboarding and Training</a></p> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/token_login/">.token_login</a></p> -</p> -<figure class="left col-3 col-lg-3"><a href="link.png"> -<img src="link.png"/> </a> -</figure> -<figure class="left col-3 col-lg-3"><a href="open-with.png"> -<img src="open-with.png"/> </a> -</figure> -<figure class="left col-3 col-lg-3"><a href="log-in.png"> -<img src="log-in.png"/> </a> -</figure> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -The magic link workflow will not work for users who want to use multiple devices or for multiple users on one device. -</div>Apps: Navigating CHT Appshttps://docs.communityhealthtoolkit.org/apps/concepts/navigation/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/concepts/navigation/ -<h2 id="summary-of-page-tabs">Summary of Page Tabs</h2> -<p>Page tabs are the primary way to navigate apps built with the Core Framework. The number of tabs is variable depending on the user’s role and place in the hierarchy. For example, non-admin users don’t have Messages. The Reports tab is accessible to CHWs but often located inside the secondary menu drawer.</p> -<figure class="left col-12 col-lg-7"><a href="tabs.gif"> -<img src="tabs.gif"/> </a> -</figure> -<p><br><br><br><br></p> -<ul> -<li><strong>Messages​</strong>: A place for community-based staff to send and exchange messages</li> -<li><strong>Tasks​</strong>: This is a list of upcoming visits, follow-ups, or other required tasks</li> -<li><strong>Reports​</strong>: A detailed history of all forms submitted by CHWs and other staff</li> -<li><strong>People​</strong>: This is where profiles of districts, staff, CHWs and patients live</li> -<li><strong>Targets</strong>: Displays real-time visualizations of key activity and impact indicators</li> -</ul> -<h2 id="the-menu-drawer">The Menu Drawer</h2> -<figure class="right col-6 col-lg-4"><a href="menu.png"> -<img src="menu.png"/> </a> -</figure> -<p>Tap the menu icon in the upper right corner of the header to access other pages, edit personal settings, view sync status and more.</p> -<ul> -<li><strong>Admin Console</strong>: Change advanced app settings (only admin users will see this)</li> -<li><strong>About</strong>: View your app version and other detailed database information</li> -<li><strong>User Settings</strong>: Update basic user information like email, phone number, and password</li> -<li><strong>Report Bug</strong>: Let us know if something isn’t working or you encounter errors</li> -<li><strong>Log Out</strong>: Sign out of the app</li> -</ul> -<h3 id="sync-status">Sync Status</h3> -<figure class="right col-6 col-lg-4"><a href="sync-status.png"> -<img src="sync-status.png"/> </a> -</figure> -<p>Data synchronization is important for <a href="https://docs.communityhealthtoolkit.org/apps/concepts/users/">offline users</a>. These users keep a copy of the data they have access to on their device. They can work from their device while disconnected from the internet (offline), by reading from and writing to their copy of the data. “Sync” (synchronization) is when data on the device is made to match the data on the server and requires an internet connection. The CHT app monitors the online status and attempts sync accordingly.</p> -<h4 id="replication-types">Replication Types</h4> -<p>Synchronization consists of upward replication and downward replication.</p> -<ul> -<li><strong>Upward Replication</strong>: Uploading all new or updated data from the device to the server. It includes a retry mechanism for handling larger data batches, ensuring a robust and reliable upload process.</li> -<li><strong>Downward Replication</strong>: Downloading new or updated data from the server to the device. Downward replication may include the download of software updates to the CHT app when available.</li> -</ul> -<p>The CHT application manages data synchronization across two types of databases:</p> -<ul> -<li><strong>Main Application Database (<code>medic</code>)</strong>: The main database that stores the primary data used by the application. It includes contacts, reports, messages, and other critical documents necessary for the core functionality of the application. This database is synchronized continuously to reflect changes to the application, such as new contact creations. Each user stores a subset of the main database which includes only the documents they&rsquo;re allowed to view.</li> -<li><strong>User-Metadata Database(<code>medic-user-{username}-meta</code>)</strong>: Each user has a dedicated database that stores operational metadata, including <a href="https://docs.communityhealthtoolkit.org/apps/guides/performance/telemetry/">telemetry data</a> and error messages. Synchronization occurs at predefined intervals to ensure up-to-date monitoring and analysis.</li> -</ul> -<h4 id="sync-status-notification">Sync Status Notification</h4> -<p>At the bottom of the menu is a notification which provides important information about data synchronization.</p> -<p>If the sync status is green and says “All reports synced,” this means you have successfully uploaded the most recent data on your device to the server. It also means that you downloaded the latest data from the server as of the time displayed. Note that there could be more recent data changes on the server, and it doesn&rsquo;t guarantee you are up to date.</p> -<p>If the indicator is red, it means you have data changes waiting to be uploaded to the server. You should check your internet and data connection to ensure a successful sync.</p> -<aside class="right col-6 col-lg-4"> -<figure><a href="sync-inprogress.jpg"> -<img src="sync-inprogress.jpg"/> </a> -</figure> -<figure><a href="sync-failure.jpg"> -<img src="sync-failure.jpg"/> </a> -</figure> -<figure><a href="sync-successful.jpg"> -<img src="sync-successful.jpg"/> </a> -</figure> -</aside> -<p>Triggering a manual sync by clicking the &ldquo;Sync now&rdquo; button will provide feedback at every step of the process through a snackbar appearing on the bottom side of the screen. This performs upward and downward synchronization of both databases. It will also retry the sync process in case of failure..</p> -<h4 id="synchronization-triggers">Synchronization Triggers</h4> -<ul> -<li><strong>On Login</strong>: Synchronization is automatically initiated upon successful user login if the app is connected to the internet.</li> -<li><strong>Manual</strong>: Clicking the &ldquo;Sync now&rdquo; button.</li> -<li><strong>Periodic Sync</strong>: The application performs regular checks and attempts to synchronize. The main application database syncs every 5 minutes, while the user metadata database syncs every 30 minutes..</li> -<li><strong>On Reload</strong>: Synchronization is automatically initiated when the user reloads the application, refreshes the page, or clicks the reload button in the &ldquo;Update available&rdquo; modal.</li> -<li><strong>On Connect</strong>: The app also detects when an internet connection becomes available and attempts to sync immediately.</li> -</ul> -<h4 id="sync-status-states">Sync Status States</h4> -<p>The synchronization process can be in one of the following states:</p> -<ul> -<li><strong>Unknown</strong>: The sync status is not determined yet.</li> -<li><strong>Disabled</strong>: Sync is disabled - only applies to online-only users.</li> -<li><strong>InProgress</strong>: Synchronization is currently ongoing.</li> -<li><strong>Success</strong>: The last sync operation was successful.</li> -<li><strong>Required</strong>: There is data pending synchronization.</li> -</ul>Apps: Formshttps://docs.communityhealthtoolkit.org/apps/concepts/forms/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/concepts/forms/ -<p>Forms are a building block of all CHT apps. They are used when creating or editing contacts, and when completing a care guide or survey within the app. Forms are also used to interpret SMS interactions with the CHT.</p> -<p>When a completed form is submitted, it is treated as a Report in the app. All reports can be viewed in the <a href="https://docs.communityhealthtoolkit.org/apps/features/reports/">Reports tab</a> by those with the proper access within the <a href="https://docs.communityhealthtoolkit.org/apps/concepts/hierarchy/">hierarchy</a>.</p> -<p>There are four different types of forms:</p> -<ul> -<li><a href="https://docs.communityhealthtoolkit.org/apps/reference/forms/contact/"><strong>Contact Forms</strong></a>: used to create and edit contacts. Defined as CHT-enhanced XForms.</li> -<li><a href="https://docs.communityhealthtoolkit.org/apps/reference/forms/app/"><strong>App Forms</strong></a>: serve as actions within the app, such as a task or an action. Defined as CHT-enhanced XForms.</li> -<li><a href="https://docs.communityhealthtoolkit.org/apps/reference/forms/collect/"><strong>Collect Forms</strong></a>: used to render forms in Medic Collect. Defined as ODK XForms and need a corresponding JSON form to receive data in CHT.</li> -<li><a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/forms/"><strong>JSON Forms</strong></a>: used for data coming from external channels such as SMS, or via interoperability with other tools. Defined according to a JavaScript Object Notation schema.</li> -</ul> -<p>Forms that can be completed in the app are built using a CHT-enhanced version of <a href="https://opendatakit.github.io/xforms-spec/">ODK XForms</a> notation &ndash; a XML definition of the structure and format for a set of questions. Since writing raw XML can be tedious, the <a href="http://xlsform.org/">XLSForm standard</a> is commonly used to define forms. The <a href="https://github.com/medic/cht-conf">cht-conf</a> command line tool can be used to convert to the XForm format and include the form in a CHT application. The instructions on this site assume some knowledge of XLSForm.</p>Apps: Care Guideshttps://docs.communityhealthtoolkit.org/apps/concepts/care-guides/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/concepts/care-guides/ -<h2 id="care-guides">Care Guides</h2> -<figure class="right col-12 col-lg-6"><a href="care-guides.png"> -<img src="care-guides.png"/> </a> -</figure> -<p>Forms are used to build “Care Guides” that take health workers through care protocols and provide decision support for their interactions with patients. App designers can use the basic form building functionality in a variety of ways.</p> -<p>Care Guides also allow CHWs to register new families and people, assess a sick child, and enroll a new pregnancy into an antenatal care schedule. Care Guides can be located in many parts of your app, including the Tasks, People, and Reports tabs.</p> -<p>Care Guides provided in the CHT&rsquo;s Reference Applications can be configured for your app, or a new Care Guide can be written from scratch. Some configuration is probably necessary due to different local requirements, and government protocols.</p> -<h3 id="functionality">Functionality</h3> -<figure class="right col-12 col-lg-4"><a href="functionality.png"> -<img src="functionality.png"/> </a> -</figure> -<p>Care Guides consists of questions grouped into pages. They are capable of presenting many different types of questions, skip logic, images, and videos. Validation rules can require certain questions to be answered or restrict answers to a specified type or range.</p> -<p>It’s possible to reference previous information that was submitted about the person or household from within the care guide. The interaction can also conclude with a summary that includes assessment results, treatment recommendations, and referral info.</p> -<p>Care Guides can include images for instructional purposes and can access a user’s camera to take a photo if needed.</p> -<h3 id="summary">Summary</h3> -<figure class="right col-12 col-lg-4"><a href="summary.png"> -<img src="summary.png"/> </a> -</figure> -<p>After all of the required questions have been answered, a summary page can be displayed.</p> -<p>Here, health workers can review the information they entered, receive instructions for treatment, care, and referrals, and relay detailed education to the patient.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -The form is not submitted until the user scrolls to the very end of the summary and clicks the <code>Submit</code> button. -</div> -<h3 id="examples">Examples</h3> -<figure class="right col-12 col-lg-4"><a href="examples.png"> -<img src="examples.png"/> </a> -</figure> -<ul> -<li>While a health worker is going through the form during the care visit, you can include a family planning question only if the person who the form is about is a woman and not pregnant.</li> -<li>You can include on-the-spot conversational prompts and advice for the CHW based on how they answer questions in the form. For instance, if a CHW answers “yes” to the question about a woman’s interest in family planning, text can automatically appear to provide information on her options.</li> -<li>An image showing how to read a rapid test can be displayed within a form, to help health workers to correctly interpret their test results.</li> -</ul>Apps: Building Workflowshttps://docs.communityhealthtoolkit.org/apps/concepts/workflows/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/concepts/workflows/ -<p>Workflows can be defined within apps built with the Core Framework to connect actions and data with people. Forms are the main building block of tasks and messaging workflows, and are useful in creating reminders for follow-up visits or referrals.</p> -<h2 id="tasks">Tasks</h2> -<p>Tasks within the app can drive a workflow, ensuring that the right actions are taken for people at the right time. Tasks indicate a recommended action to the user. They indicate who the user should perform the action with, and the recommended timeframe of that action. When the user taps the task, they are directed to a form where the details of the action are captured.</p> -<p>Tasks can be triggered by a set of conditions, such as contact details or submitted reports. Tasks are accessible in the Tasks tab and the profile in the Contact tab, and initiate a follow up action to complete a form. More information on building app workflows is available in the <a href="https://docs.communityhealthtoolkit.org/apps/features/tasks/">Tasks section</a>.</p> -<p>Data submitted in one form can generate several tasks at once, for example, multiple ANC visits following one pregnancy registration. Some workflows involve a series of sequential forms and tasks, such as a child health assessment form, a follow up task scheduled 48 hours later, a referral form (only if the child’s condition hasn’t improved), and then a referral follow up task. Tasks are accessible on the Tasks tab, as well as the Tasks section of profiles. -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/reference/tasks/">Defining Tasks</a></p> -</p> -<p> -<figure class="left col-3 col-lg-3"><a href="tasks-mobile.png"> -<img src="tasks-mobile.png"/> </a> -</figure> -<figure class="left col-9 col-lg-9"><a href="tasks-desktop.png"> -<img src="tasks-desktop.png"/> </a> -</figure> -</p> -<h2 id="sms-messaging">SMS Messaging</h2> -<p>Workflows can include notifications and interactions with CHWs, nurses, supervisors, and patients via SMS. A report can trigger SMS messages to be sent immediately or upon a set schedule. Responses via SMS or the app can update the workflows. -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/#sms-workflows">Defining SMS Workflows</a></p> -</p> -<p> -<figure class="left col-3 col-lg-3"><a href="messages-mobile.png"> -<img src="messages-mobile.png"/> </a> -</figure> -<figure class="left col-9 col-lg-9"><a href="messages-desktop.png"> -<img src="messages-desktop.png"/> </a> -</figure> -</p> -<h2 id="interoperability">Interoperability</h2> -<p>Workflows can incorporate other digital tools, such as a facility-based electronic medical record system for referral workflows. New contacts or reports can trigger an interoperabilty workflow using the <a href="">outbound push</a> feature. Data can be received as reports using the <a href="https://github.com/medic/cht-core/tree/master/api">CHT API</a> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/outbound/">Outbound Push</a></p> -</p>Apps: Configurable Hierarchieshttps://docs.communityhealthtoolkit.org/apps/concepts/hierarchy/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/concepts/hierarchy/ -<p>The Core Framework requires a hierarchy to organize the information in the app. It is often based on the hierarchy of a health system within a particular geographic region.</p> -<p>Large deployment sites often need three or more levels of place hierarchy, while some small sites need fewer than three levels. For this reason, the Core Framework’s information hierarchies are configurable to meet a users needs.</p> -<p>A user logging into their app will see a custom set of people, tasks, reports, and analytics based on the hierarchy level that they belong to. This allows appropriate data sharing based on the role of the user in the health system.</p> -<p>The information hierarchy is configured in the administration console. The hierarchy levels can be given different titles depending on a particular health system’s program or reporting structures.</p> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/hierarchy/">Defining Hierarchy</a></p> -<h3 id="places">Places</h3> -<p>This is an example of a simple hierarchy that includes a CHW Supervisor area, CHW area, and families as levels which serve as “places” or units of organizing people.</p> -<pre class="mermaid">flowchart TB -linkStyle default stroke-width:1px,stroke:lightgrey -classDef none fill:none,stroke:none -super_area_a[&lt;img src=&#39;health-center.svg&#39; width=&#39;30&#39; /&gt;\nCHW Supervisor Area A]:::none -chw_area_a[&lt;img src=&#39;chw-area.svg&#39; width=&#39;30&#39; /&gt;\nCHW CHW Area A]:::none -chw_area_b[&lt;img src=&#39;chw-area.svg&#39; width=&#39;30&#39; /&gt;\nCHW CHW Area B]:::none -chw_area_c[&lt;img src=&#39;chw-area.svg&#39; width=&#39;30&#39; /&gt;\nCHW CHW Area C]:::none -family_a[&lt;img src=&#39;family.svg&#39; width=&#39;30&#39; /&gt;\nFamily A]:::none -family_b[&lt;img src=&#39;family.svg&#39; width=&#39;30&#39; /&gt;\nFamily B]:::none -family_c[&lt;img src=&#39;family.svg&#39; width=&#39;30&#39; /&gt;\nFamily C]:::none -family_d[&lt;img src=&#39;family.svg&#39; width=&#39;30&#39; /&gt;\nFamily D]:::none -family_e[&lt;img src=&#39;family.svg&#39; width=&#39;30&#39; /&gt;\nFamily E]:::none -family_f[&lt;img src=&#39;family.svg&#39; width=&#39;30&#39; /&gt;\nFamily F]:::none -super_area_a --- chw_area_a &amp; chw_area_b &amp; chw_area_c -chw_area_a --- family_a &amp; family_b -chw_area_b --- family_c &amp; family_d -chw_area_c --- family_e &amp; family_f</pre> -<p>User roles can be assigned to log in at any of these levels. For example, it would be customary for a CHW to log in at the CHW Area level and view the families, and below that the people, i.e. patients or family members, who belong there.</p> -<h3 id="people">People</h3> -<p>The hierarchy can be modeled after the health system, health program and/or the community. All people are associated with a place and these places can be associated to each other.</p> -<p>For example, a Family Member is part of a Family. A Family and CHWs are part of a CHW Area. A Family Member, a Family, and CHWs are part of a CHW Supervisor Area.</p> -<pre class="mermaid">%%{init: { &#34;flowchart&#34;: { &#34;rankSpacing&#34;: 20, &#34;nodeSpacing&#34;: 10 } } }%% -flowchart TB -linkStyle default stroke-width:1px,stroke:lightgrey -classDef node fill:none,stroke:none -classDef cluster fill:none,stroke:#ccc -subgraph district[ ] -admin[&lt;img src=&#39;district-hospital.svg&#39; width=&#39;30&#39; /&gt;\nAdmin]:::none -officer[&lt;img src=&#39;officer.svg&#39; width=&#39;30&#39; /&gt;\nProgram Officer]:::none -admin --- officer -end -district:::none -subgraph supervision[ ] -super_area_a[&lt;img src=&#39;health-center.svg&#39; width=&#39;30&#39; /&gt;\nCHW Supervisor\nArea A]:::none -super_a[&lt;img src=&#39;supervisor.svg&#39; width=&#39;30&#39; /&gt;\nCHW Supervisor A]:::none -super_area_a --- super_a -end -supervision:::none -subgraph chw_group_a[ ] -chw_area_a[&lt;img src=&#39;chw-area.svg&#39; width=&#39;30&#39; /&gt;\nCHW\nArea A]:::none -chw_a[&lt;img src=&#39;chw.svg&#39; width=&#39;30&#39; /&gt;\nCHW]:::none -chw_area_a --- chw_a -end -chw_group_a:::none -subgraph chw_group_b[ ] -chw_area_b[&lt;img src=&#39;chw-area.svg&#39; width=&#39;30&#39; /&gt;\nCHW\nArea B]:::none -chw_b[&lt;img src=&#39;chw.svg&#39; width=&#39;30&#39; /&gt;\nCHW]:::none -chw_area_b --- chw_b -end -chw_group_b:::none -family_a[&lt;img src=&#39;family.svg&#39; width=&#39;30&#39; /&gt;\nFamily\nA]:::none -family_b[&lt;img src=&#39;family.svg&#39; width=&#39;30&#39; /&gt;\nFamily\nB]:::none -family_c[&lt;img src=&#39;family.svg&#39; width=&#39;30&#39; /&gt;\nFamily\nC]:::none -family_d[&lt;img src=&#39;family.svg&#39; width=&#39;30&#39; /&gt;\nFamily\nD]:::none -family_e[&lt;img src=&#39;family.svg&#39; width=&#39;30&#39; /&gt;\nFamily\nE]:::none -family_f[&lt;img src=&#39;family.svg&#39; width=&#39;30&#39; /&gt;\nFamily\nF]:::none -person_a_b[&lt;img src=&#39;person.svg&#39; width=&#39;30&#39; /&gt;&lt;img src=&#39;person.svg&#39; width=&#39;30&#39; /&gt;\nFamily\nMembers\nA and B]:::none -person_c[&lt;img src=&#39;person.svg&#39; width=&#39;30&#39; /&gt;\nFamily\nMember\nC]:::none -person_d[&lt;img src=&#39;person.svg&#39; width=&#39;30&#39; /&gt;\nFamily\nMember\nD]:::none -person_e_f[&lt;img src=&#39;person.svg&#39; width=&#39;30&#39; /&gt;&lt;img src=&#39;person.svg&#39; width=&#39;30&#39; /&gt;\nFamily\nMembers\nE and F]:::none -person_g[&lt;img src=&#39;person.svg&#39; width=&#39;30&#39; /&gt;\nFamily\nMember\nG]:::none -person_h_i_j[&lt;img src=&#39;person.svg&#39; width=&#39;30&#39; /&gt;&lt;img src=&#39;person.svg&#39; width=&#39;30&#39; /&gt;&lt;img src=&#39;person.svg&#39; width=&#39;30&#39; /&gt;\nFamily Members\nH, I, and J]:::none -district --- supervision -supervision --- chw_group_a &amp; chw_group_b -supervision ---- family_e &amp; family_f -chw_group_a --- family_a &amp; family_b -chw_group_b --- family_c &amp; family_d -family_a --- person_a_b -family_b --- person_c -family_c --- person_d -family_d --- person_e_f -family_e --- person_g -family_f --- person_h_i_j</pre> -<p>Additional hierarchy levels may be added as needed and each section of the hierarchy is configurable. For instance, many large projects have unbalanced hierarchies, which is to say, some parts of the hierarchy have more or different layers than others. -The Admin level operates outside of the hierarchy structure and enables access to all levels and people within the hierarchy.</p>Apps: Usershttps://docs.communityhealthtoolkit.org/apps/concepts/users/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/concepts/users/ -<p>Apps built with the Core Framework use roles and permissions to determine who has access to what data. User roles are general categories you can use to assign a collection of broad permissions to users. There are two classes of roles: online and offline. Generally speaking, CHWs are usually offline users, while managers and nurses are usually online users. SMS users do not use the app, and thus do not have a user role.</p> -<h2 id="roles">Roles</h2> -<p>Differing levels of access and permissions are assigned to each persona. A user role is created to provide them with access to the information they need. Offline and online access, storage limitations, and data privacy are taken into account.</p> -<table> -<thead> -<tr> -<th style="text-align:left">Persona</th> -<th style="text-align:left">Hierarchy</th> -<th style="text-align:left">Device</th> -<th style="text-align:left">Permissions</th> -</tr> -</thead> -<tbody> -<tr> -<td style="text-align:left">Program Officer</td> -<td style="text-align:left">Logs in as Admin</td> -<td style="text-align:left">Computer</td> -<td style="text-align:left">Admin users, usually Program Officers, are online-only admin users not associated to a particular level. They have access to all people, places, and records in the app, but since they are online-only users, they cannot view any tasks or targets.</td> -</tr> -<tr> -<td style="text-align:left">CHW Supervisors</td> -<td style="text-align:left">Logs in at Health Facility level</td> -<td style="text-align:left">Tablet</td> -<td style="text-align:left">User at this level have online and offline access to view CHWs, fill out reports about them, and view tasks and targets related to them. Due to storage limitations, they aren’t able to view households or submit reports and review tasks and targets about them.</td> -</tr> -<tr> -<td style="text-align:left">CHWs</td> -<td style="text-align:left">Logs in at CHW Area level</td> -<td style="text-align:left">Phone</td> -<td style="text-align:left">Users at this level have online and offline access to view households and family members, submit reports about them, and view tasks and targets about them.</td> -</tr> -<tr> -<td style="text-align:left">Family members</td> -<td style="text-align:left">Registered at Household level, does not log in</td> -<td style="text-align:left">Messaging</td> -<td style="text-align:left">Family members might include fathers, mothers, children, and other adults. The program model determines which family members should be registered in the app. However, they are not users of the app, and do not log in themselves.</td> -</tr> -</tbody> -</table> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-roles/">Defining User Roles</a></p> -<h3 id="online-users">Online Users</h3> -<p>Online roles are for users who need access to a lot of data and need to maintain the system or update system settings. An internet connection is required.</p> -<h3 id="offline-users">Offline Users</h3> -<p>Offline roles are for users who need to be able to access data on-the-go in the field and don’t have a reliable internet connection. All the data they have access to will be synced to their device. System administrators cannot be offline users as they won&rsquo;t have access to the app management tools offline.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Advanced configuration options are available for a specific offline user role to manage what <a href="https://docs.communityhealthtoolkit.org/apps/guides/performance/replication/">level of data</a> is synced to their device. -</div> -<h2 id="permissions">Permissions</h2> -<p>User Permissions are settings that can be individually toggled on or off to allow users with a particular Role to do a certain action or see a certain thing. CHT app developers and administrators can add as many User Roles as needed to grant permissions to different groups of users.</p> -<p>Viewing permissions determine which page tabs a user sees in the app and which types of data they do and don’t have access to. User action permissions include who can create (e.g., create new users), who can delete (e.g., delete reports), who can edit (e.g., edit profiles), and who can export (e.g., export server logs).</p> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-permissions/">Defining User Permissions</a></p> -<figure class="left col-12 col-lg-10"><a href="https://docs.communityhealthtoolkit.org/apps/features/admin/admin-roles.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/admin/admin-roles.png"/> </a> -</figure>Apps: Interoperabilityhttps://docs.communityhealthtoolkit.org/apps/concepts/interoperability/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/concepts/interoperability/ -<h2 id="introduction">Introduction</h2> -<p>Interoperability refers to the ability of different health information systems and applications to communicate with each other and exchange data seamlessly. With interoperability, patient information can be seen, exchanged, and used across different platforms. The information/data exchanged has to be understood across the different software for these systems to become interoperable. This is different from <em>integration</em> which requires custom development to connect two specific systems together.</p> -<p>Interoperability is the best practice for health systems because it allows information from one system to be shared with one or more other systems with no additional development. Interoperability allows technical teams to scale in an efficient and repeatable manner due to the already predefined standards.</p> -<h2 id="cht-interoperability">CHT Interoperability</h2> -<p>The native CHT database structure does not map directly to a <a href="http://www.hl7.org/fhir/">Fast Healthcare Interoperability Resources (FHIR)</a> message format. To be compatible, we use a middleware to convert the CHT data structure into a standardized JSON format so the other systems can read it. See below the data workflow:</p> -<pre class="mermaid">graph LR -cht[CHT] -mediator_a([Mediator]) -mediator_b([Mediator]) -openhim[OpenHIM] -cht -- Outbound push\nfa:fa-arrow-right --- mediator_a -cht -- API request\nfa:fa-arrow-left --- mediator_b -mediator_a -- Request\nfa:fa-arrow-right --- openhim -mediator_b -- Channel\nfa:fa-arrow-left --- openhim</pre> -<p>OpenHIM was utilised as the middleware component with <a href="http://openhim.org/docs/configuration/mediators/">Mediators</a> to do the conversion. <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/outbound/">Outbound Push</a> is configured to make a request to the middleware when relevant documents are created or modified in the CHT. A Mediator then creates a FHIR resource, which is then routed to OpenHIM. OpenHIM routes the resource to any other configured systems.</p> -<p>Conversely to bring data into the CHT, OpenHIM is configured to route the updated resource to the Mediator, which then calls the relevant <a href="https://docs.communityhealthtoolkit.org/apps/reference/api/">CHT APIs</a> to update the document in the CHT database. This will then be replicated to users’ devices as per usual.</p> -<h2 id="standards--components">Standards &amp; Components</h2> -<ul> -<li> -<p><a href="https://ohie.org/">OpenHIE</a>: OpenHIE is an open-source framework for building interoperable health information systems. OpenHIE provides a set of standards and protocols for enabling different health systems and applications to communicate with each other.</p> -</li> -<li> -<p><a href="http://openhim.org/">OpenHIM</a>: OpenHIM is an open-source middleware platform that provides a central point of control for managing health information exchange (HIE). OpenHIM enables healthcare providers to connect different health systems and applications and provides a common interface for managing data exchange and security.</p> -</li> -<li> -<p><a href="http://www.hl7.org/fhir">FHIR</a>: FHIR is a standard for exchanging healthcare data electronically. FHIR provides a modern, web-based approach to exchanging healthcare data and is rapidly becoming the preferred standard for healthcare interoperability.</p> -</li> -</ul> -<p>A reference application for this pattern is available in the <a href="https://github.com/medic/cht-interoperability">CHIS Interoperability repository</a>. -This application implements a Loss to Follow Up (LTFU) workflow system for CHIS based on the OpenHIE LTFU Guide.</p> -<h2 id="frequently-asked-questions">Frequently Asked Questions</h2> -<h3 id="is-the-cht-fhir-compatible-and-does-it-have-a-fhir-api">Is the CHT FHIR Compatible and does it have a FHIR API?</h3> -<p>Yes. Mediators are one of the components of a CHT deployment and expose FHIR compatible APIs to the rest of the healthcare ecosystem.</p> -<h3 id="does-the-cht-support-legacy-standards">Does the CHT support legacy standards?</h3> -<p>One of the advantages of using mediators is they are highly configurable to support different FHIR Implementation Guides, different FHIR versions, and other information standards, so the CHT can work with whatever systems are in the ecosystem.</p> -<h3 id="what-about-compatibility-with-future-standards">What about compatibility with future standards?</h3> -<p>The flexibility of mediators also means the CHT is future-proof and can be configured to support future FHIR revisions or completely new standards. Because this can be configured in the mediator layer it&rsquo;s likely to be supported without any Core development required.</p> -<h3 id="what-does-the-mediator-do-to-the-source-data">What does the mediator do to the source data?</h3> -<ol> -<li>It transforms the structure from the CHT format to the required standardized format.</li> -<li>It can make requests for additional data. This could be querying the Client Registry for the patient&rsquo;s national ID number, or other services such as the Terminology service to translate conditions, medications, procedures, and so on into the required classification system.</li> -<li>Finally it passes the FHIR resource to the interoperability layer to be shared with other systems.</li> -</ol> -<h3 id="what-are-the-fhir-resources-utilized">What are the FHIR Resources utilized?</h3> -<ol> -<li><a href="https://www.hl7.org/fhir/patient.html">Patient</a></li> -<li><a href="https://build.fhir.org/encounter.html">Encounter</a></li> -<li><a href="https://build.fhir.org/subscription.html">Subscription</a></li> -<li><a href="https://build.fhir.org/organization.html">Organization</a></li> -<li><a href="https://build.fhir.org/endpoint.html">Endpoint</a></li> -</ol> -<p>You can find additional information and instructions for setting up locally in the <a href="https://github.com/medic/cht-interoperability">cht-interoperability repository</a>.</p>Apps: Prerequisites for App Developmenthttps://docs.communityhealthtoolkit.org/apps/concepts/prerequisites/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/concepts/prerequisites/ -<p>There are no set prerequisites for users of CHT apps, yet the following are helpful for developing CHT applications.</p> -<h2 id="test-instance">Test Instance</h2> -<p>To build your own application using the Core Framework you will need an instance set up for testing. You can set up a local instance by <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/">following these instructions</a>.</p> -<h2 id="build-tool">Build tool</h2> -<p>The build tool for applications using the Core Framework is <code>cht-conf</code>. To set it up, follow the <a href="https://github.com/medic/cht-conf/blob/master/README.md">installation instructions</a>. To properly use the tool you will need your application files in set locations within a folder. Once you are set up with the basic file structure you can edit the files, and rebuild the application by compiling or converting components as needed, and uploading them to your test instance.</p> -<h2 id="background-skills">Background skills</h2> -<p>To build applications with the Core Framework the following skills are helpful, ordered by importance:</p> -<h3 id="xlsforms-and-xforms">XLSForms and XForms</h3> -<p>Many workflows in your application, including completing tasks and creating contacts, will be generated using <a href="https://opendatakit.github.io/xforms-spec/">ODK XForms</a>. Many app developers use XLSForms as an easier way to generate XForms. A strong knowledge of <a href="http://xlsform.org/">XLSForm standard</a> is very useful in building your own application.</p> -<h3 id="json">JSON</h3> -<p>JSON (JavaScript Object Notation) is a format for storing structured text. Understanding JSON will help with minor modification of existing applications.</p> -<h3 id="javascript">Javascript</h3> -<p>Many key aspects are defined with JavaScript code and expressions. This includes managing profile pages, creating tasks and targets, and setting the condition for when to show forms. Unless you are only doing minor modification to an existing application, a good understanding of JavaScript is required.</p> -<h3 id="couchdb">CouchDB</h3> -<p>A free and open source NoSQL database we use to store all our data, configuration, and even the application code. CouchDB is really good at replication which is the process of sending the data to another database, such as PouchDB in the client application, and back again. Although building your own app using the Core Framework does not require knowledge or experience with CouchDB it can be useful to be familiar with general concepts as a document store.</p> -<h3 id="sql">SQL</h3> -<p>Although the application you build uses a NoSQL database, a parallel PostgreSQL database is available in the Core Framework to make querying data easier. Familiarity with SQL is needed to set up and query the database.</p> \ No newline at end of file +Concepts on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/concepts/Recent content in Concepts on Community Health ToolkitHugo -- gohugo.ioenAccessing CHT Appshttps://docs.communityhealthtoolkit.org/apps/concepts/access/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/concepts/access/Apps built with the Core Framework run on most modern computers with the newest versions of Google Chrome or Mozilla Firefox. +Hardware &amp; Software Requirements Hardware procurement, ownership, and management is the responsibility of each implementing organization. We strongly urge all organizations to procure hardware locally to ensure ease of replacement, repair, sustainability, and hardware support when needed. +Accessing on Desktop On desktop devices, there is no need to download anything.Navigating CHT Appshttps://docs.communityhealthtoolkit.org/apps/concepts/navigation/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/concepts/navigation/Summary of Page Tabs Page tabs are the primary way to navigate apps built with the Core Framework. The number of tabs is variable depending on the user’s role and place in the hierarchy. For example, non-admin users don’t have Messages. The Reports tab is accessible to CHWs but often located inside the secondary menu drawer. +Messages​: A place for community-based staff to send and exchange messages Tasks​: This is a list of upcoming visits, follow-ups, or other required tasks Reports​: A detailed history of all forms submitted by CHWs and other staff People​: This is where profiles of districts, staff, CHWs and patients live Targets: Displays real-time visualizations of key activity and impact indicators The Menu Drawer Tap the menu icon in the upper right corner of the header to access other pages, edit personal settings, view sync status and more.Formshttps://docs.communityhealthtoolkit.org/apps/concepts/forms/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/concepts/forms/Forms are a building block of all CHT apps. They are used when creating or editing contacts, and when completing a care guide or survey within the app. Forms are also used to interpret SMS interactions with the CHT. +When a completed form is submitted, it is treated as a Report in the app. All reports can be viewed in the Reports tab by those with the proper access within the hierarchy.Care Guideshttps://docs.communityhealthtoolkit.org/apps/concepts/care-guides/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/concepts/care-guides/Care Guides Forms are used to build “Care Guides” that take health workers through care protocols and provide decision support for their interactions with patients. App designers can use the basic form building functionality in a variety of ways. +Care Guides also allow CHWs to register new families and people, assess a sick child, and enroll a new pregnancy into an antenatal care schedule. Care Guides can be located in many parts of your app, including the Tasks, People, and Reports tabs.Building Workflowshttps://docs.communityhealthtoolkit.org/apps/concepts/workflows/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/concepts/workflows/Workflows can be defined within apps built with the Core Framework to connect actions and data with people. Forms are the main building block of tasks and messaging workflows, and are useful in creating reminders for follow-up visits or referrals. +Tasks Tasks within the app can drive a workflow, ensuring that the right actions are taken for people at the right time. Tasks indicate a recommended action to the user. They indicate who the user should perform the action with, and the recommended timeframe of that action.Configurable Hierarchieshttps://docs.communityhealthtoolkit.org/apps/concepts/hierarchy/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/concepts/hierarchy/The Core Framework requires a hierarchy to organize the information in the app. It is often based on the hierarchy of a health system within a particular geographic region. +Large deployment sites often need three or more levels of place hierarchy, while some small sites need fewer than three levels. For this reason, the Core Framework’s information hierarchies are configurable to meet a users needs. +A user logging into their app will see a custom set of people, tasks, reports, and analytics based on the hierarchy level that they belong to.Usershttps://docs.communityhealthtoolkit.org/apps/concepts/users/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/concepts/users/Apps built with the Core Framework use roles and permissions to determine who has access to what data. User roles are general categories you can use to assign a collection of broad permissions to users. There are two classes of roles: online and offline. Generally speaking, CHWs are usually offline users, while managers and nurses are usually online users. SMS users do not use the app, and thus do not have a user role.Interoperabilityhttps://docs.communityhealthtoolkit.org/apps/concepts/interoperability/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/concepts/interoperability/Introduction Interoperability refers to the ability of different health information systems and applications to communicate with each other and exchange data seamlessly. With interoperability, patient information can be seen, exchanged, and used across different platforms. The information/data exchanged has to be understood across the different software for these systems to become interoperable. This is different from integration which requires custom development to connect two specific systems together. +Interoperability is the best practice for health systems because it allows information from one system to be shared with one or more other systems with no additional development.Prerequisites for App Developmenthttps://docs.communityhealthtoolkit.org/apps/concepts/prerequisites/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/concepts/prerequisites/There are no set prerequisites for users of CHT apps, yet the following are helpful for developing CHT applications. +Test Instance To build your own application using the Core Framework you will need an instance set up for testing. You can set up a local instance by following these instructions. +Build tool The build tool for applications using the Core Framework is cht-conf. To set it up, follow the installation instructions. \ No newline at end of file diff --git a/apps/concepts/interoperability/index.html b/apps/concepts/interoperability/index.html index 02b7aac81a..3e9cf0946c 100644 --- a/apps/concepts/interoperability/index.html +++ b/apps/concepts/interoperability/index.html @@ -1,9 +1,9 @@ -Interoperability | Community Health Toolkit +Interoperability | Community Health Toolkit

Interoperability

Exchanging information between the CHT Core and other health systems

Introduction

Interoperability refers to the ability of different health information systems and applications to communicate with each other and exchange data seamlessly. With interoperability, patient information can be seen, exchanged, and used across different platforms. The information/data exchanged has to be understood across the different software for these systems to become interoperable. This is different from integration which requires custom development to connect two specific systems together.

Interoperability is the best practice for health systems because it allows information from one system to be shared with one or more other systems with no additional development. Interoperability allows technical teams to scale in an efficient and repeatable manner due to the already predefined standards.

CHT Interoperability

The native CHT database structure does not map directly to a Fast Healthcare Interoperability Resources (FHIR) message format. To be compatible, we use a middleware to convert the CHT data structure into a standardized JSON format so the other systems can read it. See below the data workflow:

graph LR
+ Create project issue

Interoperability

Exchanging information between the CHT Core and other health systems

Introduction

Interoperability refers to the ability of different health information systems and applications to communicate with each other and exchange data seamlessly. With interoperability, patient information can be seen, exchanged, and used across different platforms. The information/data exchanged has to be understood across the different software for these systems to become interoperable. This is different from integration which requires custom development to connect two specific systems together.

Interoperability is the best practice for health systems because it allows information from one system to be shared with one or more other systems with no additional development. Interoperability allows technical teams to scale in an efficient and repeatable manner due to the already predefined standards.

CHT Interoperability

The native CHT database structure does not map directly to a Fast Healthcare Interoperability Resources (FHIR) message format. To be compatible, we use a middleware to convert the CHT data structure into a standardized JSON format so the other systems can read it. See below the data workflow:

graph LR
 cht[CHT]
 mediator_a([Mediator])
 mediator_b([Mediator])
@@ -313,7 +313,63 @@
 This application implements a Loss to Follow Up (LTFU) workflow system for CHIS based on the OpenHIE LTFU Guide.

Frequently Asked Questions

Is the CHT FHIR Compatible and does it have a FHIR API?

Yes. Mediators are one of the components of a CHT deployment and expose FHIR compatible APIs to the rest of the healthcare ecosystem.

Does the CHT support legacy standards?

One of the advantages of using mediators is they are highly configurable to support different FHIR Implementation Guides, different FHIR versions, and other information standards, so the CHT can work with whatever systems are in the ecosystem.

What about compatibility with future standards?

The flexibility of mediators also means the CHT is future-proof and can be configured to support future FHIR revisions or completely new standards. Because this can be configured in the mediator layer it’s likely to be supported without any Core development required.

What does the mediator do to the source data?

  1. It transforms the structure from the CHT format to the required standardized format.
  2. It can make requests for additional data. This could be querying the Client Registry for the patient’s national ID number, or other services such as the Terminology service to translate conditions, medications, procedures, and so on into the required classification system.
  3. Finally it passes the FHIR resource to the interoperability layer to be shared with other systems.

What are the FHIR Resources utilized?

  1. Patient
  2. Encounter
  3. Subscription
  4. Organization
  5. Endpoint

You can find additional information and instructions for setting up locally in the cht-interoperability repository.


CHT Applications > Features > Integrations

Exchange data with other systems

-

\ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/concepts/navigation/index.html b/apps/concepts/navigation/index.html index ebe12683fd..7b5425a838 100644 --- a/apps/concepts/navigation/index.html +++ b/apps/concepts/navigation/index.html @@ -1,9 +1,9 @@ -Navigating CHT Apps | Community Health Toolkit +Navigating CHT Apps | Community Health Toolkit

Navigating CHT Apps

Browsing your digital health apps

Summary of Page Tabs

Page tabs are the primary way to navigate apps built with the Core Framework. The number of tabs is variable depending on the user’s role and place in the hierarchy. For example, non-admin users don’t have Messages. The Reports tab is accessible to CHWs but often located inside the secondary menu drawer.





  • Messages​: A place for community-based staff to send and exchange messages
  • Tasks​: This is a list of upcoming visits, follow-ups, or other required tasks
  • Reports​: A detailed history of all forms submitted by CHWs and other staff
  • People​: This is where profiles of districts, staff, CHWs and patients live
  • Targets: Displays real-time visualizations of key activity and impact indicators

The Menu Drawer

Tap the menu icon in the upper right corner of the header to access other pages, edit personal settings, view sync status and more.

  • Admin Console: Change advanced app settings (only admin users will see this)
  • About: View your app version and other detailed database information
  • User Settings: Update basic user information like email, phone number, and password
  • Report Bug: Let us know if something isn’t working or you encounter errors
  • Log Out: Sign out of the app

Sync Status

Data synchronization is important for offline users. These users keep a copy of the data they have access to on their device. They can work from their device while disconnected from the internet (offline), by reading from and writing to their copy of the data. “Sync” (synchronization) is when data on the device is made to match the data on the server and requires an internet connection. The CHT app monitors the online status and attempts sync accordingly.

Replication Types

Synchronization consists of upward replication and downward replication.

  • Upward Replication: Uploading all new or updated data from the device to the server. It includes a retry mechanism for handling larger data batches, ensuring a robust and reliable upload process.
  • Downward Replication: Downloading new or updated data from the server to the device. Downward replication may include the download of software updates to the CHT app when available.

The CHT application manages data synchronization across two types of databases:

  • Main Application Database (medic): The main database that stores the primary data used by the application. It includes contacts, reports, messages, and other critical documents necessary for the core functionality of the application. This database is synchronized continuously to reflect changes to the application, such as new contact creations. Each user stores a subset of the main database which includes only the documents they’re allowed to view.
  • User-Metadata Database(medic-user-{username}-meta): Each user has a dedicated database that stores operational metadata, including telemetry data and error messages. Synchronization occurs at predefined intervals to ensure up-to-date monitoring and analysis.

Sync Status Notification

At the bottom of the menu is a notification which provides important information about data synchronization.

If the sync status is green and says “All reports synced,” this means you have successfully uploaded the most recent data on your device to the server. It also means that you downloaded the latest data from the server as of the time displayed. Note that there could be more recent data changes on the server, and it doesn’t guarantee you are up to date.

If the indicator is red, it means you have data changes waiting to be uploaded to the server. You should check your internet and data connection to ensure a successful sync.

Triggering a manual sync by clicking the “Sync now” button will provide feedback at every step of the process through a snackbar appearing on the bottom side of the screen. This performs upward and downward synchronization of both databases. It will also retry the sync process in case of failure..

Synchronization Triggers

  • On Login: Synchronization is automatically initiated upon successful user login if the app is connected to the internet.
  • Manual: Clicking the “Sync now” button.
  • Periodic Sync: The application performs regular checks and attempts to synchronize. The main application database syncs every 5 minutes, while the user metadata database syncs every 30 minutes..
  • On Reload: Synchronization is automatically initiated when the user reloads the application, refreshes the page, or clicks the reload button in the “Update available” modal.
  • On Connect: The app also detects when an internet connection becomes available and attempts to sync immediately.

Sync Status States

The synchronization process can be in one of the following states:

  • Unknown: The sync status is not determined yet.
  • Disabled: Sync is disabled - only applies to online-only users.
  • InProgress: Synchronization is currently ongoing.
  • Success: The last sync operation was successful.
  • Required: There is data pending synchronization.
-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/concepts/prerequisites/index.html b/apps/concepts/prerequisites/index.html index 9ba2c42cd9..d8e180d9c8 100644 --- a/apps/concepts/prerequisites/index.html +++ b/apps/concepts/prerequisites/index.html @@ -1,9 +1,9 @@ -Prerequisites for App Development | Community Health Toolkit +Prerequisites for App Development | Community Health Toolkit

Prerequisites for App Development

Tools and background skills that are helpful for developing CHT apps

There are no set prerequisites for users of CHT apps, yet the following are helpful for developing CHT applications.

Test Instance

To build your own application using the Core Framework you will need an instance set up for testing. You can set up a local instance by following these instructions.

Build tool

The build tool for applications using the Core Framework is cht-conf. To set it up, follow the installation instructions. To properly use the tool you will need your application files in set locations within a folder. Once you are set up with the basic file structure you can edit the files, and rebuild the application by compiling or converting components as needed, and uploading them to your test instance.

Background skills

To build applications with the Core Framework the following skills are helpful, ordered by importance:

XLSForms and XForms

Many workflows in your application, including completing tasks and creating contacts, will be generated using ODK XForms. Many app developers use XLSForms as an easier way to generate XForms. A strong knowledge of XLSForm standard is very useful in building your own application.

JSON

JSON (JavaScript Object Notation) is a format for storing structured text. Understanding JSON will help with minor modification of existing applications.

Javascript

Many key aspects are defined with JavaScript code and expressions. This includes managing profile pages, creating tasks and targets, and setting the condition for when to show forms. Unless you are only doing minor modification to an existing application, a good understanding of JavaScript is required.

CouchDB

A free and open source NoSQL database we use to store all our data, configuration, and even the application code. CouchDB is really good at replication which is the process of sending the data to another database, such as PouchDB in the client application, and back again. Although building your own app using the Core Framework does not require knowledge or experience with CouchDB it can be useful to be familiar with general concepts as a document store.

SQL

Although the application you build uses a NoSQL database, a parallel PostgreSQL database is available in the Core Framework to make querying data easier. Familiarity with SQL is needed to set up and query the database.


CHT Applications > + Create project issue

Prerequisites for App Development

Tools and background skills that are helpful for developing CHT apps

There are no set prerequisites for users of CHT apps, yet the following are helpful for developing CHT applications.

Test Instance

To build your own application using the Core Framework you will need an instance set up for testing. You can set up a local instance by following these instructions.

Build tool

The build tool for applications using the Core Framework is cht-conf. To set it up, follow the installation instructions. To properly use the tool you will need your application files in set locations within a folder. Once you are set up with the basic file structure you can edit the files, and rebuild the application by compiling or converting components as needed, and uploading them to your test instance.

Background skills

To build applications with the Core Framework the following skills are helpful, ordered by importance:

XLSForms and XForms

Many workflows in your application, including completing tasks and creating contacts, will be generated using ODK XForms. Many app developers use XLSForms as an easier way to generate XForms. A strong knowledge of XLSForm standard is very useful in building your own application.

JSON

JSON (JavaScript Object Notation) is a format for storing structured text. Understanding JSON will help with minor modification of existing applications.

Javascript

Many key aspects are defined with JavaScript code and expressions. This includes managing profile pages, creating tasks and targets, and setting the condition for when to show forms. Unless you are only doing minor modification to an existing application, a good understanding of JavaScript is required.

CouchDB

A free and open source NoSQL database we use to store all our data, configuration, and even the application code. CouchDB is really good at replication which is the process of sending the data to another database, such as PouchDB in the client application, and back again. Although building your own app using the Core Framework does not require knowledge or experience with CouchDB it can be useful to be familiar with general concepts as a document store.

SQL

Although the application you build uses a NoSQL database, a parallel PostgreSQL database is available in the Core Framework to make querying data easier. Familiarity with SQL is needed to set up and query the database.


CHT Applications > Tutorials > Getting started

Setting up a local environment to build and test CHT 4.x applications

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/concepts/users/index.html b/apps/concepts/users/index.html index 8536c9985e..c1c10387d7 100644 --- a/apps/concepts/users/index.html +++ b/apps/concepts/users/index.html @@ -1,9 +1,9 @@ -Users | Community Health Toolkit +Users | Community Health Toolkit

Users

Defining the user roles and their permissions

Apps built with the Core Framework use roles and permissions to determine who has access to what data. User roles are general categories you can use to assign a collection of broad permissions to users. There are two classes of roles: online and offline. Generally speaking, CHWs are usually offline users, while managers and nurses are usually online users. SMS users do not use the app, and thus do not have a user role.

Roles

Differing levels of access and permissions are assigned to each persona. A user role is created to provide them with access to the information they need. Offline and online access, storage limitations, and data privacy are taken into account.

PersonaHierarchyDevicePermissions
Program OfficerLogs in as AdminComputerAdmin users, usually Program Officers, are online-only admin users not associated to a particular level. They have access to all people, places, and records in the app, but since they are online-only users, they cannot view any tasks or targets.
CHW SupervisorsLogs in at Health Facility levelTabletUser at this level have online and offline access to view CHWs, fill out reports about them, and view tasks and targets related to them. Due to storage limitations, they aren’t able to view households or submit reports and review tasks and targets about them.
CHWsLogs in at CHW Area levelPhoneUsers at this level have online and offline access to view households and family members, submit reports about them, and view tasks and targets about them.
Family membersRegistered at Household level, does not log inMessagingFamily members might include fathers, mothers, children, and other adults. The program model determines which family members should be registered in the app. However, they are not users of the app, and do not log in themselves.

See Also: Defining User Roles

Online Users

Online roles are for users who need access to a lot of data and need to maintain the system or update system settings. An internet connection is required.

Offline Users

Offline roles are for users who need to be able to access data on-the-go in the field and don’t have a reliable internet connection. All the data they have access to will be synced to their device. System administrators cannot be offline users as they won’t have access to the app management tools offline.

Permissions

User Permissions are settings that can be individually toggled on or off to allow users with a particular Role to do a certain action or see a certain thing. CHT app developers and administrators can add as many User Roles as needed to grant permissions to different groups of users.

Viewing permissions determine which page tabs a user sees in the app and which types of data they do and don’t have access to. User action permissions include who can create (e.g., create new users), who can delete (e.g., delete reports), who can edit (e.g., edit profiles), and who can export (e.g., export server logs).

See Also: Defining User Permissions


Design System > + Create project issue

Users

Defining the user roles and their permissions

Apps built with the Core Framework use roles and permissions to determine who has access to what data. User roles are general categories you can use to assign a collection of broad permissions to users. There are two classes of roles: online and offline. Generally speaking, CHWs are usually offline users, while managers and nurses are usually online users. SMS users do not use the app, and thus do not have a user role.

Roles

Differing levels of access and permissions are assigned to each persona. A user role is created to provide them with access to the information they need. Offline and online access, storage limitations, and data privacy are taken into account.

PersonaHierarchyDevicePermissions
Program OfficerLogs in as AdminComputerAdmin users, usually Program Officers, are online-only admin users not associated to a particular level. They have access to all people, places, and records in the app, but since they are online-only users, they cannot view any tasks or targets.
CHW SupervisorsLogs in at Health Facility levelTabletUser at this level have online and offline access to view CHWs, fill out reports about them, and view tasks and targets related to them. Due to storage limitations, they aren’t able to view households or submit reports and review tasks and targets about them.
CHWsLogs in at CHW Area levelPhoneUsers at this level have online and offline access to view households and family members, submit reports about them, and view tasks and targets about them.
Family membersRegistered at Household level, does not log inMessagingFamily members might include fathers, mothers, children, and other adults. The program model determines which family members should be registered in the app. However, they are not users of the app, and do not log in themselves.

See Also: Defining User Roles

Online Users

Online roles are for users who need access to a lot of data and need to maintain the system or update system settings. An internet connection is required.

Offline Users

Offline roles are for users who need to be able to access data on-the-go in the field and don’t have a reliable internet connection. All the data they have access to will be synced to their device. System administrators cannot be offline users as they won’t have access to the app management tools offline.

Permissions

User Permissions are settings that can be individually toggled on or off to allow users with a particular Role to do a certain action or see a certain thing. CHT app developers and administrators can add as many User Roles as needed to grant permissions to different groups of users.

Viewing permissions determine which page tabs a user sees in the app and which types of data they do and don’t have access to. User action permissions include who can create (e.g., create new users), who can delete (e.g., delete reports), who can edit (e.g., edit profiles), and who can export (e.g., export server logs).

See Also: Defining User Permissions


Design System > Personas

The “typical” users of CHT Apps across diverse contexts

CHT Applications > Tutorials > Contacts + Users 1

Creating and editing contacts and users in the CHT UI

CHT Applications > @@ -309,7 +309,8 @@ Quick Guides > Data > Bulk Load Users

How to create users in bulk

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/concepts/workflows/index.html b/apps/concepts/workflows/index.html index df30a3dbd6..9901356084 100644 --- a/apps/concepts/workflows/index.html +++ b/apps/concepts/workflows/index.html @@ -1,9 +1,9 @@ -Building Workflows | Community Health Toolkit +Building Workflows | Community Health Toolkit

Building Workflows

Building connections between people, actions, and data systems

Workflows can be defined within apps built with the Core Framework to connect actions and data with people. Forms are the main building block of tasks and messaging workflows, and are useful in creating reminders for follow-up visits or referrals.

Tasks

Tasks within the app can drive a workflow, ensuring that the right actions are taken for people at the right time. Tasks indicate a recommended action to the user. They indicate who the user should perform the action with, and the recommended timeframe of that action. When the user taps the task, they are directed to a form where the details of the action are captured.

Tasks can be triggered by a set of conditions, such as contact details or submitted reports. Tasks are accessible in the Tasks tab and the profile in the Contact tab, and initiate a follow up action to complete a form. More information on building app workflows is available in the Tasks section.

Data submitted in one form can generate several tasks at once, for example, multiple ANC visits following one pregnancy registration. Some workflows involve a series of sequential forms and tasks, such as a child health assessment form, a follow up task scheduled 48 hours later, a referral form (only if the child’s condition hasn’t improved), and then a referral follow up task. Tasks are accessible on the Tasks tab, as well as the Tasks section of profiles.

See Also: Defining Tasks

SMS Messaging

Workflows can include notifications and interactions with CHWs, nurses, supervisors, and patients via SMS. A report can trigger SMS messages to be sent immediately or upon a set schedule. Responses via SMS or the app can update the workflows.

See Also: Defining SMS Workflows

Interoperability

Workflows can incorporate other digital tools, such as a facility-based electronic medical record system for referral workflows. New contacts or reports can trigger an interoperabilty workflow using the outbound push feature. Data can be received as reports using the CHT API

See Also: Outbound Push

-

Last modified 10.12.2020: Removed unrelated content (ed0dffee)
\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/examples/anc/index.html b/apps/examples/anc/index.html index c008103223..d6a9ea4ddc 100644 --- a/apps/examples/anc/index.html +++ b/apps/examples/anc/index.html @@ -1,9 +1,9 @@ -Maternal and Newborn Health Reference App | Community Health Toolkit +Maternal and Newborn Health Reference App | Community Health Toolkit

Maternal and Newborn Health Reference App

Reference application for maternal and newborn care for CHW’s using a mobile app

This “reference application” for maternal and newborn health provides a template for structuring and organizing your Community Health Toolkit digital health app, its configuration, and test code. It can be used as is, or serve as a great way to learn about the CHT’s foundation for forms, data fields, and analytics that can be easily customized to fit your context.

Problem Being Addressed

Access to quality maternal and newborn care is the cornerstone of many community health programs. For many women living in communities at the last mile, pregnancy can be a vulnerable time. There is a need for community health programs to support early pregnancy registration, consistent antenatal care (ANC) visits, and in-facility deliveries. In addition, the short time window following delivery for postnatal care (PNC) is a critical time for catching life-threatening danger signs for the new mother and baby.

Solution Overview

The maternal and newborn health workflow ensures that women receive the care that they need during their pregnancy with the support of CHWs. Early pregnancy registration, timely antenatal care visits, and improved care coordination between CHWs and clinics increase the likelihood that women will deliver in a facility with the support of skilled birth attendants. This ultimately will help save the lives of mothers and babies, as well as strengthen the maternal and newborn health services of the health system. Community Health Toolkit applications with maternal and newborn workflows are used by health workers and clinical staff to:

  • Register pregnancies
  • Provide a schedule for on-time ANC visits
  • Offer education for the mother at each stage of pregnancy
  • Screen for and report danger signs in the pregnant woman and newborn
  • Refer and encourage pregnant women to deliver at a facility
  • Ensure on-time PNC visits for mother and newborn

Forms Hierarchy

Once a hierarchy of people and places is established, forms are added at different levels. This diagram indicates the forms that can be filled about a person in the app (in this case, family members at the household level), as well as the person/user who will access these forms and make the reports (CHWs at the CHW Area level). Some forms are accessible as actions from the family member’s profile as actions, others from the CHW’s task list as tasks, and some as either.

See Also: Controlling form properties

Workflows

Both maternal and newborn care workflows are defined to connect form actions and data with people. Detailed documentation for these forms and task schedules are linked from the workflow diagrams below. Accompanying this documentation are tips and insights into the design decisions made along the way, and suggestions for how and where to customize the forms.

Pregnancy Workflow

Condition

Condition

Task

Task

Resolution

Resolution

Condition

Upon discovering a pregnancy, a CHW submits a Pregnancy Form confirming a new pregnancy with the estimated gestational age.

Task

At the 8 ANC touchpoints defined by the WHO, the CHW receives a Pregnancy Home Visit Task to let her know that it’s time to check in on the pregnant woman.

Resolution

CHW submits Pregnancy Home Visit Form, demonstrating that she provided ANC counseling, gathered information from prior facility visits, and screened for danger signs.

Condition

A Pregnancy Form is submitted with the gestational age unknown.

Task

A Pregnancy Visit Task appears every 2 weeks for 42 weeks.

Resolution

CHW submits Pregnancy Home Visit Form. If the gestational age is entered, the workflow will change to Pregnancy Home Visit Tasks.

Condition

A CHW submits a Pregnancy Home Visit Form that includes an upcoming facility visit date.

Task

A Health Facility ANC Reminder Task 1 week ahead of the facility visit to remind the woman to attend.

Resolution

CHW submits Health Facility ANC Reminder Form, confirming that she called or visited the woman to remind her of her upcoming facility visit.

Condition

If the CHW notices danger signs at any time, then she submits a Danger Sign Form and immediately refers the patient to the facility.

Task

A Danger Sign Follow-Up Task will appear immediately and is due 3 days later. Tasks persists for 7 days after due date.

Resolution

CHW submits a Danger Sign Follow-Up Form, verifying that she called or visited the woman to confirm that she attended the facility. If this is not received, another Danger Sign Follow-Up Task is triggered.

Delivery Workflow

Condition

Condition

Task

Task

Resolution

Resolution

Condition

A currently registered pregnant person has reached a gestational age of 42 weeks and has not had a miscarriage or a delivery reported.

Task

A Delivery Task requesting that the CHW check in on the woman to see whether she has delivered.

Resolution

CHW submits a Delivery Form, confirming the pregnancy outcomes. Profiles are created for each baby that is alive. This “ends” the pregnancy workflow.

Condition

A CHW submits a Delivery Form that contains a danger sign for mom or baby, or reports that either mom or baby are “alive and unwell.”

Task

A Danger Sign Follow-Up Task appears immediately and is due 3 days later. Persists for 7 days after due date.

Resolution

The CHW calls or visits woman to confirm that she attended and submits a Danger Sign Follow-Up Form. If this is not received, another Danger Sign Follow-Up Task is triggered.

Condition

If the CHW notices danger signs at any time, then she submits a Danger Sign Form and immediately refers the patient to the facility.

Task

A Danger Sign Follow-Up Task will appear immediately and is due 3 days later. Tasks persists for 7 days after due date.

Resolution

CHW submits Danger Sign Follow-Up Form, verifying that she called or visited the woman to confirm that she attended the facility. If this is not received, another Danger Sign Follow-Up Task is triggered.

Additional Resources to Get Started

Here are a few additional resources to help get you started with the maternal and newborn health reference application.


CHT Applications > + Create project issue

Maternal and Newborn Health Reference App

Reference application for maternal and newborn care for CHW’s using a mobile app

This “reference application” for maternal and newborn health provides a template for structuring and organizing your Community Health Toolkit digital health app, its configuration, and test code. It can be used as is, or serve as a great way to learn about the CHT’s foundation for forms, data fields, and analytics that can be easily customized to fit your context.

Problem Being Addressed

Access to quality maternal and newborn care is the cornerstone of many community health programs. For many women living in communities at the last mile, pregnancy can be a vulnerable time. There is a need for community health programs to support early pregnancy registration, consistent antenatal care (ANC) visits, and in-facility deliveries. In addition, the short time window following delivery for postnatal care (PNC) is a critical time for catching life-threatening danger signs for the new mother and baby.

Solution Overview

The maternal and newborn health workflow ensures that women receive the care that they need during their pregnancy with the support of CHWs. Early pregnancy registration, timely antenatal care visits, and improved care coordination between CHWs and clinics increase the likelihood that women will deliver in a facility with the support of skilled birth attendants. This ultimately will help save the lives of mothers and babies, as well as strengthen the maternal and newborn health services of the health system. Community Health Toolkit applications with maternal and newborn workflows are used by health workers and clinical staff to:

  • Register pregnancies
  • Provide a schedule for on-time ANC visits
  • Offer education for the mother at each stage of pregnancy
  • Screen for and report danger signs in the pregnant woman and newborn
  • Refer and encourage pregnant women to deliver at a facility
  • Ensure on-time PNC visits for mother and newborn

Forms Hierarchy

Once a hierarchy of people and places is established, forms are added at different levels. This diagram indicates the forms that can be filled about a person in the app (in this case, family members at the household level), as well as the person/user who will access these forms and make the reports (CHWs at the CHW Area level). Some forms are accessible as actions from the family member’s profile as actions, others from the CHW’s task list as tasks, and some as either.

See Also: Controlling form properties

Workflows

Both maternal and newborn care workflows are defined to connect form actions and data with people. Detailed documentation for these forms and task schedules are linked from the workflow diagrams below. Accompanying this documentation are tips and insights into the design decisions made along the way, and suggestions for how and where to customize the forms.

Pregnancy Workflow

Condition

Condition

Task

Task

Resolution

Resolution

Condition

Upon discovering a pregnancy, a CHW submits a Pregnancy Form confirming a new pregnancy with the estimated gestational age.

Task

At the 8 ANC touchpoints defined by the WHO, the CHW receives a Pregnancy Home Visit Task to let her know that it’s time to check in on the pregnant woman.

Resolution

CHW submits Pregnancy Home Visit Form, demonstrating that she provided ANC counseling, gathered information from prior facility visits, and screened for danger signs.

Condition

A Pregnancy Form is submitted with the gestational age unknown.

Task

A Pregnancy Visit Task appears every 2 weeks for 42 weeks.

Resolution

CHW submits Pregnancy Home Visit Form. If the gestational age is entered, the workflow will change to Pregnancy Home Visit Tasks.

Condition

A CHW submits a Pregnancy Home Visit Form that includes an upcoming facility visit date.

Task

A Health Facility ANC Reminder Task 1 week ahead of the facility visit to remind the woman to attend.

Resolution

CHW submits Health Facility ANC Reminder Form, confirming that she called or visited the woman to remind her of her upcoming facility visit.

Condition

If the CHW notices danger signs at any time, then she submits a Danger Sign Form and immediately refers the patient to the facility.

Task

A Danger Sign Follow-Up Task will appear immediately and is due 3 days later. Tasks persists for 7 days after due date.

Resolution

CHW submits a Danger Sign Follow-Up Form, verifying that she called or visited the woman to confirm that she attended the facility. If this is not received, another Danger Sign Follow-Up Task is triggered.

Delivery Workflow

Condition

Condition

Task

Task

Resolution

Resolution

Condition

A currently registered pregnant person has reached a gestational age of 42 weeks and has not had a miscarriage or a delivery reported.

Task

A Delivery Task requesting that the CHW check in on the woman to see whether she has delivered.

Resolution

CHW submits a Delivery Form, confirming the pregnancy outcomes. Profiles are created for each baby that is alive. This “ends” the pregnancy workflow.

Condition

A CHW submits a Delivery Form that contains a danger sign for mom or baby, or reports that either mom or baby are “alive and unwell.”

Task

A Danger Sign Follow-Up Task appears immediately and is due 3 days later. Persists for 7 days after due date.

Resolution

The CHW calls or visits woman to confirm that she attended and submits a Danger Sign Follow-Up Form. If this is not received, another Danger Sign Follow-Up Task is triggered.

Condition

If the CHW notices danger signs at any time, then she submits a Danger Sign Form and immediately refers the patient to the facility.

Task

A Danger Sign Follow-Up Task will appear immediately and is due 3 days later. Tasks persists for 7 days after due date.

Resolution

CHW submits Danger Sign Follow-Up Form, verifying that she called or visited the woman to confirm that she attended the facility. If this is not received, another Danger Sign Follow-Up Task is triggered.

Additional Resources to Get Started

Here are a few additional resources to help get you started with the maternal and newborn health reference application.


CHT Applications > Concepts

Basic concepts that will help you understand how CHT applications are built

CHT Applications > Reference > forms/ > app

App Forms: Used to complete reports, tasks, and actions in the app

CHT Applications > Reference > tasks.js

Tasks: Definition of tasks shown to app users

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/examples/contact-tracing/index.html b/apps/examples/contact-tracing/index.html index 27a421b512..181f1c24f3 100644 --- a/apps/examples/contact-tracing/index.html +++ b/apps/examples/contact-tracing/index.html @@ -1,9 +1,9 @@ -Contact Tracing | Community Health Toolkit +Contact Tracing | Community Health Toolkit

Contact Tracing

A community surveillance tool to help control infectious disease outbreaks and mitigate secondary disease transmission

The CHT’s Contact Tracing functionality enables effective disease surveillance within communities to help control infectious disease outbreaks. It is a community public health tool that is designed to:

  • Centrally register patient cases and track contacts to prevent secondary spread of diseases in communities
  • Create a coordinated approach to contact tracing within existing health systems
  • Communicate the importance of self-isolation and symptom screening to exposed individuals and their families

Problem Being Addressed

An essential part of containing disease outbreaks, such as COVID-19, requires public health organizations to rapidly notify people who have come into contact with confirmed or suspected patient cases. In many health systems, existing health management information systems are ill equipped to handle the necessary rapid tracking, triage, and care referrals for large numbers of patient cases and contacts to prevent secondary spread of viruses.

Solution Overview

Contact Tracing with the CHT offers a rapidly scalable surveillance strategy by registering patient cases and their contacts within communities in a responsive and empathetic way. Through proactive tracing of contacts, active symptom screening, and referrals to care or self-isolation, the app can be deployed to assist in:

  • Disrupting secondary transmission via coordinated registration of patient cases
  • Enrolling all contacts and households in monitoring during isolation periods
  • Referring symptomatic cases for skilled care following triage
  • Engaging in community education and follow-up during isolation

Users and Hierarchy Example

UserLocationDevicesRole
Data EntryFacility levelDesktop, laptop or tabletResponsible for ensuring all contacts are traced. Create contact list based on confirmed cases. Assign contacts to Tracers for follow-up. Notified of unsuccessful traces and symptomatic cases.
TracerCommunity levelSmartphoneResponsible for tracing and screening the contacts of the confirmed case upon notification, verifying symptomatic contacts on self-monitoring and escalating them for further investigation.
COVID-19 PatientCommunity levelIdentified and contacted by staff to ensure all contacts are captured for follow-up.
ContactsCommunity levelFeature phoneReceive notification alert upon registration of Tracer follow-up. Submit self-monitoring check during self-quarantine via SMS. Referred to care if symptomatic.

Workflow Example

This demo illustrates how a CHT workflow for tracing of contacts of suspected or confirmed patient cases can be deployed to support a COVID-19 health program response. It works on both SMS and the CHT app. It can be combined with other care workflows and configured to suit specific health system needs, particularly in terms of tracer roles.


More background information can be found in this summary deck

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/examples/covid-education/index.html b/apps/examples/covid-education/index.html index 5c2396d866..6dd589db6a 100644 --- a/apps/examples/covid-education/index.html +++ b/apps/examples/covid-education/index.html @@ -1,9 +1,9 @@ -COVID-19 Education and Training for CHWs | Community Health Toolkit +COVID-19 Education and Training for CHWs | Community Health Toolkit

COVID-19 Education and Training for CHWs

Education and training for Community Health Workers to address COVID-19

The COVID-19 pandemic has created unique challenges to providing in-person Community Health Worker training. To support CHWs, three learning modules were created to rapidly and remotely train them on COVID-19 safety protocols and patient care. Through CHT app and SMS deployments, Supervisors can now train CHWs on care workflows without being physically present. This example covers the following learning modules:

  • Health safety protocols for preventing the spread of COVID-19
  • Preventing the dissemination of misinformation about COVID-19
  • Recognizing COVID-19 and caring for patients with suspected infection

Problem Being Addressed

It is important for Community Health Workers to stay safe and serve their communities by understanding effective health protocols for preventing the spread of COVID-19. Training would traditionally be done in person, however, due to public health concerns, travel and gatherings of people are limited. Health systems have been forced to quickly adapt to deliver training content in a safely distanced, timely, and effective manner.

Solution Overview

The CHT provides a flexible way to deliver timely COVID-19 remote training and education to CHWs. In collaboration with Stanford’s Digital Medic Team, and vetted by a team of health experts at Stanford, three remote learning modules were designed. CHWs can progress through training at their own pace, and receive tasks in their CHT app to encourage them to complete the modules. The training modules:

  • Build on verifiable health safety guidance from the CDC and WHO
  • Can be deployed using the CHT app or SMS workflows
  • Capture assessment and understanding of CHW knowledge through quizzes, which can be tracked by Supervisors

Users and Hierarchy Example

UserLocationDevicesRole
National Officials and County TeamsCentral and district officesDesktop, laptop and smartphonesMonitor central analytics regarding onboarding and training completion. Revisit content where needed.
Sub-County TeamsFacilities or local officesTablet or smartphoneReview CHW progress on aggregate. Support CHW supervisors and monitor location-specific analytics.
CHW SupervisorsCommunity level, based at facilitiesSmartphones and personal phonesVerify CHW onboarding and training completion. Follow-up with those who have not completed training. Complete their own onboarding and training tasks.
CHWs and VolunteersCommunity levelFeature phones and some smartphonesComplete onboarding and training via SMS or App. Provide feedback and ask for support where needed.

Workflow Example

This demo illustrates how a CHT workflow can be easily adapted to integrate a sequence of learning modules through the usage of tasks. These modules are designed to be delivered sequentially to promote engagement and absorption of content. Learning can be tracked across CHWs to inform opportunities to provide additional support. The sequence can also be modified to interact with the CHW through SMS, using question-and-answer text-based workflows.


More background on the SMS workflow content and key considerations for adapting it can be found in this summary document.


CHT Applications > + Create project issue

COVID-19 Education and Training for CHWs

Education and training for Community Health Workers to address COVID-19

The COVID-19 pandemic has created unique challenges to providing in-person Community Health Worker training. To support CHWs, three learning modules were created to rapidly and remotely train them on COVID-19 safety protocols and patient care. Through CHT app and SMS deployments, Supervisors can now train CHWs on care workflows without being physically present. This example covers the following learning modules:

  • Health safety protocols for preventing the spread of COVID-19
  • Preventing the dissemination of misinformation about COVID-19
  • Recognizing COVID-19 and caring for patients with suspected infection

Problem Being Addressed

It is important for Community Health Workers to stay safe and serve their communities by understanding effective health protocols for preventing the spread of COVID-19. Training would traditionally be done in person, however, due to public health concerns, travel and gatherings of people are limited. Health systems have been forced to quickly adapt to deliver training content in a safely distanced, timely, and effective manner.

Solution Overview

The CHT provides a flexible way to deliver timely COVID-19 remote training and education to CHWs. In collaboration with Stanford’s Digital Medic Team, and vetted by a team of health experts at Stanford, three remote learning modules were designed. CHWs can progress through training at their own pace, and receive tasks in their CHT app to encourage them to complete the modules. The training modules:

  • Build on verifiable health safety guidance from the CDC and WHO
  • Can be deployed using the CHT app or SMS workflows
  • Capture assessment and understanding of CHW knowledge through quizzes, which can be tracked by Supervisors

Users and Hierarchy Example

UserLocationDevicesRole
National Officials and County TeamsCentral and district officesDesktop, laptop and smartphonesMonitor central analytics regarding onboarding and training completion. Revisit content where needed.
Sub-County TeamsFacilities or local officesTablet or smartphoneReview CHW progress on aggregate. Support CHW supervisors and monitor location-specific analytics.
CHW SupervisorsCommunity level, based at facilitiesSmartphones and personal phonesVerify CHW onboarding and training completion. Follow-up with those who have not completed training. Complete their own onboarding and training tasks.
CHWs and VolunteersCommunity levelFeature phones and some smartphonesComplete onboarding and training via SMS or App. Provide feedback and ask for support where needed.

Workflow Example

This demo illustrates how a CHT workflow can be easily adapted to integrate a sequence of learning modules through the usage of tasks. These modules are designed to be delivered sequentially to promote engagement and absorption of content. Learning can be tracked across CHWs to inform opportunities to provide additional support. The sequence can also be modified to interact with the CHW through SMS, using question-and-answer text-based workflows.


More background on the SMS workflow content and key considerations for adapting it can be found in this summary document.

-

Last modified 11.01.2021: Update covid-education.md (3771e339)
\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/examples/covid-rdt-reference-app/index.html b/apps/examples/covid-rdt-reference-app/index.html index 4e1e0e118b..cb176116e5 100644 --- a/apps/examples/covid-rdt-reference-app/index.html +++ b/apps/examples/covid-rdt-reference-app/index.html @@ -1,9 +1,9 @@ -COVID-19 Testing with Rapid Diagnostic Tests | Community Health Toolkit +COVID-19 Testing with Rapid Diagnostic Tests | Community Health Toolkit

COVID-19 Testing with Rapid Diagnostic Tests

CHT example application that uses a third party app to capture the result of a Rapid Diagnostic Test.

Medic has worked with FIND to build a CHT reference application for COVID-19 point-of-care testing with Rapid Diagnostic Tests (RDT). Using the reference app as an example, CHT app developers can easily include the provisioning and capture of RDT in workflows. These workflows can include third-party applications, like Dimagi’s RD-Toolkit, that guide health workers through the use of the RDT.

You can find the code for this application in the CHT Core repository on GitHub.

Problem Being Addressed

The original call for proposals best describes why Medic created this app:

FIND is working towards supporting countries in implementing an effective test-trace-isolate response using digital tools. To this end, FIND is looking to partner with leading digital solution providers to accelerate the development and deployment of a set of minimum functionalities for the collection of COVID RDT data and supporting incorporation of these functionalities into existing digital tools for use in low- and middle-income country settings.

Solution Overview

This reference app provides a base layer of functionality that you can easily customize to meet the needs of your health program. Specifically, this application has examples of:

  • Provisioning COVID-19 RDT tests
  • Capturing COVID-19 RDT results
  • Storing all RDT data, including pictures, in the CHT
  • Best practices for RDT workflows in the CHT

Your instance of CHT needs to be on v3.13+ which has features developed to support this reference application, including:

  • Display Base64 Image - show ASCII encoded images inline
  • Android App Launcher - integrate with third party apps like the RD-Toolkit
  • Parse Timestamp to Date - convert epoch time stamps (1628945040308) to your desired format (Sun Mar 19 13:25:08).

For more information on these features, see the “Related Content”.

Additional requirements for this application beyond CHT 3.13, include CHT Android 0.10.0 or later and Dimagi’s RD-Toolkit 0.9.8 or later.

While this application calls the RD-Toolkit, the integration features in the CHT Core and CHT Android are generic. This means you could use a different RDT Android application if you prefer. Beyond the scope of RDTs, you could use this integration feature to launch any other Android app to perform an action and save the result in the CHT. To read more about this feature, see the Android App Launcher section in the Forms reference documentation.

Workflow

There are three main components to this application:

  • The CHT forms to provision and capture RDTs (Green)
  • Using a third-party app to use an RDT (Yellow)
  • Tasks to remind CHWs to complete an RDT that has been started (Blue)

Training Materials

Medic is providing images and videos for use in training CHWs on how to use the CHT with the RD-Toolkit. These could be combined with existing in app training if needed. As well, the RD-Toolkit has in app instructions for how to use specific RDTs.

Images

27 high resolutions images taken from a demonstration CHW device are available for download. These cover the entire usage of the reference app.

Videos

Provided here are two videos of the Provision and Capture forms in the COVID-19 application.

Provision

This first video shows the left side of the workflow above to provision an RDT for a patient. The CHW is shown finding Jessica Whitehouse’s contact and choosing a new action of “RDT Covid-19 - Provision”. The CHW then does pre-test set up, checking Jessica’s symptoms and location and confirming the test method and lot information. You can see the CHW launching the RD-Toolkit, reading the instructions and then, starting the session and timer. With the RD-Toolkit sending back all the information to the CHT, a task “Capture Covid-19 RDT” can be seen in the CHT for the CHW to follow up on when the RD-Timer has completed. The last part of the video shows the completed provision report in the CHT:

Capture

This second video shows the right side of the workflow above to capture RDT results for a patient. The CHW is shown viewing the “Capture Covid-19 RDT” task after the 15 minute timer from the RD-Toolkit app has completed. After selecting the task, the CHW is brought to the CHT form which has the session information for the RD-Toolkit already loaded. The test results are recorded and then returned to the CHT from the RD-Toolkit. The last part of the video shows the completed capture report in the CHT, including the result and image of the RDT:

Reports and Dashboards

Like all applications written for the CHT, there are built-in mechanisms to retrieve raw and aggregate data to generate reports and dashboards. Here are some ways that the data can be accessed:

  • In app targets: Gives the CHW or their supervisor an aggregate view of any of the form fields. Since targets rely on the data on the device, if targets include data from other users then permissions must be set on the relevant forms so that the data can be replicated and synchronized accordingly.
  • API Calls: Given that all form submissions are captured in JSON and that we know the data model well, you can easily do API calls to a CHT server instance and use some custom code (node, python etc) to gather and show stats on a daily basis. You can export to either JSON or CSV. See API docs for reports as well as monitoring metadata.
  • PostgreSQL queries: CHT ships with a utility to export all the data that the API has to a relational database, Postgres. You have all the raw data the API has, but can now use the power of joins and groupings to come up with totally customizable stats by day, week, month etc. Data can be synched daily or hourly from the CHT.
  • Dashboards: Medic has used both Klipfolio and, more recently, Superset to create more complex yet still user-friendly dashboards. This is particular useful for those who need to view the data but wouldn’t otherwise be logging in to CHT apps. These dashboards generally access the relational data in the Postgres database as the back end.
  • Third-Party Applications: Connecting to third-party applications can be done using built-in integrations, or building a workflow with custom integrations.

Sample API call

Start with finding out the names of the forms you can get reports from. If you had deployed this application without any customizations, you would have these forms available as seen by calling the forms API:

curl "http://LOGIN:PASSWORD@HOSTNAME/api/v1/forms" | jq
+ Create project issue

COVID-19 Testing with Rapid Diagnostic Tests

CHT example application that uses a third party app to capture the result of a Rapid Diagnostic Test.

Medic has worked with FIND to build a CHT reference application for COVID-19 point-of-care testing with Rapid Diagnostic Tests (RDT). Using the reference app as an example, CHT app developers can easily include the provisioning and capture of RDT in workflows. These workflows can include third-party applications, like Dimagi’s RD-Toolkit, that guide health workers through the use of the RDT.

You can find the code for this application in the CHT Core repository on GitHub.

Problem Being Addressed

The original call for proposals best describes why Medic created this app:

FIND is working towards supporting countries in implementing an effective test-trace-isolate response using digital tools. To this end, FIND is looking to partner with leading digital solution providers to accelerate the development and deployment of a set of minimum functionalities for the collection of COVID RDT data and supporting incorporation of these functionalities into existing digital tools for use in low- and middle-income country settings.

Solution Overview

This reference app provides a base layer of functionality that you can easily customize to meet the needs of your health program. Specifically, this application has examples of:

  • Provisioning COVID-19 RDT tests
  • Capturing COVID-19 RDT results
  • Storing all RDT data, including pictures, in the CHT
  • Best practices for RDT workflows in the CHT

Your instance of CHT needs to be on v3.13+ which has features developed to support this reference application, including:

  • Display Base64 Image - show ASCII encoded images inline
  • Android App Launcher - integrate with third party apps like the RD-Toolkit
  • Parse Timestamp to Date - convert epoch time stamps (1628945040308) to your desired format (Sun Mar 19 13:25:08).

For more information on these features, see the “Related Content”.

Additional requirements for this application beyond CHT 3.13, include CHT Android 0.10.0 or later and Dimagi’s RD-Toolkit 0.9.8 or later.

While this application calls the RD-Toolkit, the integration features in the CHT Core and CHT Android are generic. This means you could use a different RDT Android application if you prefer. Beyond the scope of RDTs, you could use this integration feature to launch any other Android app to perform an action and save the result in the CHT. To read more about this feature, see the Android App Launcher section in the Forms reference documentation.

Workflow

There are three main components to this application:

  • The CHT forms to provision and capture RDTs (Green)
  • Using a third-party app to use an RDT (Yellow)
  • Tasks to remind CHWs to complete an RDT that has been started (Blue)

Training Materials

Medic is providing images and videos for use in training CHWs on how to use the CHT with the RD-Toolkit. These could be combined with existing in app training if needed. As well, the RD-Toolkit has in app instructions for how to use specific RDTs.

Images

27 high resolutions images taken from a demonstration CHW device are available for download. These cover the entire usage of the reference app.

Videos

Provided here are two videos of the Provision and Capture forms in the COVID-19 application.

Provision

This first video shows the left side of the workflow above to provision an RDT for a patient. The CHW is shown finding Jessica Whitehouse’s contact and choosing a new action of “RDT Covid-19 - Provision”. The CHW then does pre-test set up, checking Jessica’s symptoms and location and confirming the test method and lot information. You can see the CHW launching the RD-Toolkit, reading the instructions and then, starting the session and timer. With the RD-Toolkit sending back all the information to the CHT, a task “Capture Covid-19 RDT” can be seen in the CHT for the CHW to follow up on when the RD-Timer has completed. The last part of the video shows the completed provision report in the CHT:

Capture

This second video shows the right side of the workflow above to capture RDT results for a patient. The CHW is shown viewing the “Capture Covid-19 RDT” task after the 15 minute timer from the RD-Toolkit app has completed. After selecting the task, the CHW is brought to the CHT form which has the session information for the RD-Toolkit already loaded. The test results are recorded and then returned to the CHT from the RD-Toolkit. The last part of the video shows the completed capture report in the CHT, including the result and image of the RDT:

Reports and Dashboards

Like all applications written for the CHT, there are built-in mechanisms to retrieve raw and aggregate data to generate reports and dashboards. Here are some ways that the data can be accessed:

  • In app targets: Gives the CHW or their supervisor an aggregate view of any of the form fields. Since targets rely on the data on the device, if targets include data from other users then permissions must be set on the relevant forms so that the data can be replicated and synchronized accordingly.
  • API Calls: Given that all form submissions are captured in JSON and that we know the data model well, you can easily do API calls to a CHT server instance and use some custom code (node, python etc) to gather and show stats on a daily basis. You can export to either JSON or CSV. See API docs for reports as well as monitoring metadata.
  • PostgreSQL queries: CHT ships with a utility to export all the data that the API has to a relational database, Postgres. You have all the raw data the API has, but can now use the power of joins and groupings to come up with totally customizable stats by day, week, month etc. Data can be synched daily or hourly from the CHT.
  • Dashboards: Medic has used both Klipfolio and, more recently, Superset to create more complex yet still user-friendly dashboards. This is particular useful for those who need to view the data but wouldn’t otherwise be logging in to CHT apps. These dashboards generally access the relational data in the Postgres database as the back end.
  • Third-Party Applications: Connecting to third-party applications can be done using built-in integrations, or building a workflow with custom integrations.

Sample API call

Start with finding out the names of the forms you can get reports from. If you had deployed this application without any customizations, you would have these forms available as seen by calling the forms API:

curl "http://LOGIN:PASSWORD@HOSTNAME/api/v1/forms" | jq
 

The resulting JSON is formatted by jq for you to get an easy to read list.

[
   "contact:clinic:create.xml",
   "contact:clinic:edit.xml",
@@ -683,7 +683,8 @@
 forms/ >
 app
 : Parse Timestamp to Date

App Forms: Used to complete reports, tasks, and actions in the app

-

Last modified 27.09.2021: fix video URLs per #554 (14cd8616)
\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/examples/direct-to-client/index.html b/apps/examples/direct-to-client/index.html index 09ae661d72..780b19b776 100644 --- a/apps/examples/direct-to-client/index.html +++ b/apps/examples/direct-to-client/index.html @@ -1,9 +1,9 @@ -Direct-to-client, two-way texting workflows on CHT | Community Health Toolkit +Direct-to-client, two-way texting workflows on CHT | Community Health Toolkit

Direct-to-client, two-way texting workflows on CHT

Reference for Direct-to-client, two-way texting workflows with CHT and RapidPro

This documentation provides a guide for designing and deploying direct-to-client (D2C), two-way texting (2wT) workflows to support client follow-up care using the community health toolkit (CHT). 2wT is a mobile text messaging system built on open-source, CHT tools to engage clients in their health care; to facilitate prompt client - healthcare provider interactions; to provide low-cost telehealth; and, to improve health care outcomes through the early identification of, and referral for, potential complications.

The hybrid 2wT system is used for relaying automated health education and check in messages to clients, generating tasks to improve client follow-up and health provider reporting, while also allowing for free text interaction between clients and healthcare providers. This document describes the hybrid 2wT system features and its functionalities. It also provides an opportunity for digital health implementers to explore and learn how direct-to-client communication can be achieved through integrating app features within the CHT, with messaging technologies such as RapidPro and SMS aggregators.

Problem being addressed

Recipients of health care in sub-Saharan Africa face significant barriers in accessing quality services. These barriers include geographical distance, limited availability of skilled health care workers, and both direct and indirect costs - all of which can present major challenges in clients getting timely access to health care. It is important to bring healthcare services closer to client’s households, especially those who live in remote areas. The D2C / 2wT workflows assist in addressing the health needs of such clients through connecting clients with health care providers based at the facility or community, including both clinicians or lower-level healthcare workers. Unlike phone calls which must be answered, D2C / 2wT offers convenient care – allowing both parties to communicate when needed or wanted. Unlike interventions that require a smartphone or downloading an app, 2wT clients communicate directly via the SMS capability in even basic mobile phones. D2C / 2wT can help improve healthcare access, encourage client engagement in care, promote healthy behaviors and support delivery of client centered care, including in harder to reach rural or remote areas.

Solution overview

The CHT offers general guidance on how SMS workflows can be built on CHT. CHT SMS based workflows can be configured to support D2C / 2wT between health care providers and clients. The D2C / 2wT workflows can be designed to support:

  • Regular client check-ins to help keep track of the client's condition
  • Sending of client follow up appointment reminders with confirmation requests
  • Sharing of information and educational health messages with the clients
  • Client tracing and client review tasks can be assigned to specific healthcare workers. Escalated workflows can result in tasks in CHT apps for CHWs, clerks or nurses, depending on need
  • Two-way conversation messaging between healthcare providers and clients
  • 2wT-based telehealth, providing a lower cost format for clinical triaging, reassurance or referral to in-person care via SMS

Furthermore, CHT allows integration with other health information systems including District Health Information Software 2 (DHIS2) and facility-based electronic medical record systems such as OpenMRS.

CHT SMS workflow technical overview

The SMS workflows in CHT can integrate with RapidPro flows to support two way interactive messaging between healthcare providers and household members. RapidPro is an open source tool that has the capability to support conversational messaging flows via SMS, Interactive Voice Response (IVR), Telegram, Facebook Messenger and WhatsApp. With the CHT-RapidPro integration, it is possible to design and configure SMS workflows in the two systems. Data is shared between CHT and Rapidpro via the APIs; and the information from the interactive texting is used to update clients details on CHT through CouchDB which can trigger tasks on CHT.

CHT-RapidPro SMS workflows allows for automated visit scheduling, replying and routing to an SMS based gateway based on preconfigured SMS logic, thus eliminating the need for a healthcare worker (clerk or clinician, depending on the intervention specifics) to send, monitor and reply to each text. To use SMS workflows with CHT, you will need a texting channel (a service that enables you to send or receive messages or a phone call). CHT can be integrated with a texting channel like an SMS aggregator which provides a reliable cloud based platform to send and receive an unlimited number of SMSs to and from clients or household members.

Direct-to-client, two-way texting, technical architecture

The CHT-RapidPro integration enables App builders to build complex conversational SMS workflows that support client care and support coordination of care at the facility and community levels.

Clients are enrolled directly using the CHT D2C, 2wT app using a phone, tablet or computer. Clients need only simple phones and do not need to download an app. The resulting client data is then stored and managed using CouchDB and a PostgreSQL database system. Through the CHT application programming interface (API), client messages are sent to Rapidpro, and RapidPro pushes summary data back to the CHT to update client responses and status. On the client interface these can be automated messages or interactive free text with healthcare providers. An SMS text message aggregator service and a short code channel linked to RapidPro streamlines the incoming and outgoing SMS text messages; while the CHT generates the appropriate tasks (help clinicians triage, refer, track, and trace clients)and reports (keep track of completed actions and monitor clients) as represented in figure 1.

Direct Messaging technical architecture

Figure 1: System architecture of direct-to-client (D2C), two-way texting (2wT) for voluntary medical male circumcision (VMMC) postoperative care. API: application programming interface; CHT: Community Health Toolkit.

The CHT core framework has inbuilt privacy/security features including two- factor authentication, role based authorization, data security, and a range of protected data access endpoints. The system can be configured to manage user roles and permissions for access of CHT applications. Responsible data practices is enforced through the Privacy and Data Protection Policy.

The D2C, 2wT app incorporates the CHT security features in order to protect client data and ensure the confidentiality and privacy of client information. Users are encouraged to use password protected user accounts, the app also has full disk encryption and auto-lock screens for users’ devices (android phones and tablets) in order to minimize the risk of unwarranted access. Role based access controls also make sure that users can only view and edit information that the permissions assigned to the user role allow them to.

Case adaptation: Voluntary medical male circumcision app co-designed by I-TECH and Medic

The University of Washington, Department of Global Health’s International Training and Education Center for Health (I-TECH), Medic, and local partners including Zim-TTECH, Lighthouse Trust, and Aurum Institute, collaboratively designed, developed, and scaled a CHT-based voluntary medical male circumcision (VMMC) app for post-operative follow-up with support from the National Institute of Health, USA The D2C, 2wT app supports automated, interactive telehealth via direct-to-client messaging between VMMC nurses and clients. 2wT telehealth helps improve the quality and efficiency of care, maintaining client safety while lowering follow-up care costs. In the spirit of contributing to the growth of the public global goods, I-TECH and Medic have coordinated to release the application source code of the D2C, 2wT app.

Users and hierarchy

UsersLocationDevicesRoles
Program managers / M&EAdmin levelLaptopUsers at this level have access to all sites. They can access many sites via the CHT D2C messaging App. They can download information from the dashboard.
Hub / Super nurseAdmin levelLaptop / DesktopUsers have access to multiple sites within the district and can access them via the CHT D2C messaging App. They are online users and are able to triage, refer clients to health facilities to be reviewed by site nurses.
Site nurseHealth facility or outreach siteSmartphoneUsers are routine MC nurses at health facilities or outreach sites who enroll D2C clients and interact with clients enrolled at their site via the CHT D2C messaging app. In higher volume facilities, data clerks may also be involved in data entry of client enrolment details.
VMMC ClientsCommunity / household levelSmartphone / feature phoneVMMC clients with access to mobile devices and who have consented to participate in and have been enrolled into the D2C, 2wT app. They will receive daily check in messages and can communicate with their health care providers via text.

Figure 2: User types and hierarchy

Workflows

Current workflow for VMMC client enrollment, screening and VMMC procedures (without messaging) are as described in figure 3 below.

Users and hierarchy example

Figure 3: Voluntary male circumcision workflow before direct-to-client messaging was introduced

1. Automated check-in workflow to support client care

Eligible male clients with access to mobile phones are registered on the VMMC app to enable them to receive daily check-in texts to help track the condition of the clients for 13 days after the circumcision. During registration (Day 0), details about the method of circumcision; whether the client is an adult or a minor, and the preferred phone numbers to be used for communication are recorded. Each client receives an enrollment confirmation to ensure that the communication channel is open. Clients also receive postoperative counseling using the 2wT flip-book on how to respond to the daily SMS text message and review photos that illustrate the signs of complications for reporting via daily SMS text messages. VMMC clients, including those with 2wT, have the option to attend routine pre-scheduled visits on day 2 and day 7 post-op.

After the registration, the VMMC clients registered on the VMMC app receive preconfigured, bidirectional daily follow up texts to check on their healing. These are in the form of automated daily SMS text messages on days 1 to 13 in the predominant languages of the area. The clients respond to the daily SMS text message with a single numeric (0=no problem or 1=I need help) response. They could respond to any daily SMS text message at any time. They could also initiate an unrestricted, freely-worded text response at any time, and in any language - instead of or in addition to the daily numeric response.

An adverse event (AE) is any complication or problem that may happen during or after a surgery, including a VMMC surgery. AEs may be mild, moderate or severe. All moderate and severe AEs should be reviewed by a clinician. Moderate and severe AEs after VMMC are rare, but occur about 2% of the time. . Identifying AEs swiftly and managing them properly is a sign of a quality surgical program, including VMMC programs.

Patient automated check-in SMS workflows

Figure 4: Direct to client messaging for voluntary male circumcision clients without adverse effects

2. Triaging to care workflow for clients with potential complications

Clients with potential AEs (those who respond with 1 or a free text indicating concerns) are followed up by nurses and given care via the D2C, 2wT intervention. Clients may be followed up via SMS / call. Nurses provide education and reassurance, or refer in cases where they need to go to a health facility for in-person review if an emergency was suspected (Figure 5). The clients could request a call back to speak with the nurse during routine clinic hours; the nurse could also initiate calls to follow up with clients when desired.

Figure 5: Direct-to-client messaging for voluntary male circumcision clients without adverse effects

Figure 5: Direct-to-client messaging for voluntary male circumcision clients without adverse effects

3. Escalated workflow to generate tasks when clients do not respond

If the client has not responded to any of the daily check in-messages by day 3, they get a reminder message to check on them and prompt a response. A tracing follow up task is triggered for the 2WT nurse if a VMMC client does not respond to any of the daily check in texts by day 4 for both minors and adults. The 2WT nurse then records the tracing method and outcomes by completing the trace client task.

A VMMC client who responds with a potential complication triggers a Client review: Potential complication task for the 2WT nurse. They then report and fill in whether the client was reviewed by a clinician. If the client has been reviewed by a clinician, they record the details of the review, time, place and adverse events identified. If the client has not been reviewed by a clinician, the 2WT nurse will try and trace them by SMS, phone call or a home visit; and report the tracing outcomes as shown in figure 6 below.

Figure 6: Direct-to-client messaging for voluntary male circumcision clients without adverse effects

Figure 6: Direct-to-client messaging for voluntary male circumcision clients without adverse effects

4. Clients can share requests by messaging health care providers.

Clients registered in the intervention can also initiate the bidirectional messaging with a 2WT Nurse by messaging a central phone number or a short code. Clients can report any concern, ask a question about wound healing, or request for help from health care providers via SMS. For this workflow, the logic can be preconfigured to support health triage and clinical referrals. Using the messaging functionality on CHT, health care providers based at the facility can view, manage and respond to incoming texts from VMMC clients.

Evidence of Impact

In collaboration with researchers at the University of Washington, we assessed the effectiveness of the 2wT intervention through randomized controlled trials in Zimbabwe and South Africa. The foundational randomized control trial in Zimbabwe in 2018, demonstrated that post-surgical client follow up via SMS was as safe as follow up care at the clinic, and that the transition to SMS-based follow-up option enabled an 85% reduction in unnecessary clinic visits. Similar findings were reported in South Africa. This approach to messaging with clients was more efficient and cheaper than standard care, thereby lowering costs. Both clients and clinicians both found the approach to be highly usable and acceptable for post-operative care and further usability studies found that it was preferred by clients and providers alike (see figure 7). We are also expanding our application of D2C, 2wT for antiretroviral therapy (ART) clients in Malawi, aiming to have similarly positive impact on engagement in ART and retention in HIV care.

Figure 7: Evidence supported benefits of direct-to-client messaging for voluntary male circumcision clients

Figure 7: Evidence supported benefits of direct-to-client messaging for voluntary male circumcision clients

More scenarios where direct-to-client messaging can be used to support client care

1. Facility appointment reminders workflow

Appointment reminders can be configured on CHT so that household members and clients can receive facility appointment reminders. Facility appointment reminders are configured as per the recommended health guidelines for specific use cases. The clinic visit appointment reminders can be configured to automatically stop once a client completes the expected clinic visits.

2. Active case finding by messaging household members with survey questions

Active case finding by messaging households with survey questions about the health of family members.

Frequently Asked Questions

1. Is the client charged for receiving or sending the SMS?

It is possible for health programs to acquire a zero rated short code service which is free for clients and household members to send and receive texts from the short code.

2. What are ways to handle exceptions?

Exceptions can occur when programs and flows do not work as expected. This may occur due to various technical issues and may be unavoidable. One possible way to handle these unwanted issues and errors is to set up the mail group such that a notification email is sent whenever there is any exception. Another method is to keep track of whether the flow was completed i.e., all the responses from start to finish nodes were received. For the participants whose flow may have been interrupted or incomplete, one may run the workflow again.

3. Ways to improve response rate from clients?

The workflows in RapidPro can be set up in such a way that they expire after a certain time period. Workflows can expire when there is no response from a client within a time period. You can handle such situations by repeating the workflow i.e., sending them again to the client after a certain time interval. Alternatively, it can be beneficial to do some research on which time to send the message. For example, a person may be busy at work and/or away from the phone during working hours. It may be easier for them to respond during early morning or later in the evening than in the afternoon.

Resources

VMMC code can be accessed using the following link D2C messaging app for post-op care

Training materials
Publications

Related pages - UW website, I-tech/Aurum page

Funding: 2wT Zimbabwe was supported by the Fogarty International Center of the National Institutes of Health under Award Number R21TW010583, PI Feldacker. 2wT South Africa is supported by National Institute of Nursing Research of the National Institutes of Health (NIH) under award number 5R01NR019229, PIs Feldacker and Setswe. The content is solely the responsibility of the collaborators and authors and does not necessarily represent the official views of the National Institutes of Health.


CHT Applications > + Create project issue

Direct-to-client, two-way texting workflows on CHT

Reference for Direct-to-client, two-way texting workflows with CHT and RapidPro

This documentation provides a guide for designing and deploying direct-to-client (D2C), two-way texting (2wT) workflows to support client follow-up care using the community health toolkit (CHT). 2wT is a mobile text messaging system built on open-source, CHT tools to engage clients in their health care; to facilitate prompt client - healthcare provider interactions; to provide low-cost telehealth; and, to improve health care outcomes through the early identification of, and referral for, potential complications.

The hybrid 2wT system is used for relaying automated health education and check in messages to clients, generating tasks to improve client follow-up and health provider reporting, while also allowing for free text interaction between clients and healthcare providers. This document describes the hybrid 2wT system features and its functionalities. It also provides an opportunity for digital health implementers to explore and learn how direct-to-client communication can be achieved through integrating app features within the CHT, with messaging technologies such as RapidPro and SMS aggregators.

Problem being addressed

Recipients of health care in sub-Saharan Africa face significant barriers in accessing quality services. These barriers include geographical distance, limited availability of skilled health care workers, and both direct and indirect costs - all of which can present major challenges in clients getting timely access to health care. It is important to bring healthcare services closer to client’s households, especially those who live in remote areas. The D2C / 2wT workflows assist in addressing the health needs of such clients through connecting clients with health care providers based at the facility or community, including both clinicians or lower-level healthcare workers. Unlike phone calls which must be answered, D2C / 2wT offers convenient care – allowing both parties to communicate when needed or wanted. Unlike interventions that require a smartphone or downloading an app, 2wT clients communicate directly via the SMS capability in even basic mobile phones. D2C / 2wT can help improve healthcare access, encourage client engagement in care, promote healthy behaviors and support delivery of client centered care, including in harder to reach rural or remote areas.

Solution overview

The CHT offers general guidance on how SMS workflows can be built on CHT. CHT SMS based workflows can be configured to support D2C / 2wT between health care providers and clients. The D2C / 2wT workflows can be designed to support:

  • Regular client check-ins to help keep track of the client's condition
  • Sending of client follow up appointment reminders with confirmation requests
  • Sharing of information and educational health messages with the clients
  • Client tracing and client review tasks can be assigned to specific healthcare workers. Escalated workflows can result in tasks in CHT apps for CHWs, clerks or nurses, depending on need
  • Two-way conversation messaging between healthcare providers and clients
  • 2wT-based telehealth, providing a lower cost format for clinical triaging, reassurance or referral to in-person care via SMS

Furthermore, CHT allows integration with other health information systems including District Health Information Software 2 (DHIS2) and facility-based electronic medical record systems such as OpenMRS.

CHT SMS workflow technical overview

The SMS workflows in CHT can integrate with RapidPro flows to support two way interactive messaging between healthcare providers and household members. RapidPro is an open source tool that has the capability to support conversational messaging flows via SMS, Interactive Voice Response (IVR), Telegram, Facebook Messenger and WhatsApp. With the CHT-RapidPro integration, it is possible to design and configure SMS workflows in the two systems. Data is shared between CHT and Rapidpro via the APIs; and the information from the interactive texting is used to update clients details on CHT through CouchDB which can trigger tasks on CHT.

CHT-RapidPro SMS workflows allows for automated visit scheduling, replying and routing to an SMS based gateway based on preconfigured SMS logic, thus eliminating the need for a healthcare worker (clerk or clinician, depending on the intervention specifics) to send, monitor and reply to each text. To use SMS workflows with CHT, you will need a texting channel (a service that enables you to send or receive messages or a phone call). CHT can be integrated with a texting channel like an SMS aggregator which provides a reliable cloud based platform to send and receive an unlimited number of SMSs to and from clients or household members.

Direct-to-client, two-way texting, technical architecture

The CHT-RapidPro integration enables App builders to build complex conversational SMS workflows that support client care and support coordination of care at the facility and community levels.

Clients are enrolled directly using the CHT D2C, 2wT app using a phone, tablet or computer. Clients need only simple phones and do not need to download an app. The resulting client data is then stored and managed using CouchDB and a PostgreSQL database system. Through the CHT application programming interface (API), client messages are sent to Rapidpro, and RapidPro pushes summary data back to the CHT to update client responses and status. On the client interface these can be automated messages or interactive free text with healthcare providers. An SMS text message aggregator service and a short code channel linked to RapidPro streamlines the incoming and outgoing SMS text messages; while the CHT generates the appropriate tasks (help clinicians triage, refer, track, and trace clients)and reports (keep track of completed actions and monitor clients) as represented in figure 1.

Direct Messaging technical architecture

Figure 1: System architecture of direct-to-client (D2C), two-way texting (2wT) for voluntary medical male circumcision (VMMC) postoperative care. API: application programming interface; CHT: Community Health Toolkit.

The CHT core framework has inbuilt privacy/security features including two- factor authentication, role based authorization, data security, and a range of protected data access endpoints. The system can be configured to manage user roles and permissions for access of CHT applications. Responsible data practices is enforced through the Privacy and Data Protection Policy.

The D2C, 2wT app incorporates the CHT security features in order to protect client data and ensure the confidentiality and privacy of client information. Users are encouraged to use password protected user accounts, the app also has full disk encryption and auto-lock screens for users’ devices (android phones and tablets) in order to minimize the risk of unwarranted access. Role based access controls also make sure that users can only view and edit information that the permissions assigned to the user role allow them to.

Case adaptation: Voluntary medical male circumcision app co-designed by I-TECH and Medic

The University of Washington, Department of Global Health’s International Training and Education Center for Health (I-TECH), Medic, and local partners including Zim-TTECH, Lighthouse Trust, and Aurum Institute, collaboratively designed, developed, and scaled a CHT-based voluntary medical male circumcision (VMMC) app for post-operative follow-up with support from the National Institute of Health, USA The D2C, 2wT app supports automated, interactive telehealth via direct-to-client messaging between VMMC nurses and clients. 2wT telehealth helps improve the quality and efficiency of care, maintaining client safety while lowering follow-up care costs. In the spirit of contributing to the growth of the public global goods, I-TECH and Medic have coordinated to release the application source code of the D2C, 2wT app.

Users and hierarchy

UsersLocationDevicesRoles
Program managers / M&EAdmin levelLaptopUsers at this level have access to all sites. They can access many sites via the CHT D2C messaging App. They can download information from the dashboard.
Hub / Super nurseAdmin levelLaptop / DesktopUsers have access to multiple sites within the district and can access them via the CHT D2C messaging App. They are online users and are able to triage, refer clients to health facilities to be reviewed by site nurses.
Site nurseHealth facility or outreach siteSmartphoneUsers are routine MC nurses at health facilities or outreach sites who enroll D2C clients and interact with clients enrolled at their site via the CHT D2C messaging app. In higher volume facilities, data clerks may also be involved in data entry of client enrolment details.
VMMC ClientsCommunity / household levelSmartphone / feature phoneVMMC clients with access to mobile devices and who have consented to participate in and have been enrolled into the D2C, 2wT app. They will receive daily check in messages and can communicate with their health care providers via text.

Figure 2: User types and hierarchy

Workflows

Current workflow for VMMC client enrollment, screening and VMMC procedures (without messaging) are as described in figure 3 below.

Users and hierarchy example

Figure 3: Voluntary male circumcision workflow before direct-to-client messaging was introduced

1. Automated check-in workflow to support client care

Eligible male clients with access to mobile phones are registered on the VMMC app to enable them to receive daily check-in texts to help track the condition of the clients for 13 days after the circumcision. During registration (Day 0), details about the method of circumcision; whether the client is an adult or a minor, and the preferred phone numbers to be used for communication are recorded. Each client receives an enrollment confirmation to ensure that the communication channel is open. Clients also receive postoperative counseling using the 2wT flip-book on how to respond to the daily SMS text message and review photos that illustrate the signs of complications for reporting via daily SMS text messages. VMMC clients, including those with 2wT, have the option to attend routine pre-scheduled visits on day 2 and day 7 post-op.

After the registration, the VMMC clients registered on the VMMC app receive preconfigured, bidirectional daily follow up texts to check on their healing. These are in the form of automated daily SMS text messages on days 1 to 13 in the predominant languages of the area. The clients respond to the daily SMS text message with a single numeric (0=no problem or 1=I need help) response. They could respond to any daily SMS text message at any time. They could also initiate an unrestricted, freely-worded text response at any time, and in any language - instead of or in addition to the daily numeric response.

An adverse event (AE) is any complication or problem that may happen during or after a surgery, including a VMMC surgery. AEs may be mild, moderate or severe. All moderate and severe AEs should be reviewed by a clinician. Moderate and severe AEs after VMMC are rare, but occur about 2% of the time. . Identifying AEs swiftly and managing them properly is a sign of a quality surgical program, including VMMC programs.

Patient automated check-in SMS workflows

Figure 4: Direct to client messaging for voluntary male circumcision clients without adverse effects

2. Triaging to care workflow for clients with potential complications

Clients with potential AEs (those who respond with 1 or a free text indicating concerns) are followed up by nurses and given care via the D2C, 2wT intervention. Clients may be followed up via SMS / call. Nurses provide education and reassurance, or refer in cases where they need to go to a health facility for in-person review if an emergency was suspected (Figure 5). The clients could request a call back to speak with the nurse during routine clinic hours; the nurse could also initiate calls to follow up with clients when desired.

Figure 5: Direct-to-client messaging for voluntary male circumcision clients without adverse effects

Figure 5: Direct-to-client messaging for voluntary male circumcision clients without adverse effects

3. Escalated workflow to generate tasks when clients do not respond

If the client has not responded to any of the daily check in-messages by day 3, they get a reminder message to check on them and prompt a response. A tracing follow up task is triggered for the 2WT nurse if a VMMC client does not respond to any of the daily check in texts by day 4 for both minors and adults. The 2WT nurse then records the tracing method and outcomes by completing the trace client task.

A VMMC client who responds with a potential complication triggers a Client review: Potential complication task for the 2WT nurse. They then report and fill in whether the client was reviewed by a clinician. If the client has been reviewed by a clinician, they record the details of the review, time, place and adverse events identified. If the client has not been reviewed by a clinician, the 2WT nurse will try and trace them by SMS, phone call or a home visit; and report the tracing outcomes as shown in figure 6 below.

Figure 6: Direct-to-client messaging for voluntary male circumcision clients without adverse effects

Figure 6: Direct-to-client messaging for voluntary male circumcision clients without adverse effects

4. Clients can share requests by messaging health care providers.

Clients registered in the intervention can also initiate the bidirectional messaging with a 2WT Nurse by messaging a central phone number or a short code. Clients can report any concern, ask a question about wound healing, or request for help from health care providers via SMS. For this workflow, the logic can be preconfigured to support health triage and clinical referrals. Using the messaging functionality on CHT, health care providers based at the facility can view, manage and respond to incoming texts from VMMC clients.

Evidence of Impact

In collaboration with researchers at the University of Washington, we assessed the effectiveness of the 2wT intervention through randomized controlled trials in Zimbabwe and South Africa. The foundational randomized control trial in Zimbabwe in 2018, demonstrated that post-surgical client follow up via SMS was as safe as follow up care at the clinic, and that the transition to SMS-based follow-up option enabled an 85% reduction in unnecessary clinic visits. Similar findings were reported in South Africa. This approach to messaging with clients was more efficient and cheaper than standard care, thereby lowering costs. Both clients and clinicians both found the approach to be highly usable and acceptable for post-operative care and further usability studies found that it was preferred by clients and providers alike (see figure 7). We are also expanding our application of D2C, 2wT for antiretroviral therapy (ART) clients in Malawi, aiming to have similarly positive impact on engagement in ART and retention in HIV care.

Figure 7: Evidence supported benefits of direct-to-client messaging for voluntary male circumcision clients

Figure 7: Evidence supported benefits of direct-to-client messaging for voluntary male circumcision clients

More scenarios where direct-to-client messaging can be used to support client care

1. Facility appointment reminders workflow

Appointment reminders can be configured on CHT so that household members and clients can receive facility appointment reminders. Facility appointment reminders are configured as per the recommended health guidelines for specific use cases. The clinic visit appointment reminders can be configured to automatically stop once a client completes the expected clinic visits.

2. Active case finding by messaging household members with survey questions

Active case finding by messaging households with survey questions about the health of family members.

Frequently Asked Questions

1. Is the client charged for receiving or sending the SMS?

It is possible for health programs to acquire a zero rated short code service which is free for clients and household members to send and receive texts from the short code.

2. What are ways to handle exceptions?

Exceptions can occur when programs and flows do not work as expected. This may occur due to various technical issues and may be unavoidable. One possible way to handle these unwanted issues and errors is to set up the mail group such that a notification email is sent whenever there is any exception. Another method is to keep track of whether the flow was completed i.e., all the responses from start to finish nodes were received. For the participants whose flow may have been interrupted or incomplete, one may run the workflow again.

3. Ways to improve response rate from clients?

The workflows in RapidPro can be set up in such a way that they expire after a certain time period. Workflows can expire when there is no response from a client within a time period. You can handle such situations by repeating the workflow i.e., sending them again to the client after a certain time interval. Alternatively, it can be beneficial to do some research on which time to send the message. For example, a person may be busy at work and/or away from the phone during working hours. It may be easier for them to respond during early morning or later in the evening than in the afternoon.

Resources

VMMC code can be accessed using the following link D2C messaging app for post-op care

Training materials
Publications

Related pages - UW website, I-tech/Aurum page

Funding: 2wT Zimbabwe was supported by the Fogarty International Center of the National Institutes of Health under Award Number R21TW010583, PI Feldacker. 2wT South Africa is supported by National Institute of Nursing Research of the National Institutes of Health (NIH) under award number 5R01NR019229, PIs Feldacker and Setswe. The content is solely the responsibility of the collaborators and authors and does not necessarily represent the official views of the National Institutes of Health.


CHT Applications > Concepts

Basic concepts that will help you understand how CHT applications are built

CHT Applications > Reference > forms/ > app

App Forms: Used to complete reports, tasks, and actions in the app

CHT Applications > Reference > tasks.js

Tasks: Definition of tasks shown to app users

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/examples/ebs/index.html b/apps/examples/ebs/index.html index 33723fd9fd..1a7093c6c6 100644 --- a/apps/examples/ebs/index.html +++ b/apps/examples/ebs/index.html @@ -1,9 +1,9 @@ -Event Based Surveillance | Community Health Toolkit +Event Based Surveillance | Community Health Toolkit

Event Based Surveillance

Capturing information about community events that are a risk to public health

The CHT’s Event Based Surveillance (EBS) functionality enables rapid capture of information about community events that are a potential risk to public health. Deployed at the community level, this functionality is designed to:

  • Achieve earliest possible detection of COVID-19 cases in communities
  • Provide visibility into signals, reports, and investigations centrally for follow-up
  • Coordinate and support local action based on existing roles in disease surveillance

Problem Being Addressed

Patient reporting through traditional primary health care channels is not well suited for detecting rapidly spreading outbreaks at a community scale. Timely detection of outbreaks and important public health events can be delayed or missed when relying on individual patient data and traditional health management information systems.

Solution Overview

Event Based Surveillance with the CHT offers opportunities for rapid deployment and measurable community impact for reportable diseases, including COVID-19. Through more timely event data reporting and analysis, the CHT provides early indicators of emerging issues to community health workers across the health system. The CHT solution offers:

  • Tiered reporting, verification, and escalation process, which can improve speed and quality of data
  • Signal data that can be mapped for data-driven decision-making
  • Pre-populated with Supervisor and CHW contact information
  • Health program continuity with the inclusion of pre-existing care workflows
  • System scalability through training CHWs remotely on their role and responsibilities

Users and Hierarchy Example

UserLocationDevicesRole
National and District OfficialsCentral and district officesDesktop, laptop and smartphonesMonitor overall system and analytics regarding verified, investigated and missing cases.
Sub-District InvestigatorsFacilities or local officesTablet or smartphoneInvestigate verified reports within 24 hours of signal report; add data to signal case.
CHW SupervisorsCommunity level, based at facilitiesSmartphones and personal phonesVerify COVID-19 signals. Provide feedback and guidance to CHWs.
CHWs and VolunteersCommunity levelFeature phones and some smartphonesDetect and report signals. Provide guidance and health education to patients and families.

Workflow Example

This demo illustrates a workflow that enables CHWs to use SMS to report signal codes for specific community health threats. The CHW Supervisor can then verify the initial report and, if warranted, escalate to the sub-county for investigation and follow-up. It is designed to be flexible and can be combined with other complimentary care workflows.



More background information can be found in this summary deck.

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/examples/index.html b/apps/examples/index.html index 748307687b..d6c0ac87a3 100644 --- a/apps/examples/index.html +++ b/apps/examples/index.html @@ -1,9 +1,9 @@ -Examples and Reference Applications | Community Health Toolkit +Examples and Reference Applications | Community Health Toolkit

Examples and Reference Applications

Templates for structuring and organizing a community health workflow, configuration code, and testing framework

The Community Health Toolkit’s examples and reference apps provide templates for structuring and organizing a community health workflow, its configuration code, and testing framework. They include a foundation for forms, data fields, and analytics, and can be deployed as-is or easily customized by a developer for your unique context.


Maternal and Newborn Health Reference App

Reference application for maternal and newborn care for CHW’s using a mobile app

Contact Tracing

A community surveillance tool to help control infectious disease outbreaks and mitigate secondary disease transmission

Primary Health Care Adaptations for COVID-19

Primary health care workflow and training adaptations for COVID-19

COVID-19 Education and Training for CHWs

Education and training for Community Health Workers to address COVID-19

COVID-19 Testing with Rapid Diagnostic Tests

CHT example application that uses a third party app to capture the result of a Rapid Diagnostic Test.

Direct-to-client, two-way texting workflows on CHT

Reference for Direct-to-client, two-way texting workflows with CHT and RapidPro

Event Based Surveillance

Capturing information about community events that are a risk to public health

Learning and Care

An integration built to pilot the integrated workflows focused on CHW remote learning and care support for COVID-19.

Loss to Follow Up

Generating patients with missed follow-up appointments between CHT and requesting system

Pharmacovigilance Reference app

CHT example application that supports pharmacovigilance in a community setting.

YendaNafe CHT app by PIH in Malawi

The YendaNafe app co-designed by PIH Malawi and Medic

Remote Onboarding and Training

App and care workflow training using remote capabilities.

Stock Monitoring

Guidance on design and development of stock monitoring workflows.

Reference Application for CHW Supervision and Performance Management

A reference app for CHW supervisors to support performance management of CHWs using a mobile app.

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/examples/index.xml b/apps/examples/index.xml index 33c32a02a7..14d6c55fa8 100644 --- a/apps/examples/index.xml +++ b/apps/examples/index.xml @@ -1,1885 +1,11 @@ -Community Health Toolkit – Examples and Reference Applicationshttps://docs.communityhealthtoolkit.org/apps/examples/Recent content in Examples and Reference Applications on Community Health ToolkitHugo -- gohugo.ioenApps: Maternal and Newborn Health Reference Apphttps://docs.communityhealthtoolkit.org/apps/examples/anc/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/anc/ -<div class="pageinfo pageinfo-primary"> -<p>This &ldquo;reference application&rdquo; for maternal and newborn health provides a template for structuring and organizing your Community Health Toolkit digital health app, its configuration, and test code. It can be used as is, or serve as a great way to learn about the CHT&rsquo;s foundation for forms, data fields, and analytics that can be easily customized to fit your context.</p> -</div> -<h2 id="problem-being-addressed">Problem Being Addressed</h2> -<p>Access to quality maternal and newborn care is the cornerstone of many community health programs. For many women living in communities at the last mile, pregnancy can be a <a href="https://www.who.int/health-topics/maternal-health">vulnerable time</a>. There is a need for community health programs to support early pregnancy registration, consistent antenatal care (ANC) visits, and in-facility deliveries. In addition, the short time window following delivery for postnatal care (PNC) is a critical time for catching life-threatening danger signs for the new mother and baby.</p> -<h2 id="solution-overview">Solution Overview</h2> -<p>The maternal and newborn health workflow ensures that women receive the care that they need during their pregnancy with the support of CHWs. Early pregnancy registration, timely antenatal care visits, and improved care coordination between CHWs and clinics increase the likelihood that women will deliver in a facility with the support of skilled birth attendants. This ultimately will help save the lives of mothers and babies, as well as strengthen the maternal and newborn health services of the health system. Community Health Toolkit applications with maternal and newborn workflows are used by health workers and clinical staff to:</p> -<ul> -<li>Register pregnancies</li> -<li>Provide a schedule for on-time ANC visits</li> -<li>Offer education for the mother at each stage of pregnancy</li> -<li>Screen for and report danger signs in the pregnant woman and newborn</li> -<li>Refer and encourage pregnant women to deliver at a facility</li> -<li>Ensure on-time PNC visits for mother and newborn</li> -</ul> -<h2 id="forms-hierarchy">Forms Hierarchy</h2> -<p>Once a <a href="https://docs.communityhealthtoolkit.org/apps/concepts/hierarchy/">hierarchy</a> of people and places is established, forms are added at different levels. This diagram indicates the forms that can be filled about a person in the app (in this case, family members at the household level), as well as the person/user who will access these forms and make the reports (CHWs at the CHW Area level). Some forms are accessible as actions from the family member’s profile as actions, others from the CHW’s task list as tasks, and some as either.</p> -<figure class="right col-12 col-lg-12"><a href="forms-hierarchy.png"> -<img src="forms-hierarchy.png"/> </a> -</figure> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/reference/forms/app/">Controlling form properties</a></p> -<h2 id="workflows">Workflows</h2> -<p>Both maternal and newborn care workflows are defined to connect form actions and data with people. Detailed documentation for these forms and task schedules are linked from the workflow diagrams below. Accompanying this documentation are tips and insights into the design decisions made along the way, and suggestions for how and where to customize the forms.</p> -<h3 id="pregnancy-workflow">Pregnancy Workflow</h3> -<div class="container workflow align-items-center"> -<div class="row d-none d-sm-flex text-center"> -<div class="col-3 align-self-end"> -<img src="pregnancy-1.png" alt="Condition" /><h4>Condition</h4> -</div> -<div class="col-1"></div> -<div class="col-3 align-self-end"> -<img src="pregnancy-2.png" alt="Task" /><h4>Task</h4> -</div> -<div class="col-1"></div> -<div class="col-3 align-self-end"> -<img src="pregnancy-3.png" alt="Resolution" /><h4>Resolution</h4> -</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>Upon discovering a pregnancy, a CHW submits a <a href="https://docs.google.com/drawings/d/1u4OQIgTyzUysFv9Cop-C54nrbPiuPmq7GTpBTFmwZn0/edit">Pregnancy Form</a> confirming a new pregnancy with the estimated gestational age.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>At the 8 ANC touchpoints defined by the WHO, the CHW receives a <a href="https://docs.google.com/document/d/17pJXBf2gEB2wD1P5g9S5XQ2u4QnLGMiUU4sX0XQsvSk/edit#heading=h.wf9x0zhfeasi">Pregnancy Home Visit Task</a> to let her know that it’s time to check in on the pregnant woman.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>CHW submits <a href="https://docs.google.com/drawings/d/1_2i6XTMtMkrfQ6NFNjcEDwJPS8i0rEeQlaPgYQhacSw/edit">Pregnancy Home Visit Form</a>, demonstrating that she provided ANC counseling, gathered information from prior facility visits, and screened for danger signs.</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>A <a href="https://docs.google.com/drawings/d/1u4OQIgTyzUysFv9Cop-C54nrbPiuPmq7GTpBTFmwZn0/edit">Pregnancy Form</a> is submitted with the gestational age unknown.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>A <a href="https://docs.google.com/document/d/17pJXBf2gEB2wD1P5g9S5XQ2u4QnLGMiUU4sX0XQsvSk/edit#heading=h.2pls723gu6wl">Pregnancy Visit Task</a> appears every 2 weeks for 42 weeks.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>CHW submits <a href="https://docs.google.com/drawings/d/1_2i6XTMtMkrfQ6NFNjcEDwJPS8i0rEeQlaPgYQhacSw/edit">Pregnancy Home Visit Form</a>. If the gestational age is entered, the workflow will change to <a href="https://docs.google.com/document/d/17pJXBf2gEB2wD1P5g9S5XQ2u4QnLGMiUU4sX0XQsvSk/edit#heading=h.wf9x0zhfeasi">Pregnancy Home Visit Tasks</a>.</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>A CHW submits a <a href="https://docs.google.com/drawings/d/1_2i6XTMtMkrfQ6NFNjcEDwJPS8i0rEeQlaPgYQhacSw/edit">Pregnancy Home Visit Form</a> that includes an upcoming facility visit date.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>A <a href="https://docs.google.com/document/d/17pJXBf2gEB2wD1P5g9S5XQ2u4QnLGMiUU4sX0XQsvSk/edit#heading=h.v3b7bata6j">Health Facility ANC Reminder Task</a> 1 week ahead of the facility visit to remind the woman to attend.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>CHW submits <em>Health Facility ANC Reminder Form</em>, confirming that she called or visited the woman to remind her of her upcoming facility visit.</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>If the CHW notices danger signs at any time, then she submits a <em>Danger Sign Form</em> and immediately refers the patient to the facility.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>A <a href="https://docs.google.com/document/d/17pJXBf2gEB2wD1P5g9S5XQ2u4QnLGMiUU4sX0XQsvSk/edit#heading=h.82ea7ww1x1k">Danger Sign Follow-Up Task</a> will appear immediately and is due 3 days later. Tasks persists for 7 days after due date.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>CHW submits a <em>Danger Sign Follow-Up Form</em>, verifying that she called or visited the woman to confirm that she attended the facility. If this is not received, another <em>Danger Sign Follow-Up Task</em> is triggered.</div> -</div> -</div> -<h3 id="delivery-workflow">Delivery Workflow</h3> -<div class="container workflow align-items-center"> -<div class="row d-none d-sm-flex text-center"> -<div class="col-3 align-self-end"> -<img src="delivery-1.png" alt="Condition" /><h4>Condition</h4> -</div> -<div class="col-1"></div> -<div class="col-3 align-self-end"> -<img src="delivery-2.png" alt="Task" /><h4>Task</h4> -</div> -<div class="col-1"></div> -<div class="col-3 align-self-end"> -<img src="delivery-3.png" alt="Resolution" /><h4>Resolution</h4> -</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>A currently registered pregnant person has reached a gestational age of 42 weeks and has not had a miscarriage or a delivery reported.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>A <a href="https://docs.google.com/document/d/17pJXBf2gEB2wD1P5g9S5XQ2u4QnLGMiUU4sX0XQsvSk/edit#heading=h.roycts63w3b8">Delivery Task</a> requesting that the CHW check in on the woman to see whether she has delivered.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>CHW submits a <a href="https://docs.google.com/drawings/d/1r4SkpSGWzvOZPv3-pFp9wS1k531MnWcbjzzC8U4tt0o/edit">Delivery Form</a>, confirming the pregnancy outcomes. Profiles are created for each baby that is alive. This “ends” the pregnancy workflow.</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>A CHW submits a <a href="https://docs.google.com/drawings/d/1r4SkpSGWzvOZPv3-pFp9wS1k531MnWcbjzzC8U4tt0o/edit">Delivery Form</a> that contains a danger sign for mom or baby, or reports that either mom or baby are “alive and unwell.”</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>A <a href="https://docs.google.com/document/d/17pJXBf2gEB2wD1P5g9S5XQ2u4QnLGMiUU4sX0XQsvSk/edit#heading=h.ojap92eg1e82">Danger Sign Follow-Up Task</a> appears immediately and is due 3 days later. Persists for 7 days after due date.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>The CHW calls or visits woman to confirm that she attended and submits a <em>Danger Sign Follow-Up Form</em>. If this is not received, another <a href="https://docs.google.com/document/d/17pJXBf2gEB2wD1P5g9S5XQ2u4QnLGMiUU4sX0XQsvSk/edit#heading=h.ojap92eg1e82">Danger Sign Follow-Up Task</a> is triggered.</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>If the CHW notices danger signs at any time, then she submits a <em>Danger Sign Form</em> and immediately refers the patient to the facility.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>A <a href="https://docs.google.com/document/d/17pJXBf2gEB2wD1P5g9S5XQ2u4QnLGMiUU4sX0XQsvSk/edit#heading=h.ojap92eg1e82">Danger Sign Follow-Up Task</a> will appear immediately and is due 3 days later. Tasks persists for 7 days after due date.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>CHW submits <em>Danger Sign Follow-Up Form</em>, verifying that she called or visited the woman to confirm that she attended the facility. If this is not received, another <a href="https://docs.google.com/document/d/17pJXBf2gEB2wD1P5g9S5XQ2u4QnLGMiUU4sX0XQsvSk/edit#heading=h.ojap92eg1e82">Danger Sign Follow-Up Task</a> is triggered.</div> -</div> -</div> -<h2 id="additional-resources-to-get-started">Additional Resources to Get Started</h2> -<p>Here are a few additional resources to help get you started with the maternal and newborn health reference application.</p> -<ul> -<li>View the <a href="https://github.com/medic/cht-core/tree/master/config/default/">configuration code for this reference app</a></li> -<li>Install the reference app following these <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/">easy installation instructions</a></li> -<li>Modify the maternal and newborn reference application for your project context using <a href="https://docs.communityhealthtoolkit.org/design/best-practices/">configuration best practices</a></li> -<li>Understand the basis for measuring the impact of maternal and newborn workflows by reviewing the World Health Organization&rsquo;s <a href="https://rho.emro.who.int/data-r">Core Health Indicators</a></li> -</ul>Apps: Contact Tracinghttps://docs.communityhealthtoolkit.org/apps/examples/contact-tracing/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/contact-tracing/ -<p>The CHT’s Contact Tracing functionality enables effective disease surveillance within communities to help control infectious disease outbreaks. It is a community public health tool that is designed to:</p> -<ul> -<li>Centrally register patient cases and track contacts to prevent secondary spread of diseases in communities</li> -<li>Create a coordinated approach to contact tracing within existing health systems</li> -<li>Communicate the importance of self-isolation and symptom screening to exposed individuals and their families</li> -</ul> -<h2 id="problem-being-addressed">Problem Being Addressed</h2> -<p>An essential part of containing disease outbreaks, such as COVID-19, requires public health organizations to rapidly notify people who have come into contact with confirmed or suspected patient cases. In many health systems, existing health management information systems are ill equipped to handle the necessary rapid tracking, triage, and care referrals for large numbers of patient cases and contacts to prevent secondary spread of viruses.</p> -<h2 id="solution-overview">Solution Overview</h2> -<p>Contact Tracing with the CHT offers a rapidly scalable surveillance strategy by registering patient cases and their contacts within communities in a responsive and empathetic way. Through proactive tracing of contacts, active symptom screening, and referrals to care or self-isolation, the app can be deployed to assist in:</p> -<ul> -<li>Disrupting secondary transmission via coordinated registration of patient cases</li> -<li>Enrolling all contacts and households in monitoring during isolation periods</li> -<li>Referring symptomatic cases for skilled care following triage</li> -<li>Engaging in community education and follow-up during isolation</li> -</ul> -<h2 id="users-and-hierarchy-example">Users and Hierarchy Example</h2> -<table> -<thead> -<tr> -<th style="text-align:left">User</th> -<th style="text-align:left">Location</th> -<th style="text-align:left">Devices</th> -<th style="text-align:left">Role</th> -</tr> -</thead> -<tbody> -<tr> -<td style="text-align:left">Data Entry</td> -<td style="text-align:left">Facility level</td> -<td style="text-align:left">Desktop, laptop or tablet</td> -<td style="text-align:left">Responsible for ensuring all contacts are traced. Create contact list based on confirmed cases. Assign contacts to Tracers for follow-up. Notified of unsuccessful traces and symptomatic cases.</td> -</tr> -<tr> -<td style="text-align:left">Tracer</td> -<td style="text-align:left">Community level</td> -<td style="text-align:left">Smartphone</td> -<td style="text-align:left">Responsible for tracing and screening the contacts of the confirmed case upon notification, verifying symptomatic contacts on self-monitoring and escalating them for further investigation.</td> -</tr> -<tr> -<td style="text-align:left">COVID-19 Patient</td> -<td style="text-align:left">Community level</td> -<td style="text-align:left"></td> -<td style="text-align:left">Identified and contacted by staff to ensure all contacts are captured for follow-up.</td> -</tr> -<tr> -<td style="text-align:left">Contacts</td> -<td style="text-align:left">Community level</td> -<td style="text-align:left">Feature phone</td> -<td style="text-align:left">Receive notification alert upon registration of Tracer follow-up. Submit self-monitoring check during self-quarantine via SMS. Referred to care if symptomatic.</td> -</tr> -</tbody> -</table> -<h2 id="workflow-example">Workflow Example</h2> -<p>This demo illustrates how a CHT workflow for tracing of contacts of suspected or confirmed patient cases can be deployed to support a COVID-19 health program response. It works on both SMS and the CHT app. It can be combined with other care workflows and configured to suit specific health system needs, particularly in terms of tracer roles.</p> -<p> -<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"> -<iframe src="https://www.youtube.com/embed/I8bBeh80j-0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe> -</div> -<br></p> -<p>More background information can be found in this <a href="https://docs.google.com/presentation/d/1gG2CqndW5pWp6Lx_3t6haiqqO-wFY7_JJ4r246YbVEw">summary deck</a></p>Apps: Primary Health Care Adaptations for COVID-19https://docs.communityhealthtoolkit.org/apps/examples/phc-covid/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/phc-covid/ -<p>Primary Health Care (PHC) workflows using the CHT are easily adaptable to help communities and facilities strengthen continuity of routine primary care services during COVID-19. Adapting PHC workflows is designed to:</p> -<ul> -<li>Address disruptions to PHC delivery within communities while keeping CHWs and patients safe</li> -<li>Offer remote patient and CHW support through call and text-first protocols</li> -<li>Limit physical contact between patients, CHW’s, and facility providers by modifying existing health assessments</li> -<li>Contain transmission via embedded COVID-19 symptom screening and referral protocols</li> -</ul> -<h2 id="problem-being-addressed">Problem Being Addressed</h2> -<p>Most primary health care programs are not designed to address the specific health needs of pandemics, such as COVID-19. For CHWs and the communities they support, the impact of the pandemic has resulted in disruptions to much needed primary health care services, such as antenatal and postnatal care. This is particularly devastating for already vulnerable populations, such as patients who live in places that lack robust health care infrastructure or have co-morbidities, who face increased challenges when CHWs and care facilities are unable to provide adequate services.</p> -<p>In response, health program administrators are rapidly adapting digitally supported PHC programs to help respond to immediate patient and community health needs. Relying on CHWs and Supervisors to quickly learn and adhere to new care workflows on their own is a major challenge to successfully modifying PHC health programs to impact patients quickly and maintain the safety of health workers.</p> -<h2 id="solution-overview">Solution Overview</h2> -<p>Maintaining primary health care is critical both in slowing the spread of COVID-19 and managing the care of patients at home with moderate cases, as well as ensuring that disruptions to PHC are addressed and that routine care is supported during the pandemic. Building on the success of existing digitally-supported health programs and CHW patient relationships enables more effective responses to the specific health needs of their communities.</p> -<p>Through adaptations to PHC workflows, health program administrators are able to:</p> -<ul> -<li>Support COVID-19 prevention, detection, and containment efforts within communities</li> -<li>Create a coordinated approach that safely serves health workers, facilities, and communities</li> -<li>Support home-based care for non-emergent COVID-19 cases to reduce overburdening on hospital systems</li> -<li>Capture program learnings to create more robust and resilient health systems beyond supporting the immediate COVID-19 response</li> -</ul> -<h2 id="users-and-hierarchy-example">Users and Hierarchy Example</h2> -<table> -<thead> -<tr> -<th style="text-align:left">User</th> -<th style="text-align:left">Location</th> -<th style="text-align:left">Devices</th> -<th style="text-align:left">Role</th> -</tr> -</thead> -<tbody> -<tr> -<td style="text-align:left">Program Staff</td> -<td style="text-align:left">Central, district or local offices</td> -<td style="text-align:left">Desktop, laptop and smartphones</td> -<td style="text-align:left">Oversee delivery of routine PHC services. Continually monitor care delivery for disruptions. Ensure compliance with program-specific, national and global care protocols.</td> -</tr> -<tr> -<td style="text-align:left">CHW Supervisor</td> -<td style="text-align:left">Community level, based at facilities</td> -<td style="text-align:left">Smartphone and personal phone</td> -<td style="text-align:left">Oversee CHWs and track completion of their remote training modules. Follow up with CHWs needing extra support.</td> -</tr> -<tr> -<td style="text-align:left">CHW</td> -<td style="text-align:left">Community level</td> -<td style="text-align:left">Feature phone or smartphone</td> -<td style="text-align:left">Provide care to community using adapted PHC workflows. Complete remote training for new workflows. Enable home-based care and offer remote support through call and text-first protocols.</td> -</tr> -<tr> -<td style="text-align:left">Patient</td> -<td style="text-align:left">Community level</td> -<td style="text-align:left"></td> -<td style="text-align:left">Seek care for non-emergent conditions. Examples: maternal and child health, other infectious diseases, non-communicable diseases.</td> -</tr> -</tbody> -</table> -<h2 id="workflow-example">Workflow Example</h2> -<p>This demo illustrates how a CHT workflow that currently supports PHC can be easily adapted to reflect a COVID-19 health program response. Remote support allows for the integration of new care protocols that follow “no touch” or “visual only” reviews, transition to call/text-first protocols, and embedding COVID-19 symptom screening within PHC workflows.</p> -<p> -<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"> -<iframe src="https://www.youtube.com/embed/ic_sBf4KlYQ" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe> -</div> -<br></p> -<p>More background information can be found in this <a href="https://docs.google.com/presentation/d/1DBbEc2R6-TwgD4pp_LjTmgGXAUzZzeVrYlrDzFdWqSI">summary deck</a>.</p>Apps: COVID-19 Education and Training for CHWshttps://docs.communityhealthtoolkit.org/apps/examples/covid-education/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/covid-education/ -<p>The COVID-19 pandemic has created unique challenges to providing in-person Community Health Worker training. To support CHWs, three learning modules were created to rapidly and remotely train them on COVID-19 safety protocols and patient care. Through CHT app and SMS deployments, Supervisors can now train CHWs on care workflows without being physically present. This example covers the following learning modules:</p> -<ul> -<li>Health safety protocols for preventing the spread of COVID-19</li> -<li>Preventing the dissemination of misinformation about COVID-19</li> -<li>Recognizing COVID-19 and caring for patients with suspected infection</li> -</ul> -<h2 id="problem-being-addressed">Problem Being Addressed</h2> -<p>It is important for Community Health Workers to stay safe and serve their communities by understanding effective health protocols for preventing the spread of COVID-19. Training would traditionally be done in person, however, due to public health concerns, travel and gatherings of people are limited. Health systems have been forced to quickly adapt to deliver training content in a safely distanced, timely, and effective manner.</p> -<h2 id="solution-overview">Solution Overview</h2> -<p>The CHT provides a flexible way to deliver timely COVID-19 remote training and education to CHWs. In collaboration with <a href="https://digitalmedic.stanford.edu/">Stanford&rsquo;s Digital Medic Team</a>, and vetted by a team of health experts at Stanford, three remote learning modules were designed. CHWs can progress through training at their own pace, and receive tasks in their CHT app to encourage them to complete the modules. The training modules:</p> -<ul> -<li>Build on verifiable health safety guidance from the CDC and WHO</li> -<li>Can be deployed using the CHT app or SMS workflows</li> -<li>Capture assessment and understanding of CHW knowledge through quizzes, which can be tracked by Supervisors</li> -</ul> -<h3 id="users-and-hierarchy-example">Users and Hierarchy Example</h3> -<table> -<thead> -<tr> -<th style="text-align:left">User</th> -<th style="text-align:left">Location</th> -<th style="text-align:left">Devices</th> -<th style="text-align:left">Role</th> -</tr> -</thead> -<tbody> -<tr> -<td style="text-align:left">National Officials and County Teams</td> -<td style="text-align:left">Central and district offices</td> -<td style="text-align:left">Desktop, laptop and smartphones</td> -<td style="text-align:left">Monitor central analytics regarding onboarding and training completion. Revisit content where needed.</td> -</tr> -<tr> -<td style="text-align:left">Sub-County Teams</td> -<td style="text-align:left">Facilities or local offices</td> -<td style="text-align:left">Tablet or smartphone</td> -<td style="text-align:left">Review CHW progress on aggregate. Support CHW supervisors and monitor location-specific analytics.</td> -</tr> -<tr> -<td style="text-align:left">CHW Supervisors</td> -<td style="text-align:left">Community level, based at facilities</td> -<td style="text-align:left">Smartphones and personal phones</td> -<td style="text-align:left">Verify CHW onboarding and training completion. Follow-up with those who have not completed training. Complete their own onboarding and training tasks.</td> -</tr> -<tr> -<td style="text-align:left">CHWs and Volunteers</td> -<td style="text-align:left">Community level</td> -<td style="text-align:left">Feature phones and some smartphones</td> -<td style="text-align:left">Complete onboarding and training via SMS or App. Provide feedback and ask for support where needed.</td> -</tr> -</tbody> -</table> -<h2 id="workflow-example">Workflow Example</h2> -<p>This demo illustrates how a CHT workflow can be easily adapted to integrate a sequence of learning modules through the usage of tasks. These modules are designed to be delivered sequentially to promote engagement and absorption of content. Learning can be tracked across CHWs to inform opportunities to provide additional support. The sequence can also be modified to interact with the CHW through SMS, using question-and-answer text-based workflows. -<br /></p> -<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"> -<iframe src="https://www.youtube.com/embed/pFEFIY_SA7M" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe> -</div> -<br /> -<p>More background on the SMS workflow content and key considerations for adapting it can be found in this <a href="https://docs.google.com/document/d/1r6Aheao7Y0CW0P7d54HiEoouAVpOvoh3w-hJyXWCvFk/edit#heading=h.pqvvwz2eq9i2">summary document</a>.</p>Apps: COVID-19 Testing with Rapid Diagnostic Testshttps://docs.communityhealthtoolkit.org/apps/examples/covid-rdt-reference-app/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/covid-rdt-reference-app/ -<p>Medic has worked with <a href="https://www.finddx.org">FIND</a> to build a CHT reference application for COVID-19 point-of-care testing with Rapid Diagnostic Tests (RDT). Using the reference app as an example, CHT app developers can easily include the provisioning and capture of RDT in workflows. These workflows can include third-party applications, like <a href="https://github.com/dimagi/rd-toolkit/">Dimagi&rsquo;s RD-Toolkit</a>, that guide health workers through the use of the RDT.</p> -<p>You can find the code for this application in the <a href="https://github.com/medic/cht-core/tree/master/config/covid-19">CHT Core repository on GitHub</a>.</p> -<h2 id="problem-being-addressed">Problem Being Addressed</h2> -<p>The original call for proposals best describes why Medic created this app:</p> -<blockquote> -<p>FIND is working towards supporting countries in implementing an effective test-trace-isolate response using digital tools. To this end, FIND is looking to partner with leading digital solution providers to accelerate the development and deployment of a set of minimum functionalities for the collection of COVID RDT data and supporting incorporation of these functionalities into existing digital tools for use in low- and middle-income country settings.</p> -</blockquote> -<h2 id="solution-overview">Solution Overview</h2> -<p>This reference app provides a base layer of functionality that you can easily customize to meet the needs of your health program. Specifically, this application has examples of:</p> -<ul> -<li>Provisioning COVID-19 RDT tests</li> -<li>Capturing COVID-19 RDT results</li> -<li>Storing all RDT data, including pictures, in the CHT</li> -<li>Best practices for RDT workflows in the CHT</li> -</ul> -<p>Your instance of CHT needs to be on v3.13+ which has features developed to support this reference application, including:</p> -<ul> -<li><strong>Display Base64 Image</strong> - show ASCII encoded images inline</li> -<li><strong>Android App Launcher</strong> - integrate with third party apps like the RD-Toolkit</li> -<li><strong>Parse Timestamp to Date</strong> - convert epoch time stamps (<code>1628945040308</code>) to your desired format (<code>Sun Mar 19 13:25:08</code>).</li> -</ul> -<p>For more information on these features, see the <a href="#related-content">&ldquo;Related Content&rdquo;</a>.</p> -<p>Additional requirements for this application beyond CHT 3.13, include <a href="https://github.com/medic/cht-android">CHT Android 0.10.0</a> or later and Dimagi&rsquo;s <a href="https://github.com/dimagi/rd-toolkit/">RD-Toolkit 0.9.8</a> or later.</p> -<p>While this application calls the RD-Toolkit, the integration features in the CHT Core and CHT Android are generic. This means you could use a different RDT Android application if you prefer. Beyond the scope of RDTs, you could use this integration feature to launch any other Android app to perform an action and save the result in the CHT. To read more about this feature, see the <a href="https://docs.communityhealthtoolkit.org/apps/reference/forms/app/#android-app-launcher">Android App Launcher section in the Forms reference documentation</a>.</p> -<h2 id="workflow">Workflow</h2> -<p>There are three main components to this application:</p> -<ul> -<li>The CHT forms to provision and capture RDTs (Green)</li> -<li>Using a third-party app to use an RDT (Yellow)</li> -<li>Tasks to remind CHWs to complete an RDT that has been started (Blue)</li> -</ul> -<figure class="right col-12 col-lg-12"><a href="flow.png"> -<img src="flow.png"/> </a> -</figure> -<h2 id="training-materials">Training Materials</h2> -<p>Medic is providing images and videos for use in training CHWs on how to use the CHT with the RD-Toolkit. These could be combined with existing in <a href="https://docs.communityhealthtoolkit.org/apps/examples/covid-education/">app training</a> if needed. As well, the RD-Toolkit has in app instructions for how to use specific RDTs.</p> -<h3 id="images">Images</h3> -<p>27 high resolutions images taken from a demonstration CHW device are <a href="CHT.COVID-19.RDT.Images.zip">available for download</a>. These cover the entire usage of the reference app.</p> -<h3 id="videos">Videos</h3> -<p>Provided here are two videos of the Provision and Capture forms in the COVID-19 application.</p> -<h4 id="provision">Provision</h4> -<p>This first video shows the left side of the workflow above to provision an RDT for a patient. The CHW is shown finding Jessica Whitehouse&rsquo;s contact and choosing a new action of &ldquo;RDT Covid-19 - Provision&rdquo;. The CHW then does pre-test set up, checking Jessica&rsquo;s symptoms and location and confirming the test method and lot information. You can see the CHW launching the RD-Toolkit, reading the instructions and then, starting the session and timer. With the RD-Toolkit sending back all the information to the CHT, a task &ldquo;Capture Covid-19 RDT&rdquo; can be seen in the CHT for the CHW to follow up on when the RD-Timer has completed. The last part of the video shows the completed provision report in the CHT:</p> -<!-- -use raw HTML for now as hugo youtube short codes show 16:9 ration -box instead of mobile portrait ~9:16 ---> -<iframe id="ytplayer" type="text/html" width="342" height="610" src="https://www.youtube.com/embed/3o5d7p9O9OE" frameborder="0"></iframe> -<h4 id="capture">Capture</h4> -<p>This second video shows the right side of the workflow above to capture RDT results for a patient. The CHW is shown viewing the &ldquo;Capture Covid-19 RDT&rdquo; task after the 15 minute timer from the RD-Toolkit app has completed. After selecting the task, the CHW is brought to the CHT form which has the session information for the RD-Toolkit already loaded. The test results are recorded and then returned to the CHT from the RD-Toolkit. The last part of the video shows the completed capture report in the CHT, including the result and image of the RDT:</p> -<!-- -use raw HTML for now as hugo youtube short codes show 16:9 ration -box instead of mobile portrait ~9:16 ---> -<iframe id="ytplayer" type="text/html" width="342" height="610" src="https://www.youtube.com/embed/gpExUOJ6eQ0" frameborder="0"></iframe> -<h2 id="reports-and-dashboards">Reports and Dashboards</h2> -<p>Like all applications written for the CHT, there are built-in mechanisms to retrieve raw and aggregate data to generate reports and dashboards. Here are some ways that the data can be accessed:</p> -<ul> -<li><strong><a href="https://docs.communityhealthtoolkit.org/apps/features/targets/">In app targets</a>:</strong> Gives the CHW or their supervisor an aggregate view of any of the form fields. Since targets rely on the data on the device, if targets include data from other users then permissions must be set on the relevant forms so that the data can be replicated and synchronized accordingly.</li> -<li><strong>API Calls:</strong> Given that all form submissions are captured in JSON and that we know the data model well, you can easily do API calls to a CHT server instance and use some custom code (node, python etc) to gather and show stats on a daily basis. You can export to either JSON or CSV. See API docs <a href="https://docs.communityhealthtoolkit.org/apps/reference/api/#get-apiv2exportreports">for reports</a> as well as <a href="https://docs.communityhealthtoolkit.org/apps/reference/api/#get-apiv2monitoring">monitoring metadata</a>.</li> -<li><strong><a href="https://github.com/medic/cht-couch2pg">PostgreSQL queries</a>:</strong> CHT ships with a utility to export all the data that the API has to a relational database, Postgres. You have all the raw data the API has, but can now use the power of joins and groupings to come up with totally customizable stats by day, week, month etc. Data can be synched daily or hourly from the CHT.</li> -<li><strong>Dashboards:</strong> Medic has used both <a href="https://www.klipfolio.com/">Klipfolio</a> and, more recently, <a href="https://superset.incubator.apache.org/">Superset</a> to create more complex yet still user-friendly dashboards. This is particular useful for those who need to view the data but wouldn&rsquo;t otherwise be logging in to CHT apps. These dashboards generally access the relational data in the Postgres database as the back end.</li> -<li><strong>Third-Party Applications:</strong> Connecting to third-party applications can be done using built-in <a href="https://docs.communityhealthtoolkit.org/apps/features/integrations/">integrations</a>, or building a workflow with <a href="https://docs.communityhealthtoolkit.org/apps/features/integrations/custom/">custom integrations</a>.</li> -</ul> -<h3 id="sample-api-call">Sample API call</h3> -<p>Start with finding out the names of the forms you can get reports from. If you had deployed this application without any customizations, you would have these forms available as seen by calling the <a href="https://docs.communityhealthtoolkit.org/apps/reference/api/#get-apiv1forms">forms API</a>:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>curl <span style="color:#4e9a06">&#34;http://LOGIN:PASSWORD@HOSTNAME/api/v1/forms&#34;</span> <span style="color:#000;font-weight:bold">|</span> jq -</span></span></code></pre></div><p>The resulting JSON is formatted by <code>jq</code> for you to get an easy to read list.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;contact:clinic:create.xml&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;contact:clinic:edit.xml&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;contact:district_hospital:create.xml&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;contact:district_hospital:edit.xml&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;contact:health_center:create.xml&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;contact:health_center:edit.xml&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;contact:person:create.xml&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;contact:person:edit.xml&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;covid19_rdt_capture.xml&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;covid19_rdt_provision.xml&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">]</span> -</span></span></code></pre></div><p>The last two forms can be used to query <a href="https://docs.communityhealthtoolkit.org/apps/reference/api/#get-apiv2exportreports">the reports API </a> to get RDT provions and captures. To get all the capture reports into a file called <code>output.csv</code>, you would use this call:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>curl <span style="color:#4e9a06">&#34;http://LOGIN:PASSWORD@HOSTNAME/api/v2/export/reports?filters[search]=&amp;filters[forms][selected][0][code]=covid19_rdt_capture&#34;</span> &gt; output.csv -</span></span></code></pre></div> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -As all API calls need to be authenticated, be sure to use a login with admin permissions with this structure: <code>http://LOGIN:PASSWORD@HOSTNAME</code>. -</div> -<h3 id="base64-image-extraction">Base64 image extraction</h3> -<p>To extract the binary image from the ASCII value in a report, you can view it in the CHT or you can use a script to do so. Here&rsquo;s an example of using Bash utilities (<code>sed</code>, <code>cut</code>, <code>tr</code> and <code>base64</code>) in Linux to loop over each line of the <code>output.csv</code> file from <a href="#sample-api-call">the above call</a> to reports API. This results in numbered binary image files called <code>imageNUMBER.jpg</code> for each line in the export:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#000">i</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#0000cf;font-weight:bold">1</span> <span style="color:#000">n</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#0000cf;font-weight:bold">0</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">while</span> <span style="color:#204a87">read</span> -r line<span style="color:#000;font-weight:bold">;</span> <span style="color:#204a87;font-weight:bold">do</span> -</span></span><span style="display:flex;"><span> <span style="color:#ce5c00;font-weight:bold">((</span>n &gt;<span style="color:#ce5c00;font-weight:bold">=</span> i <span style="color:#ce5c00;font-weight:bold">))</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#204a87">echo</span> <span style="color:#000">$line</span> <span style="color:#000;font-weight:bold">|</span> cut -d<span style="color:#4e9a06">&#39;,&#39;</span> -f15 <span style="color:#000;font-weight:bold">|</span> tr -d <span style="color:#4e9a06">&#39;&#34;&#39;</span> <span style="color:#000;font-weight:bold">|</span> base64 -d &gt; image<span style="color:#000">$n</span>.jpg -</span></span><span style="display:flex;"><span> <span style="color:#ce5c00;font-weight:bold">((</span>n++<span style="color:#ce5c00;font-weight:bold">))</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">done</span> &lt; output.csv -</span></span></code></pre></div><p>This code snippet is good just to validate data ad hoc, but more likely you&rsquo;ll be doing either an extract-transform-load (ETL) process into a third-party system, or just import the raw contents and then display the base64 as an image, as done in the CHT.</p> -<p>When retrieving <a href="#capture-1">JSON</a>, this value is found in <code>capture.android-app-outputs.rdt_session_bundle.rdt_session_result_bundle.rdt_session_result_extra_images.cropped</code> field.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -The reports API always outputs in CSV, but the COVID-19 application uses <a href="https://en.wikipedia.org/wiki/Base64">Base64 encoding</a> to store the images as text as noted above. These may misbehave when opened them in a spreadsheet application like LibreOffice (“maximum number of characters per cell exceeded”) or Excel (silently clipped to 32k chars) as they’re thousands, if not hundreds of thousands, of characters long. Be sure to programmatically process these into image files as needed. -</div> -<h2 id="customizing-the-application">Customizing the application</h2> -<p>These are the files in the COVID-19 app where you&rsquo;ll want to focus your customization efforts:</p> -<pre tabindex="0"><code>├── forms -│   ├── app -│   │   ├── covid19_rdt_capture.properties.json -│   │   ├── covid19_rdt_capture.xlsx -│   │   ├── covid19_rdt_capture.xml -│   │   ├── covid19_rdt_provision.properties.json -│   │   ├── covid19_rdt_provision.xlsx -│   │   └── covid19_rdt_provision.xml -├── tasks.js -</code></pre><p>The <code>forms/app/covid19_rdt_provision</code> and <code>forms/app/covid19_rdt_capture</code> forms (<code>xlsx</code>, <code>xml</code> and <code>properties.json</code>) represent the provision and capture portions of the forms. The tasks that get created are defined in <code>tasks.js</code>. Not shown are standard contact definitions in <code>forms/contact/*</code> as well as supporting configurations for icons and other CHT application settings.</p> -<p>To read more about how these files all work together, see <a href="https://docs.communityhealthtoolkit.org/apps/reference/forms/app/">app forms</a>, <a href="https://docs.communityhealthtoolkit.org/apps/reference/forms/contact/">contact forms</a>, and <a href="https://docs.communityhealthtoolkit.org/apps/reference/tasks/">task</a> reference documentation</p> -<h2 id="example-form-submission">Example form submission</h2> -<p>While likely too verbose for humans to read, these unredacted sample JSON documents from the COVID-19 application can be used for reference to know where to find fields when querying the CHT API or Postgres.</p> -<h3 id="provision-1">Provision</h3> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;8ef8b4c4-59fc-4139-b760-64414637ed65&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_rev&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2-f64dd6048aebaf8f9bbaa38452640cf3&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;covid19_rdt_provision&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;data_record&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;content_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;xml&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;reported_date&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1631312469783</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;contact&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;d0bd9d0e-7a6e-4424-baea-f2ad13cc6e27&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;ca70460f-157d-415c-82a8-beeddaee54be&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;5821ca69-af9d-4245-8495-64092e2ed7f1&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;from&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;+13125551211&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;hidden_fields&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;patient_age_in_years&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;patient_age_in_months&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;patient_age_in_days&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;provision&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;summary&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;meta&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;fields&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;inputs&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;meta&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;location&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;lat&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;long&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;error&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;message&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;deprecatedID&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;user&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;contact_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;d0bd9d0e-7a6e-4424-baea-f2ad13cc6e27&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;facility_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;ca70460f-157d-415c-82a8-beeddaee54be&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;CHW123&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;source&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;contact&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;bf3b7049-219d-44ef-a2a4-1647b75157e1&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;patient_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;34150&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;first_name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Jessica&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;last_name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Whitehouse&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Jessica Whitehouse&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;date_of_birth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;1984-06-05&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;sex&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;female&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;phone&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;+13125551222&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;phone_alternate&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;+13125551233&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;address&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;45 Frank ST, Kent Town&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;a894c6b9-d4be-428e-a855-27cd8082be3c&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Jessica Whitehouse&#39;s Household&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;address&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;ca70460f-157d-415c-82a8-beeddaee54be&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Blue Tree Health Center&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;address&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;23 Willard ST, Kent Town&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;patient_uuid&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;bf3b7049-219d-44ef-a2a4-1647b75157e1&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;patient_name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Jessica Whitehouse&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;patient_age_in_years&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;37&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;patient_age_in_months&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;447&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;patient_age_in_days&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;13611&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;age&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;37 years old&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;preview_time_expired&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;15:41:01 on 10 Sep, 2021&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;test-reference&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;session_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;0faf0709-3c5f-405c-a692-1e0c96166782&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;test_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;69f30b96-c9cd-4a4e-acd2-72933f8b564b&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;facility_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;ca70460f-157d-415c-82a8-beeddaee54be&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;facility_name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Blue Tree Health Center&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;facility_address&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;23 Willard ST, Kent Town&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;facility_confirm_ask&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;yes&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;test_reason&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;symptomatic&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;symptoms&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;sore_throat cough loss_of_taste_or_smell&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;days_since_symptoms_began&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;4&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;spec-lot&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;specimen_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;nasopharyngeal&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_lot&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;LOT12BA&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_lot_expiry_date&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2021-09-17&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;additional_notes&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;All seals intact, all doses kept at correct temperature.&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;provision&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdtoolkit-provision&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;action&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;org.rdtoolkit.action.Provision&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;note_instructions_provision&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;android-app-inputs&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_session_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;69f30b96-c9cd-4a4e-acd2-72933f8b564b&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_config_bundle&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_config_classifier_mode&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;PRE_POPULATE&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_config_session_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;TWO_PHASE&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_config_provision_mode&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;CRITERIA_SET_AND&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_config_provision_mode_data&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;sars_cov2&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_config_flavor_one&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;bf3b7049-219d-44ef-a2a4-1647b75157e1&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_config_flavor_two&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Jessica Whitehouse&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_config_cloudworks_dns&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;android-app-outputs&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_session_bundle&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_session_state&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;RUNNING&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_session_time_started&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;1631312461680&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_session_time_resolved&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;1631313361680&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_session_time_expired&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;1631313661680&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_session_test_profile&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;premier_medical_sure_status_c19&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;preview_session_state&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;RUNNING&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;note_continue_1&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;note_continue_2&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;summary&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;syndrome_test_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;COVID-19&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_loinc_code&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;94558-4&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_loinc_long_common_name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;SARS-CoV-2 (COVID-19) Ag [Presence] in Respiratory specimen by Rapid immunoassay&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;summary_header&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;summary_details&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;summary_name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;summary_info&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;summary_test_name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;summary_test_expire&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;meta&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;instanceID&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;uuid:e21e1079-462b-4f56-9a33-c1e9e22a6643&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;geolocation_log&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;timestamp&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1631312469904</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;recording&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;latitude&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">5.9949813</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;longitude&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">-5.0980983</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;altitude&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">706.7633651675733</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;accuracy&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">16.44700050354004</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;altitudeAccuracy&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">null</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;heading&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">null</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;speed&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">null</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;geolocation&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;latitude&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">5.9949813</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;longitude&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">-5.0980983</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;altitude&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">706.7633651675733</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;accuracy&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">16.44700050354004</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;altitudeAccuracy&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">null</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;heading&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">null</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;speed&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">null</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_attachments&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;content&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;content_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;application/xml&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;revpos&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;digest&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;md5-VkGDlztsrWtyqNI6X4U9Yg==&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;length&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">5612</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;stub&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="capture-1">Capture</h3> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -The <code>rdt_session_result_extra_images.cropped</code> field is truncated as it normally exceeds <a href="#base64-image-extraction">10,000 characters</a>. -</div> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;a5f92d70-a3c0-4684-8ee7-fe435b49c974&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_rev&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;1-a6cac66d104d779d91e7cfe16a2ee970&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;covid19_rdt_capture&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;data_record&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;content_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;xml&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;reported_date&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1631313478618</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;contact&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;d0bd9d0e-7a6e-4424-baea-f2ad13cc6e27&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;ca70460f-157d-415c-82a8-beeddaee54be&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;5821ca69-af9d-4245-8495-64092e2ed7f1&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;from&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;+13125551211&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;hidden_fields&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;inputs&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;patient_age_in_years&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;patient_age_in_months&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;patient_age_in_days&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;test-information&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;capture&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;summary.summary_header&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;summary.summary_details&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;summary.summary_name&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;summary.summary_info&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;summary.summary_rdt_loinc_long_common_name&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;summary.summary_results&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;meta&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;fields&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;inputs&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;source&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;task&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;contact&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;bf3b7049-219d-44ef-a2a4-1647b75157e1&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;patient_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;34150&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;first_name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Jessica&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;last_name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Whitehouse&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Jessica Whitehouse&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;date_of_birth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;1984-06-05&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;sex&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;female&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;phone&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;+13125551222&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;phone_alternate&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;+13125551233&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;address&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;45 Frank ST, Kent Town&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;a894c6b9-d4be-428e-a855-27cd8082be3c&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Jessica Whitehouse&#39;s Household&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;address&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;ca70460f-157d-415c-82a8-beeddaee54be&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Blue Tree Health Center&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;address&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;23 Willard ST, Kent Town&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;patient_uuid&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;bf3b7049-219d-44ef-a2a4-1647b75157e1&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;patient_name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Jessica Whitehouse&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;patient_age_in_years&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;37&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;patient_age_in_months&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;447&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;patient_age_in_days&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;13611&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;age&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;37 years old&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;test-information&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;session_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;0faf0709-3c5f-405c-a692-1e0c96166782&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;administrator_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;d0bd9d0e-7a6e-4424-baea-f2ad13cc6e27&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;administrator_name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;mrjones&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;test_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;69f30b96-c9cd-4a4e-acd2-72933f8b564b&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;facility_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;ca70460f-157d-415c-82a8-beeddaee54be&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;facility_name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Blue Tree Health Center&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;facility_address&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;23 Willard ST, Kent Town&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;facility_test_setting&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;other_test_setting&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;test_reason&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;symptomatic&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;symptoms&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;sore_throat cough loss_of_taste_or_smell&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;days_since_symptoms_began&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;4&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;specimen_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;nasopharyngeal&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;specimen_type_other&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_lot&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;LOT12BA&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_lot_expiry_date&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2021-09-17&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;additional_notes&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;All seals intact, all doses kept at correct temperature.&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;capture&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdtoolkit-capture&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;action&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;org.rdtoolkit.action.Capture&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;note_instructions_provision&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;android-app-inputs&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_session_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;69f30b96-c9cd-4a4e-acd2-72933f8b564b&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;android-app-outputs&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_session_bundle&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_session_state&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;QUEUED&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_session_time_started&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;1631312461680&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_session_time_resolved&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;1631313361680&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_session_time_expired&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;1631313661680&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_session_test_profile&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;premier_medical_sure_status_c19&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_session_result_bundle&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_session_result_time_read&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;1631313440887&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_session_result_extra_images&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;cropped&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;/9j/4AAQSkZJRgABAQAAAQABAAD/4gIoSUNDX1BST0ZJTEUAAQEAAAIYAAAAAAIQAABtbnRyUkdCIFhZWiAAAAAAAAAAAAAAAABhY3NwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAA9tYAAQAAAADTLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlkZXNjAAAA8AAAAHRyWFlaAAABZAAAABRnWFlaAAABeAAAABRiWFlaAAABjAAAABRyVFJDAAABoAAAAChnVFJDAAABoAAAAChiVFJDAAABoAAAACh3dHB0AAAByAAAABRjcHJ0AAAB3AAAADxtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAFgAAAAcAHMAUgBHAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAABvogAAOPUAAAOQWFlaIAAAAAAAAGKZAAC3hQAAGNpYWVogAAAAAAAAJKAAAA+EAAC2z3BhcmEAAAAAAAQAAAACZmYAAPKnAAANWQAAE9+-----SNIP-----+SkUbNztxB28pg/3WxqNihXn/ZSlB//Z&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rdt_session_result_map&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;sars_cov2&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;sars_cov2_pos&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;preview_session_state&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;COMPLETED&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;note_continue_1&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;note_continue_2&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;repeat-test&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;repeat_test&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;no&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;summary&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;time_started&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;10 Sep, 2021 at 15:21:01&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;time_read&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;10 Sep, 2021 at 15:37:20&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;time_expired&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;10 Sep, 2021 at 15:41:01&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;results_expired&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;FALSE&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;result&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Positive&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;summary_header&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;summary_details&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;summary_name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;summary_info&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;summary_rdt_loinc_long_common_name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;summary_results&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;summary_image&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;meta&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;instanceID&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;uuid:3225ed73-209f-4ddf-864a-ee0e4d2a51e3&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;deprecatedID&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;geolocation_log&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;timestamp&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1631313478752</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;recording&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;latitude&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">5.9950304</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;longitude&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">-5.0981426</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;altitude&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">706.6073668124792</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;accuracy&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">11.522000312805176</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;altitudeAccuracy&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">null</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;heading&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">null</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;speed&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">null</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;geolocation&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;latitude&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">5.9950304</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;longitude&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">-5.0981426</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;altitude&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">706.6073668124792</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;accuracy&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">11.522000312805176</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;altitudeAccuracy&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">null</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;heading&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">null</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;speed&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">null</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_attachments&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;user-file/covid19_rdt_capture/summary/summary_image&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;content_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;image/png&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;revpos&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;digest&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;md5-H537gv0l2mOyltmyzBRaZA==&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;length&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">211302</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;stub&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;content&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;content_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;application/xml&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;revpos&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;digest&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;md5-cwJDPyvLshux7nfuJxjnzQ==&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;length&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">287073</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;stub&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div>Apps: Direct-to-client, two-way texting workflows on CHThttps://docs.communityhealthtoolkit.org/apps/examples/direct-to-client/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/direct-to-client/ -<div class="pageinfo pageinfo-primary"> -<p>This documentation provides a guide for designing and deploying direct-to-client (D2C), two-way texting (2wT) workflows to support client follow-up care using the community health toolkit (CHT). 2wT is a mobile text messaging system built on open-source, CHT tools to engage clients in their health care; to facilitate prompt client - healthcare provider interactions; to provide low-cost telehealth; and, to improve health care outcomes through the early identification of, and referral for, potential complications.</p> -<p>The hybrid 2wT system is used for relaying automated health education and check in messages to clients, generating tasks to improve client follow-up and health provider reporting, while also allowing for free text interaction between clients and healthcare providers. This document describes the hybrid 2wT system features and its functionalities. It also provides an opportunity for digital health implementers to explore and learn how direct-to-client communication can be achieved through integrating app features within the CHT, with messaging technologies such as <a href="https://docs.communityhealthtoolkit.org/apps/features/integrations/rapidpro/">RapidPro</a> and SMS aggregators.</p> -</div> -<h2 id="problem-being-addressed">Problem being addressed</h2> -<p>Recipients of health care in sub-Saharan Africa face significant barriers in accessing quality services. These barriers include geographical distance, limited availability of skilled health care workers, and both direct and indirect costs - all of which can present major challenges in clients getting timely access to health care. It is important to bring healthcare services closer to client’s households, especially those who live in remote areas. The D2C / 2wT workflows assist in addressing the health needs of such clients through connecting clients with health care providers based at the facility or community, including both clinicians or lower-level healthcare workers. Unlike phone calls which must be answered, D2C / 2wT offers convenient care – allowing both parties to communicate when needed or wanted. Unlike interventions that require a smartphone or downloading an app, 2wT clients communicate directly via the SMS capability in even basic mobile phones. D2C / 2wT can help improve healthcare access, encourage client engagement in care, promote healthy behaviors and support delivery of client centered care, including in harder to reach rural or remote areas.</p> -<h2 id="solution-overview">Solution overview</h2> -<p>The CHT offers general guidance on how SMS workflows can be built on CHT. CHT SMS based workflows can be configured to support D2C / 2wT between health care providers and clients. The D2C / 2wT workflows can be designed to support:</p> -<ul> -<li><strong>Regular client check-ins</strong> to help keep track of the client's condition</li> -<li>Sending of <strong>client follow up appointment reminders</strong> with confirmation requests</li> -<li>Sharing of <strong>information and educational health messages</strong> with the clients</li> -<li><strong>Client tracing and client review tasks</strong> can be assigned to specific healthcare workers. Escalated workflows can result in tasks in CHT apps for CHWs, clerks or nurses, depending on need</li> -<li><strong>Two-way conversation messaging</strong> between healthcare providers and clients</li> -<li><strong>2wT-based telehealth</strong>, providing a lower cost format for clinical triaging, reassurance or referral to in-person care via SMS</li> -</ul> -<p>Furthermore, CHT allows integration with other health information systems including District Health Information Software 2 (DHIS2) and facility-based electronic medical record systems such as OpenMRS.</p> -<h2 id="cht-sms-workflow-technical-overview">CHT SMS workflow technical overview</h2> -<p>The SMS workflows in CHT can integrate with <a href="https://docs.communityhealthtoolkit.org/apps/features/integrations/rapidpro/">RapidPro</a> flows to support two way interactive messaging between healthcare providers and household members. RapidPro is an open source tool that has the capability to support conversational messaging flows via SMS, Interactive Voice Response (IVR), Telegram, Facebook Messenger and WhatsApp. With the CHT-RapidPro integration, it is possible to design and configure SMS workflows in the two systems. Data is shared between CHT and Rapidpro via the APIs; and the information from the interactive texting is used to update clients details on CHT through CouchDB which can trigger tasks on CHT.</p> -<p>CHT-RapidPro SMS workflows allows for automated visit scheduling, replying and routing to an SMS based gateway based on preconfigured SMS logic, thus eliminating the need for a healthcare worker (clerk or clinician, depending on the intervention specifics) to send, monitor and reply to each text. To use SMS workflows with CHT, you will need a texting channel (a service that enables you to send or receive messages or a phone call). CHT can be integrated with a texting channel like an SMS aggregator which provides a reliable cloud based platform to send and receive an unlimited number of SMSs to and from clients or household members.</p> -<h3 id="direct-to-client-two-way-texting-technical-architecture">Direct-to-client, two-way texting, technical architecture</h3> -<p>The CHT-RapidPro integration enables App builders to build complex conversational SMS workflows that support client care and support coordination of care at the facility and community levels.</p> -<p>Clients are enrolled directly using the CHT D2C, 2wT app using a phone, tablet or computer. Clients need only simple phones and do not need to download an app. The resulting client data is then stored and managed using CouchDB and a PostgreSQL database system. Through the CHT application programming interface (API), client messages are sent to Rapidpro, and RapidPro pushes summary data back to the CHT to update client responses and status. On the client interface these can be automated messages or interactive free text with healthcare providers. An SMS text message aggregator service and a short code channel linked to RapidPro streamlines the incoming and outgoing SMS text messages; while the CHT generates the appropriate tasks (help clinicians triage, refer, track, and trace clients)and reports (keep track of completed actions and monitor clients) as represented in figure 1.</p> -<figure class="left col-10"><a href="Figure1SystemArchitecture.jpg"> -<img src="Figure1SystemArchitecture.jpg" -alt="Direct Messaging technical architecture"/> </a><figcaption> -<p><b>Figure 1:</b> System architecture of direct-to-client (D2C), two-way texting (2wT) for voluntary medical male circumcision (VMMC) postoperative care. API: application programming interface; CHT: Community Health Toolkit.</p> -</figcaption> -</figure> -<p>The CHT core framework has inbuilt <a href="https://docs.communityhealthtoolkit.org/apps/guides/privacy/">privacy/security features</a> including two- factor authentication, role based authorization, data security, and a range of protected data access endpoints. The system can be configured to manage <a href="https://docs.communityhealthtoolkit.org/apps/concepts/users/">user roles and permissions</a> for access of CHT applications. Responsible data practices is enforced through the <a href="https://docs.communityhealthtoolkit.org/apps/guides/privacy/policy/">Privacy and Data Protection Policy</a>.</p> -<p>The D2C, 2wT app incorporates the CHT security features in order to protect client data and ensure the confidentiality and privacy of client information. Users are encouraged to use password protected user accounts, the app also has full disk encryption and auto-lock screens for users’ devices (android phones and tablets) in order to minimize the risk of unwarranted access. Role based access controls also make sure that users can only view and edit information that the permissions assigned to the user role allow them to.</p> -<h2 id="case-adaptation-voluntary-medical-male-circumcision-app-co-designed-by-i-tech-and-medic">Case adaptation: Voluntary medical male circumcision app co-designed by I-TECH and Medic</h2> -<p>The University of Washington, Department of Global Health’s International Training and Education Center for Health (<a href="https://www.go2itech.org/">I-TECH</a>), Medic, and local partners including Zim-TTECH, Lighthouse Trust, and Aurum Institute, collaboratively designed, developed, and scaled a CHT-based voluntary medical male circumcision (VMMC) app for post-operative follow-up with support from the National Institute of Health, USA The D2C, 2wT app supports automated, interactive telehealth via direct-to-client messaging between VMMC nurses and clients. 2wT telehealth helps improve the quality and efficiency of care, maintaining client safety while lowering follow-up care costs. In the spirit of contributing to the growth of the public global goods, I-TECH and Medic have coordinated to release the application <a href="https://github.com/medic/cht-post-ops-app">source code of the D2C, 2wT app</a>.</p> -<h3 id="users-and-hierarchy">Users and hierarchy</h3> -<table> -<thead> -<tr> -<th>Users</th> -<th>Location</th> -<th>Devices</th> -<th>Roles</th> -</tr> -</thead> -<tbody> -<tr> -<td>Program managers / M&amp;E</td> -<td>Admin level</td> -<td>Laptop</td> -<td>Users at this level have access to all sites. They can access many sites via the CHT D2C messaging App. They can download information from the dashboard.</td> -</tr> -<tr> -<td>Hub / Super nurse</td> -<td>Admin level</td> -<td>Laptop / Desktop</td> -<td>Users have access to multiple sites within the district and can access them via the CHT D2C messaging App. They are online users and are able to triage, refer clients to health facilities to be reviewed by site nurses.</td> -</tr> -<tr> -<td>Site nurse</td> -<td>Health facility or outreach site</td> -<td>Smartphone</td> -<td>Users are routine MC nurses at health facilities or outreach sites who enroll D2C clients and interact with clients enrolled at their site via the CHT D2C messaging app. In higher volume facilities, data clerks may also be involved in data entry of client enrolment details.</td> -</tr> -<tr> -<td>VMMC Clients</td> -<td>Community / household level</td> -<td>Smartphone / feature phone</td> -<td>VMMC clients with access to mobile devices and who have consented to participate in and have been enrolled into the D2C, 2wT app. They will receive daily check in messages and can communicate with their health care providers via text.</td> -</tr> -</tbody> -</table> -<p><b>Figure 2:</b> User types and hierarchy</p> -<h3 id="workflows">Workflows</h3> -<p>Current workflow for VMMC client enrollment, screening and VMMC procedures (without messaging) are as described in figure 3 below.</p> -<figure class="col-9"><a href="Figure3Workflows.png"> -<img src="Figure3Workflows.png" -alt="Users and hierarchy example"/> </a><figcaption> -<p><b>Figure 3:</b> Voluntary male circumcision workflow before direct-to-client messaging was introduced</p> -</figcaption> -</figure> -<h4 id="1-automated-check-in-workflow-to-support-client-care">1. Automated check-in workflow to support client care</h4> -<p>Eligible male clients with access to mobile phones are registered on the VMMC app to enable them to receive daily check-in texts to help track the condition of the clients for 13 days after the circumcision. During registration (Day 0), details about the method of circumcision; whether the client is an adult or a minor, and the preferred phone numbers to be used for communication are recorded. Each client receives an enrollment confirmation to ensure that the communication channel is open. Clients also receive postoperative counseling using the 2wT flip-book on how to respond to the daily SMS text message and review photos that illustrate the signs of complications for reporting via daily SMS text messages. VMMC clients, including those with 2wT, have the option to attend routine pre-scheduled visits on day 2 and day 7 post-op.</p> -<p>After the registration, the VMMC clients registered on the VMMC app receive preconfigured, bidirectional daily follow up texts to check on their healing. These are in the form of automated daily SMS text messages on days 1 to 13 in the predominant languages of the area. The clients respond to the daily SMS text message with a single numeric (0=no problem or 1=I need help) response. They could respond to any daily SMS text message at any time. They could also initiate an unrestricted, freely-worded text response at any time, and in any language - instead of or in addition to the daily numeric response.</p> -<p>An adverse event (AE) is any complication or problem that may happen during or after a surgery, including a VMMC surgery. AEs may be mild, moderate or severe. All moderate and severe AEs should be reviewed by a clinician. Moderate and severe AEs after VMMC are rare, but occur about 2% of the time. . Identifying AEs swiftly and managing them properly is a sign of a quality surgical program, including VMMC programs.</p> -<figure class="col-9"><a href="Figure4PatientAutomated.png"> -<img src="Figure4PatientAutomated.png" -alt="Patient automated check-in SMS workflows"/> </a><figcaption> -<p><b>Figure 4:</b> Direct to client messaging for voluntary male circumcision clients without adverse effects</p> -</figcaption> -</figure> -<h4 id="2-triaging-to-care-workflow-for-clients-with-potential-complications">2. Triaging to care workflow for clients with potential complications</h4> -<p>Clients with potential AEs (those who respond with 1 or a free text indicating concerns) are followed up by nurses and given care via the D2C, 2wT intervention. Clients may be followed up via SMS / call. Nurses provide education and reassurance, or refer in cases where they need to go to a health facility for in-person review if an emergency was suspected (Figure 5). The clients could request a call back to speak with the nurse during routine clinic hours; the nurse could also initiate calls to follow up with clients when desired.</p> -<figure class="col-9"><a href="Figure5Triage.png"> -<img src="Figure5Triage.png" -alt="Figure 5: Direct-to-client messaging for voluntary male circumcision clients without adverse effects"/> </a><figcaption> -<p><b>Figure 5:</b> Direct-to-client messaging for voluntary male circumcision clients without adverse effects</p> -</figcaption> -</figure> -<h4 id="3-escalated-workflow-to-generate-tasks-when-clients-do-not-respond">3. Escalated workflow to generate tasks when clients do not respond</h4> -<p>If the client has not responded to any of the daily check in-messages by day 3, they get a reminder message to check on them and prompt a response. A tracing follow up task is triggered for the 2WT nurse if a VMMC client does not respond to any of the daily check in texts by day 4 for both minors and adults. The 2WT nurse then records the tracing method and outcomes by completing the trace client task.</p> -<p>A VMMC client who responds with a potential complication triggers a Client review: Potential complication task for the 2WT nurse. They then report and fill in whether the client was reviewed by a clinician. If the client has been reviewed by a clinician, they record the details of the review, time, place and adverse events identified. If the client has not been reviewed by a clinician, the 2WT nurse will try and trace them by SMS, phone call or a home visit; and report the tracing outcomes as shown in figure 6 below.</p> -<figure class="col-9"><a href="Figure6Escalated.png"> -<img src="Figure6Escalated.png" -alt="Figure 6: Direct-to-client messaging for voluntary male circumcision clients without adverse effects"/> </a><figcaption> -<p><b>Figure 6:</b> Direct-to-client messaging for voluntary male circumcision clients without adverse effects</p> -</figcaption> -</figure> -<h4 id="4-clients-can-share-requests-by-messaging-health-care-providers">4. Clients can share requests by messaging health care providers.</h4> -<p>Clients registered in the intervention can also initiate the bidirectional messaging with a 2WT Nurse by messaging a central phone number or a short code. Clients can report any concern, ask a question about wound healing, or request for help from health care providers via SMS. For this workflow, the logic can be preconfigured to support health triage and clinical referrals. Using the messaging functionality on CHT, health care providers based at the facility can view, manage and respond to incoming texts from VMMC clients.</p> -<h2 id="evidence-of-impact">Evidence of Impact</h2> -<p>In collaboration with researchers at the University of Washington, we assessed the effectiveness of the 2wT intervention through randomized controlled trials in Zimbabwe and South Africa. The foundational <a href="https://trialsjournal.biomedcentral.com/articles/10.1186/s13063-019-3470-9">randomized control trial in Zimbabwe in 2018</a>, demonstrated that post-surgical client follow up via SMS was as safe as follow up care at the clinic, and that the transition to SMS-based follow-up option enabled an 85% reduction in unnecessary clinic visits. Similar findings were reported in <a href="https://www.jmir.org/2023/1/e42111/">South Africa</a>. This approach to messaging with clients was more efficient and <a href="https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0239915">cheaper</a> than standard care, thereby lowering costs. Both clients and clinicians both found the approach to be <a href="https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0233234">highly usable</a> and <a href="https://journals.sagepub.com/doi/full/10.1177/20552076231194924">acceptable for post-operative care</a> and further <a href="https://formative.jmir.org/2023/1/e44122">usability studies</a> found that it was preferred by clients and providers alike (see figure 7). We are also expanding our application of D2C, 2wT for antiretroviral therapy (ART) clients in Malawi, aiming to have similarly <a href="https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0278806">positive impact on engagement in ART and retention in HIV care</a>.</p> -<figure class="col-9"><a href="Figure7EvidenceOfImpact.png"> -<img src="Figure7EvidenceOfImpact.png" -alt="Figure 7: Evidence supported benefits of direct-to-client messaging for voluntary male circumcision clients"/> </a><figcaption> -<p><b>Figure 7:</b> Evidence supported benefits of direct-to-client messaging for voluntary male circumcision clients</p> -</figcaption> -</figure> -<h2 id="more-scenarios-where-direct-to-client-messaging-can-be-used-to-support-client-care">More scenarios where direct-to-client messaging can be used to support client care</h2> -<h3 id="1-facility-appointment-reminders-workflow">1. Facility appointment reminders workflow</h3> -<p>Appointment reminders can be configured on CHT so that household members and clients can receive facility appointment reminders. Facility appointment reminders are configured as per the recommended health guidelines for specific use cases. The clinic visit appointment reminders can be configured to automatically stop once a client completes the expected clinic visits.</p> -<h3 id="2-active-case-finding-by-messaging-household-members-with-survey-questions">2. Active case finding by messaging household members with survey questions</h3> -<p>Active case finding by messaging households with survey questions about the health of family members.</p> -<h2 id="frequently-asked-questions">Frequently Asked Questions</h2> -<h5 id="1-is-the-client-charged-for-receiving-or-sending-the-sms">1. Is the client charged for receiving or sending the SMS?</h5> -<p>It is possible for health programs to acquire a zero rated short code service which is free for clients and household members to send and receive texts from the short code.</p> -<h5 id="2-what-are-ways-to-handle-exceptions">2. What are ways to handle exceptions?</h5> -<p>Exceptions can occur when programs and flows do not work as expected. This may occur due to various technical issues and may be unavoidable. One possible way to handle these unwanted issues and errors is to set up the mail group such that a notification email is sent whenever there is any exception. Another method is to keep track of whether the flow was completed i.e., all the responses from start to finish nodes were received. For the participants whose flow may have been interrupted or incomplete, one may run the workflow again.</p> -<h5 id="3-ways-to-improve-response-rate-from-clients">3. Ways to improve response rate from clients?</h5> -<p>The workflows in RapidPro can be set up in such a way that they expire after a certain time period. Workflows can expire when there is no response from a client within a time period. You can handle such situations by repeating the workflow i.e., sending them again to the client after a certain time interval. Alternatively, it can be beneficial to do some research on which time to send the message. For example, a person may be busy at work and/or away from the phone during working hours. It may be easier for them to respond during early morning or later in the evening than in the afternoon.</p> -<h2 id="resources">Resources</h2> -<p>VMMC code can be accessed using the following link <a href="https://github.com/medic/cht-post-ops-app">D2C messaging app for post-op care</a></p> -<h5 id="training-materials">Training materials</h5> -<ul> -<li><a href="https://drive.google.com/file/d/13fz8vDYSFNHLc2a-afsAEjM2nrmca6os/view?ts=65e996b1">User guide including tools for the nurses and clients</a></li> -<li>Illustrative <a href="https://www.youtube.com/watch?v=HNC5T7QuK2M&amp;list=PLutu6_ZOg77dAgJhDCRKdEIUnThUsqjEh">Videos</a></li> -</ul> -<h5 id="publications">Publications</h5> -<ul> -<li>2wT is safe and improves efficiency over routine visits: Evidence from <a href="https://www.jmir.org/2023/1/e42111/">South Africa</a> and from <a href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC6903365/">Zimbabwe</a></li> -<li>2wT is highly usable among clients: Evidence from <a href="https://journals.sagepub.com/doi/full/10.1177/20552076231194924">South Africa</a> and from <a href="https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0233234">Zimbabwe</a></li> -<li>2wT saves program costs: Evidence from <a href="https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0294449">South Africa</a> and from <a href="https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0239915">Zimbabwe</a></li> -<li>2wT is usable for healthcare workers: Evidence from <a href="https://formative.jmir.org/2023/1/e44122">South Africa</a></li> -<li>2wT is scale up, reaching over 45,000 in <a href="https://journals.plos.org/digitalhealth/article?id=10.1371/journal.pdig.0000066">Zimbabwe</a></li> -</ul> -<p><strong>Related pages</strong> - <a href="https://sites.uw.edu/twowaytexting/">UW website</a>, <a href="https://globalhealth.washington.edu/news/2020/07/02/expanding-two-way-texting-reduce-follow-appointments-male-circumcision-patients">I-tech/Aurum page</a></p> -<figure class="col-9"><a href="Figure8Collab.png"> -<img src="Figure8Collab.png"/> </a> -</figure> -<p><strong>Funding:</strong> 2wT Zimbabwe was supported by the Fogarty International Center of the National Institutes of Health under Award Number R21TW010583, PI Feldacker. 2wT South Africa is supported by National Institute of Nursing Research of the National Institutes of Health (NIH) under award number 5R01NR019229, PIs Feldacker and Setswe. The content is solely the responsibility of the collaborators and authors and does not necessarily represent the official views of the National Institutes of Health.</p>Apps: Event Based Surveillancehttps://docs.communityhealthtoolkit.org/apps/examples/ebs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/ebs/ -<p>The CHT’s Event Based Surveillance (EBS) functionality enables rapid capture of information about community events that are a potential risk to public health. Deployed at the community level, this functionality is designed to:</p> -<ul> -<li>Achieve earliest possible detection of COVID-19 cases in communities</li> -<li>Provide visibility into signals, reports, and investigations centrally for follow-up</li> -<li>Coordinate and support local action based on existing roles in disease surveillance</li> -</ul> -<h2 id="problem-being-addressed">Problem Being Addressed</h2> -<p>Patient reporting through traditional primary health care channels is not well suited for detecting rapidly spreading outbreaks at a community scale. Timely detection of outbreaks and important public health events can be delayed or missed when relying on individual patient data and traditional health management information systems.</p> -<h2 id="solution-overview">Solution Overview</h2> -<p>Event Based Surveillance with the CHT offers opportunities for rapid deployment and measurable community impact for reportable diseases, including COVID-19. Through more timely event data reporting and analysis, the CHT provides early indicators of emerging issues to community health workers across the health system. The CHT solution offers:</p> -<ul> -<li>Tiered reporting, verification, and escalation process, which can improve speed and quality of data</li> -<li>Signal data that can be mapped for data-driven decision-making</li> -<li>Pre-populated with Supervisor and CHW contact information</li> -<li>Health program continuity with the inclusion of pre-existing care workflows</li> -<li>System scalability through training CHWs remotely on their role and responsibilities</li> -</ul> -<h2 id="users-and-hierarchy-example">Users and Hierarchy Example</h2> -<table> -<thead> -<tr> -<th style="text-align:left">User</th> -<th style="text-align:left">Location</th> -<th style="text-align:left">Devices</th> -<th style="text-align:left">Role</th> -</tr> -</thead> -<tbody> -<tr> -<td style="text-align:left">National and District Officials</td> -<td style="text-align:left">Central and district offices</td> -<td style="text-align:left">Desktop, laptop and smartphones</td> -<td style="text-align:left">Monitor overall system and analytics regarding verified, investigated and missing cases.</td> -</tr> -<tr> -<td style="text-align:left">Sub-District Investigators</td> -<td style="text-align:left">Facilities or local offices</td> -<td style="text-align:left">Tablet or smartphone</td> -<td style="text-align:left">Investigate verified reports within 24 hours of signal report; add data to signal case.</td> -</tr> -<tr> -<td style="text-align:left">CHW Supervisors</td> -<td style="text-align:left">Community level, based at facilities</td> -<td style="text-align:left">Smartphones and personal phones</td> -<td style="text-align:left">Verify COVID-19 signals. Provide feedback and guidance to CHWs.</td> -</tr> -<tr> -<td style="text-align:left">CHWs and Volunteers</td> -<td style="text-align:left">Community level</td> -<td style="text-align:left">Feature phones and some smartphones</td> -<td style="text-align:left">Detect and report signals. Provide guidance and health education to patients and families.</td> -</tr> -</tbody> -</table> -<h2 id="workflow-example">Workflow Example</h2> -<p>This demo illustrates a workflow that enables CHWs to use SMS to report signal codes for specific community health threats. The CHW Supervisor can then verify the initial report and, if warranted, escalate to the sub-county for investigation and follow-up. It is designed to be flexible and can be combined with other complimentary care workflows.</p> -<p> -<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"> -<iframe src="https://www.youtube.com/embed/5uIEs4DQLRA" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe> -</div> -<br><br></p> -<p>More background information can be found in this <a href="https://docs.google.com/presentation/d/1BvYA0c8vHlXdGiL1Ne9cbWM4rGaarUGz9SfckP3NWG4">summary deck</a>.</p>Apps: Learning and Carehttps://docs.communityhealthtoolkit.org/apps/examples/learning-care/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/learning-care/ -<p>The Learning &amp; Care Apps in the Community Health Toolkit are designed to onboard community health workers remotely to new digital training tools, and help them learn new information and care delivery responsibilities through customized educational modules.</p> -<p>The modules can be deployed both (i) via the Android integration with the Academy App (powered by OppiaMobile) described in this documentation, and (ii) within the CHT core alone, to provide a seamless online &amp; offline experience for CHWs, supervisors, and government partners.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -It is important to consider the potential use cases for which a CHT core &lt;&gt; OppiaMobile integration is intended, as well as the digital landscape in which it is being designed and deployed. The CHT core &lt;&gt; OppiaMobile integration may be best suited for settings where both apps are already in use, given the inherent complexities in onboarding users remotely to two apps. -</div> -<h2 id="problem-being-addressed">Problem Being Addressed</h2> -<p>The Community Health Academy at Last Mile Health, with a range of content partners including Digital Medic, is building a library of open-access learning materials for community health workers.</p> -<p>With the spread of COVID-19, it is imperative that access to learning, integrated with care protocols, routine task and care workflows, is available to CHWs without relying on face-to-face training.</p> -<h2 id="solution-overview">Solution Overview</h2> -<p>The Community Health Academy and Medic set out to deliver a fully-integrated Learning and Care solution, with targeted COVID-19 learning experiences as a global public good.</p> -<p>This integrated Learning &amp; Care App leverages the <strong><a href="https://docs.communityhealthtoolkit.org/apps/examples/training/">remote onboarding</a>, <a href="https://docs.communityhealthtoolkit.org/apps/features/tasks/">task &amp; scheduling</a>, and <a href="https://docs.communityhealthtoolkit.org/apps/features/targets/">target</a> features</strong> of the CHT core framework with the <strong>curated, multimedia educational content</strong> from the Academy’s COVID-19 library.</p> -<h2 id="users-and-hierarchy-example">Users and Hierarchy Example</h2> -<table> -<thead> -<tr> -<th style="text-align:left">User</th> -<th style="text-align:left">Location</th> -<th style="text-align:left">Devices</th> -<th style="text-align:left">Role</th> -</tr> -</thead> -<tbody> -<tr> -<td style="text-align:left">CHW</td> -<td style="text-align:left">Community level</td> -<td style="text-align:left">Feature phone or smartphone</td> -<td style="text-align:left">Receive notifications about new courses, complete modules and educational assessments; provide feedback and ask for support where needed; discuss content and quizzes with supervisors; support fellow CHWs in onboarding &amp; modules.</td> -</tr> -<tr> -<td style="text-align:left">CHW Supervisors</td> -<td style="text-align:left">Community level, based at facilities</td> -<td style="text-align:left">Smartphones and personal phones</td> -<td style="text-align:left">Monitor progress &amp; ensure CHWs complete modules and quizzes; liaise with local government partners to be aware of policy updates, and to share CHW training progress &amp; implementation feedback; view customize dashboards and provide support to CHW users.</td> -</tr> -<tr> -<td style="text-align:left">Government &amp; Local Implementing Partners</td> -<td style="text-align:left">Central and district offices</td> -<td style="text-align:left">Desktop, laptop and smartphones</td> -<td style="text-align:left">Monitor progress of catchment area CHWs and supervisors through analytics &amp; dashboards, provide up-to-date information on country-wide guidelines &amp; global best practices (e.g., from WHO).</td> -</tr> -</tbody> -</table> -<h2 id="workflow-examples">Workflow Examples</h2> -<h3 id="covid-19-learning-and-care-supported-by-the-cht">COVID-19 Learning and Care Supported by the CHT</h3> -<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"> -<iframe src="https://www.youtube.com/embed/d_2yixLic3Y" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe> -</div> -<h3 id="integrated-learning-and-care-apps-covid-19-supported-by-the-cht-and-oppiamobile">Integrated Learning and Care Apps COVID-19 Supported by the CHT and OppiaMobile</h3> -<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"> -<iframe src="https://www.youtube.com/embed/WOE5QlPye0k" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe> -</div> -<br clear="all"> -<p>More background information can be found in this <a href="https://docs.google.com/presentation/d/1fsf0bpF6jdngwwj1VUqpR2QxONS7fEP88GQMWHr10Jc/edit#slide=id.g5fbe124551_2_36">summary deck</a></p>Apps: Loss to Follow Uphttps://docs.communityhealthtoolkit.org/apps/examples/interoperability/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/interoperability/ -<h2 id="loss-to-follow-up-workflow-ltfu">Loss to Follow Up Workflow (LTFU)</h2> -<p>This workflow describes a use case where a health facility or a requesting system generates a list of patients who have missed follow-up appointments that were made through the CHT. A CHW would then follow up with the listed patients through SMS, a physical visit or a phone call.</p> -<h2 id="problem-being-addressed">Problem Being Addressed</h2> -<p>Data exchange between the CHT and other systems has primarily been at peer-to-peer level. This means that the integration is built to meet the specific need in the unique scenarios. This presents a problem during maintenance and scalability as there are no defined standards that have been used. <a href="https://docs.communityhealthtoolkit.org/apps/concepts/interoperability/">Interoperability</a> allows technical teams to scale in an efficient and repeatable manner due to the already predefined standards.</p> -<h2 id="solution-overview">Solution Overview</h2> -<p>This is a model for interoperability that can be used for Loss to Follow up flows between the CHT and a health facility or a requesting system. Community health workers routinely follow up on patients physically at their residences or place of work. In instances where the patient needs to visit a health facility for routine checkups or specialized care, the CHW has no visibility of the process outside the CHT. The interoperability layer built allows other systems including health facilities to exchange data in a standardized format.</p> -<h2 id="intended-users">Intended Users</h2> -<p>The intended users of the interoperable systems are CHWs on the CHT side and Healthcare Givers on the requesting system side. System administrators can access the mediator on the administration console.</p> -<p>See the sequence diagram below showcasing the with the loss to follow up flow:</p> -<figure><a href="LTFU%20Sequence%20Diagram.png"> -<img src="LTFU%20Sequence%20Diagram.png"/> </a> -</figure> -<p>The Interoperating Systems:</p> -<ul> -<li><a href="https://docs.communityhealthtoolkit.org/">CHT</a>: Community Health Toolkit.</li> -<li><a href="http://openhim.org/">OpenHIM</a>: Mediator (middleware).</li> -<li>Requesting System: For testing purposes, we used HTTP Requests.</li> -</ul> -<p>OpenHIM was chosen as the main component of the interoperability layer and custom mediators were built to deal with the different workflows as it provides a central point of control for managing data exchange and security.</p>Apps: Pharmacovigilance Reference apphttps://docs.communityhealthtoolkit.org/apps/examples/pharmacovigilance-reference-app/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/pharmacovigilance-reference-app/ -<h2 id="problem-being-addressed">Problem Being Addressed</h2> -<p>Self-medication, unregulated medical products, and counterfeit drugs have led to a significant increase in the prevalence of adverse drug reactions (ADRs), adverse effects following Immunization (AEFI), and the proliferation of poor-quality health products and technologies. ADRs are a common cause of hospital admissions and contribute to patient mortality, placing a substantial economic burden on resource-limited healthcare systems, especially in African countries.</p> -<p>Need for Pharmacovigilance: Pharmacovigilance focuses on the detection, assessment, understanding and prevention of adverse effects and other potential drug-related problems. Pharmacovigilance facilitates early detection of unknown adverse reactions, identifies increases in frequency of known adverse reactions and helps disseminate information to improve drug prescription and regulation. To address these challenges, there is a crucial need for a robust pharmacovigilance system that enables the detection, reporting, and mitigation of adverse drug reactions and incidents.</p> -<h2 id="solution-overview">Solution Overview</h2> -<p>The CHT Pharmacovigilance Reference app is designed and developed by <a href="https://www.intellisoftkenya.com/">IntelliSOFT Consulting LTD</a>. Components of Kenya’s Pharmacovigilance Electronic Reporting System have been integrated into the Community Health Toolkit (CHT) to support community health providers (CHPs) and Community Health Assistants (CHAs) to efficiently identify, report, refer and manage ADRs and AEFI while delivering healthcare.</p> -<p>In the spirit of openness and as a contribution to the CHT open source project, Medic and IntelliSOFT have coordinated to provide the source code and documentation of the workflows of the CHT Pharmacovigilance Reference app. -This reference app serves as an example application that other CHT Implementers can use to learn how to build pharmacovigilance workflows to help improve patient safety for the community members they serve.</p> -<h2 id="key-pharmacovigilance-areas-supported">Key Pharmacovigilance Areas Supported</h2> -<ol> -<li>Adverse Drug Reactions (ADRs): detect and report ADRs resulting from pharmaceutical products.</li> -<li>Adverse Effects Following Immunization (AEFI): monitor and report adverse effects following immunization.</li> -<li>Poor Quality Health Products and Technologies: identify and report incidents related to the use of poor-quality health products and technologies.</li> -<li>Death Monitoring: Monitor, identify, report and record deaths resulting from monitored conditions within household members in the Pharmacovigilance application.</li> -</ol> -<h2 id="users-and-hierarchy">Users and hierarchy</h2> -<figure class="right col-12 col-lg-12"><a href="PharmacovigilanceHierarchy.png"> -<img src="PharmacovigilanceHierarchy.png"/> </a> -</figure> -<h2 id="pharmacovigilance-workflows">Pharmacovigilance workflows</h2> -<h3 id="reporting-referral-and-follow-up-of-patients">Reporting, referral and follow up of patients</h3> -<p>During a household visit, a CHP uses the pharmacovigilance app to assess household members for suspected severe adverse reactions to medication or recent vaccinations/immunizations. Using the assessment form, a CHP records any suspicious issues with the medication, such as unusual colour, smell, or packaging and adverse drug effects from medication and immunizations.</p> -<p>A public report task is generated for a CHA once a CHP submits a suspected case of adverse reaction. The public report form captures information about the adverse effects and medication including side effects, onset date, and current status of the reaction. Additionally, the form captures medication details such as name of the medicine, manufacturer, purchase location, start date, stop date, and expiry date. In case of a variety of medicines, the form allows a CHA to perform multiple entries.</p> -<p>The CHA also uses the public report to record outcome details, including outcome specification and recovery status. Using the public report form, the CHA is able to refer household members to the nearest facility for further review and care, this action generates a referral follow up task for the CHP. The referral follow up task appears 7 days after facility referral and guides the CHP to follow up referred cases to confirm if patients attended the referral . The CHP can also check the condition of patients and once they complete the referral follow up task, the CHA gets patient status notification task.</p> -<figure class="right col-12 col-lg-12"><a href="Workflow.png"> -<img src="Workflow.png"/> </a> -</figure> -<h3 id="death-reporting-and-confirmation-workflow">Death reporting and confirmation workflow</h3> -<p>A CHP uses the assessment form to report a death caused by an adverse drug reaction, vaccination/immunization, or poor quality medicine; submission of a death report triggers a death confirmation task for a CHA.</p> -<figure class="right col-12 col-lg-12"><a href="deathReportingConfirmation.png"> -<img src="deathReportingConfirmation.png"/> </a> -</figure> -<h2 id="resources-to-get-started">Resources to Get Started</h2> -<p>Here are a few additional resources to help get you started with the pharmacovigilance health reference application.</p> -<ul> -<li>View the <a href="https://github.com/medic/cht-accelerator/tree/main/IntelliSOFT/Example%20CHT%20application/cht_pvers">configuration code for this reference app</a></li> -<li>Install the reference app following these <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/">easy installation instructions</a></li> -<li>Modify the pharmacovigilance reference application for your project context using <a href="https://docs.communityhealthtoolkit.org/design/best-practices/">configuration best practices</a></li> -</ul> -<p>The open sharing of digital health apps used by CHWs is a monumental milestone in the digital health space, and for the CHT Community. Reach out on the <a href="https://forum.communityhealthtoolkit.org/">forum</a> to share how you will leverage these resources, along with your feedback and continued innovations that could benefit the larger community.</p>Apps: YendaNafe CHT app by PIH in Malawihttps://docs.communityhealthtoolkit.org/apps/examples/pih/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/pih/ -<div class="pageinfo pageinfo-primary"> -<p>Since 2017, <a href="https://www.pih.org/country/malawi">Partners in Health (PIH) Malawi</a> and <a href="https://medic.org/">Medic</a> have collaboratively co-designed and developed YendaNafe, a digital health app for community based service provision. In the spirit of openness, Medic and PIH have coordinated the release of the full application source code of Yendanafe app as first of kind ‘‘Integrated CHT Reference app’’. This Reference app provides an example that CHT Implementers can learn how they can design and configure integrated workflows.</p> -</div> -<h2 id="problem-being-addressed">Problem being addressed</h2> -<p>Malawi is one of the countries with a high prevalence of HIV, high rates of infant and maternal mortality and a high burden of non communicable diseases (NCD) and tuberculosis. To help respond to community needs and reduce the disease burden, PIH in partnership with the Ministry of Health are implementing an integrated household model in Neno District. Under the model, community health workers (CHWs) and CHW supervisors have been equipped with the YendaNafe app, the app supports the health care workers to provide integrated community based health care services and to coordinate care.</p> -<h2 id="solution-overview">Solution overview</h2> -<p>The YendaNafe app workflows support CHWs to conduct integrated disease screening, provide health care services, refer people in communities to facilities especially those who require facility based care and raise community awareness on health. The app is designed to support the following key health areas:</p> -<ul> -<li>Maternal child health (antenatal care and postnatal care)</li> -<li>Family planning (FP)</li> -<li>Malnutrition</li> -<li>Immunization</li> -<li>Integrated management of childhood illness (IMCI)</li> -<li>Human Immunodeficiency Virus (HIV)</li> -<li>Tuberculosis (TB)</li> -<li>Non communicable diseases (NCDs)</li> -</ul> -<h2 id="users-and-hierarchy">Users and hierarchy</h2> -<table> -<thead> -<tr> -<th>Users</th> -<th>Location</th> -<th>Devices</th> -<th>Roles</th> -</tr> -</thead> -<tbody> -<tr> -<td>Community Health Director</td> -<td>admin level</td> -<td>Laptop or desktop</td> -<td>Staff at this level will be given access to dashboards where they can monitor program indicators. Online only access to the app.</td> -</tr> -<tr> -<td>CHW Manager and Officers</td> -<td>admin level</td> -<td>Laptop or desktop</td> -<td>Staff at this level will be given access to dashboards where they can monitor program indicators. Online only access to the app.</td> -</tr> -<tr> -<td>Site Supervisors (SS)</td> -<td>admin level</td> -<td>Laptop or desktop</td> -<td>Staff at this level will be given access to dashboards where they can monitor program indicators. Online only access to the app.</td> -</tr> -<tr> -<td>Senior CHWs (SCHW) - Supervisory role</td> -<td>Facility and community levels</td> -<td>Smartphone</td> -<td>They supervise CHW and mentor CHWs, collect sputum for probable TB cases. They are offline users.</td> -</tr> -<tr> -<td>CHWs</td> -<td>Community level</td> -<td>Smartphone</td> -<td>CHWs register households, conduct household visits, case screening, referrals, follow-ups, and defaulter tracing. They are offline users.</td> -</tr> -</tbody> -</table> -<h2 id="workflows">Workflows</h2> -<h3 id="maternal-neonatal-health-workflow">Maternal neonatal health workflow</h3> -<p>This workflow consists of the pregnancy, delivery and postnatal workflows.The pregnancy workflow enables CHWs to register new pregnancies, screen pregnant mothers for danger signs and follow up pregnant mother to remind them to attend the scheduled ANC clinic appointments. The postnatal (PNC) workflow supports CHWs to follow up PNC women and newborns for danger signs screening and refer PNC women with danger signs to facilities to receive more care.</p> -<h4 id="pregnancy-workflow">Pregnancy workflow</h4> -<div class="container workflow align-items-center"> -<div class="row d-none d-sm-flex text-center"> -<div class="col-3 align-self-end"> -<img src="pnc-chw.png" alt="Condition" /><h4>Condition</h4> -</div> -<div class="col-1"></div> -<div class="col-3 align-self-end"> -<img src="chw.png" alt="Task" /><h4>Task</h4> -</div> -<div class="col-1"></div> -<div class="col-3 align-self-end"> -<img src="pnc-chw-facility.png" alt="Resolution" /><h4>Resolution</h4> -</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>Upon discovering a suspected pregnancy, a CHW escorts the woman to a health facility for a pregnancy test and submits a <strong>Pregnancy Screening Form</strong>.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>A <strong>Pregnancy Confirmation and Referral Follow-up Task</strong> will appear immediately and is due 3 days later. The task is to remind the CHW to confirm referral attendance and pregnancy status.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>The woman visits the health facility, gets a pregnancy test and starts ANC clinic.</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>In case of a confirmed pregnancy(woman has started ANC visits) the CHW submits a <strong>Pregnancy Registration Form</strong> with gestational age and facility EDD if available.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>Every first day of the month, the CHW receives <strong>Pregnancy Follow-Up Task</strong> to remind that it is time to check into pregnant mother. The tasks continue for 42 weeks.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>The CHW submits <strong>Pregnancy Follow-Up Form</strong> demonstrating that she provided ANC counseling, gathered information from prior facility visits, screened for danger signs and reminded the woman of her upcoming facility ANC visit.</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>If the CHW notices danger signs at any time, she submits a <strong>Danger Sign Screening Form</strong> and immediately refers or accompanies the patient to the facility depending on severity.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>A <strong>Danger Sign Follow-Up Task</strong> will appear immediately and is due 3 days later. Tasks persist for 7 days after due date.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>CHW submits a <strong>Danger Sign Follow-Up Form</strong>, verifying that she visited the woman to confirm that she attended the facility.</div> -</div> -</div> -<h4 id="delivery-workflow">Delivery workflow</h4> -<div class="container workflow align-items-center"> -<div class="row d-none d-sm-flex text-center"> -<div class="col-3 align-self-end"> -<img src="pnc-chw.png" alt="Condition" /><h4>Condition</h4> -</div> -<div class="col-1"></div> -<div class="col-3 align-self-end"> -<img src="chw.png" alt="Task" /><h4>Task</h4> -</div> -<div class="col-1"></div> -<div class="col-3 align-self-end"> -<img src="pnc-chw-facility.png" alt="Resolution" /><h4>Resolution</h4> -</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>A currently registered pregnant person has reached a gestational age of 42 weeks and has not had a miscarriage or a delivery reported.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>A <strong>Delivery Task</strong> requesting that the CHW check in on the woman to see whether she has delivered. Task appears from 38 weeks.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>If the woman has delivered and is available at home, CHW submits <strong>Delivery Form</strong>, confirming the pregnancy outcomes. Profiles are created for each baby that is alive. This &ldquo;ends&rdquo; the pregnancy workflow.</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>When a woman delivers and is discharged from the health facility, a health facility staff (Site Supervisor) submits a <strong>Delivery Discharge Form</strong>.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>CHW receives a <strong>Delivery Report Task</strong> to inform them to visit the mother and report the delivery.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>CHW visits the mother, submits a <strong>Delivery Form</strong>, confirming the pregnancy outcomes. Profiles are created for each baby that is alive. This &ldquo;ends&rdquo; the pregnancy workflow.</div> -</div> -</div> -<h4 id="postnatal-workflow">Postnatal workflow</h4> -<div class="container workflow align-items-center"> -<div class="row d-none d-sm-flex text-center"> -<div class="col-3 align-self-end"> -<img src="anc-chw-facility.png" alt="Condition" /><h4>Condition</h4> -</div> -<div class="col-1"></div> -<div class="col-3 align-self-end"> -<img src="chw.png" alt="Task" /><h4>Task</h4> -</div> -<div class="col-1"></div> -<div class="col-3 align-self-end"> -<img src="anc-chw-facility.png" alt="Resolution" /><h4>Resolution</h4> -</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>A woman delivers and CHW submits a <strong>Delivery Report</strong>.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>A <strong>PNC Follow Up Task</strong> for both mother and baby appear on the 3rd and 5th day post delivery. The task is to remind the CHW to visit the postnatal mother.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>CHW visits the mother and baby, submits a <strong>PNC Follow Up Task</strong> confirming that they visited the mother and/or baby, screened for danger and reminded them of upcoming facility appointment.</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>If the CHW notices danger signs at any time for either mother or baby, she submits a <strong>Danger Sign Form</strong> and immediately refers to the facility depending on severity.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>A <strong>Danger Sign Follow-Up Task</strong> will appear within 2 days and is due 3 days later. Tasks persists for 7 days after due date.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>CHW submits a <strong>Danger Sign Follow-Up Form</strong>, verifying that she visited the woman/child to confirm that she attended the facility.</div> -</div> -</div> -<h3 id="integrated-management-of-childhood-illness-imci-workflow">Integrated management of childhood illness (IMCI) workflow</h3> -<p>This workflow is designed to support CHWs to identify symptomatic children at the household level, refer symptomatic children and conduct on-time follow ups for children through the follow up tasks</p> -<h4 id="imci-workflow">IMCI workflow</h4> -<div class="container workflow align-items-center"> -<div class="row d-none d-sm-flex text-center"> -<div class="col-3 align-self-end"> -<img src="patient-child-chw.png" alt="Condition" /><h4>Condition</h4> -</div> -<div class="col-1"></div> -<div class="col-3 align-self-end"> -<img src="chw.png" alt="Task" /><h4>Task</h4> -</div> -<div class="col-1"></div> -<div class="col-3 align-self-end"> -<img src="patient-child-chw-facility.png" alt="Resolution" /><h4>Resolution</h4> -</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>CHW visits, educates, and screens under 5 for cough, diarrhea, fast breathing, fever, and fever with convulsions. CHW submits a <strong>IMCHI Screening Form</strong> and refers to the health facility if necessary.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>A <strong>IMCI Referral Follow-up Task</strong> will appear within 2 days and is due 3 days later. The task is to remind the CHW to confirm referral attendance.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>A <strong>IMCI Referral Follow-up Form</strong>, verifying that she visited the child to confirm referral completion and offer adherence counselling or remind on upcoming appointments.</div> -</div> -</div> -<h3 id="malnutrition-workflow">Malnutrition workflow</h3> -<p>The workflow supports CHWs to assess and identify malnourished children, refer them to a health facility and conduct on time follow ups.</p> -<h4 id="malnutrition-workflow-1">Malnutrition workflow</h4> -<div class="container workflow align-items-center"> -<div class="row d-none d-sm-flex text-center"> -<div class="col-3 align-self-end"> -<img src="patient-child-chw.png" alt="Condition" /><h4>Condition</h4> -</div> -<div class="col-1"></div> -<div class="col-3 align-self-end"> -<img src="chw.png" alt="Task" /><h4>Task</h4> -</div> -<div class="col-1"></div> -<div class="col-3 align-self-end"> -<img src="patient-child-chw-facility.png" alt="Resolution" /><h4>Resolution</h4> -</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>CHW counsels and screens under 5 for malnutrition using MUAC tape and danger signs. If suspected to be malnourished, the child is referred to the health facility. CHW submits a <strong>Malnutrition Screening Form</strong>.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>A <strong>Malnutrition Referral Follow-up Task</strong> will appear within 2 days and is due 3 days later. The task is to remind the CHW to confirm referral attendance.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>CHW submits <strong>Malnutrition Referral Follow up Form</strong> verifying that she visited the child to confirm that he was taken to health facility.</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>If Child is malnourished, he/she is enrolled into malnutrition program either Outpatient Therapeutic Programme (OTP) or Supplementary Feeding Programme (SFP).</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>On the first day of every month, the CHW receives <strong>Malnutrition Treatment Follow-Up Task</strong> to remind them that it is time to check on the child. The task will continue showing till CHW indicates that the child has been exited from malnutrition programme.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>The CHW submits <strong>Malnutrition Treatment Follow-Up Form</strong> demonstrating that she provided adherence counselling, and reminded the caregiver of the child&rsquo;s upcoming facility visit.</div> -</div> -</div> -<h3 id="family-planning-workflow">Family planning workflow</h3> -<p>The FP workflow ensures that eligible women receive their FP needs with support from the CHWs. The FP workflow supports CHWs to counsel eligible women on FP, refer women to facilities for FP services screen FP clients for side effects and refer and ensure ontime FP renewals.</p> -<h4 id="family-planning-workflow-1">Family planning workflow</h4> -<div class="container workflow align-items-center"> -<div class="row d-none d-sm-flex text-center"> -<div class="col-3 align-self-end"> -<img src="patient-chw.png" alt="Condition" /><h4>Condition</h4> -</div> -<div class="col-1"></div> -<div class="col-3 align-self-end"> -<img src="chw.png" alt="Task" /><h4>Task</h4> -</div> -<div class="col-1"></div> -<div class="col-3 align-self-end"> -<img src="patient-chw-facility.png" alt="Resolution" /><h4>Resolution</h4> -</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>Upon discovering a woman who is not pregnant and not on FP, the CHW provides FP education, refers them to a health facility if necessary and submits a <strong>Family Planning Screening Form</strong>.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>A <strong>Pregnancy Confirmation and FP Follow-up Task</strong> will appear 2 days and is due 3 days later. The task reminds the CHW to confirm referral attendance and FP status.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>The woman visits the health facility and is started on FP if eligible.</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>A woman is found to be already on FP, the CHW records FP method, screen for side effects and counsel on adherence to method and clinic appointments.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>Every first day of the month, the CHW receives <strong>Family Planning Follow-Up Task</strong> to remind that it is time to check on the woman.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>The CHW submits <strong>Family Planning Follow-Up Form</strong> demonstrating that she screened for side effects, need to change FP methods, offered adherence counselling and remind the woman of her upcoming appointment.</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>For women on long term FP methods either IUCD or implant, the CHW records the FP expiry date.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>One month to the expiry date the CHW receives <strong>FP Expiry Follow-Up Task</strong> to remind the woman to renew their FP. Tasks persists for 30 days past the due date.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>CHW submits a <strong>FP Expiry Follow-Up Form</strong>, verifying that she visited the woman to confirm that she attended the facility for either renewal or change of FP method.</div> -</div> -</div> -<h3 id="tb-workflows">TB workflows</h3> -<p>The TB workflow enables CHWs to screen patients for TB, refer suspected TB cases to senior CHW for sputum collection and follow up TB patients. Senior CHWs who are based at the community level submit the sputum collection form after collecting the sputum samples for TB testing and the Site supervisors who are based at the facility level support in notifying CHWs when the TB results are out and in tracing TB defaulters.</p> -<h4 id="tb-workflow-for-tb-suspected-cases-identified-in-a-community-setting">TB workflow for TB suspected cases identified in a community setting</h4> -<div class="container workflow align-items-center"> -<div class="row d-none d-sm-flex text-center"> -<div class="col-3 align-self-end"> -<img src="patient-chw.png" alt="Condition" /><h4>Condition</h4> -</div> -<div class="col-1"></div> -<div class="col-3 align-self-end"> -<img src="chw.png" alt="Task" /><h4>Task</h4> -</div> -<div class="col-1"></div> -<div class="col-3 align-self-end"> -<img src="patient-chw-facility.png" alt="Resolution" /><h4>Resolution</h4> -</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>CHW educates and screens over 5&rsquo;s for TB using the cardinal signs. CHW refers any suspected person to the Senior CHW for sputum collection and submits a <strong>TB Screening Form</strong>.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>A <strong>Sputum Collection Task</strong> will appear immediately on the SCHW device. The task is to inform the SCHW to collect sputum from the referred person.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>SCHW submits <strong>Sputum Collection Form</strong> demonstrating that she collected the sputum from the referred person and submitted the samples to the health facility.</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>When the TB results are ready, a Site Supervisors who is based at the health facility submits <strong>TB Results Form</strong> detailings the TB results outcome.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>A TB Results Task will appear immediately on the CHW device while <strong>TB results Notification Task</strong> will appear on the SCHW device.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>The TB Results task indicates the TB results outcome: positive, negative, or rejected. The SCHW submits <strong>TB results notification form</strong> demonstrating that they have updated the TB results in the paper based cough register.</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>Person is found to be TB positive. CHW refers the person to the health facility for TB treatment commencement</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>A <strong>TB Referral Follow-Up Task</strong> will appear within 2 days on the CHW device and is due 3 days later. The task is meant to inform the CHW that it&rsquo;s time to follow up on the person to confirm referral attendance.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>The CHW submits <strong>TB Follow-Up Form</strong> demonstrating that she visited the person to confirm if she went to the health facility. CHW refers other household members to health facility for TB contact tracing.</div> -</div> -</div> -<h4 id="tb-workflow-for-tb-confirmed-cases-and-tb-defaulters">TB workflow for TB confirmed cases and TB defaulters</h4> -<div class="container workflow align-items-center"> -<div class="row d-none d-sm-flex text-center"> -<div class="col-3 align-self-end"> -<img src="patient-chw.png" alt="Condition" /><h4>Condition</h4> -</div> -<div class="col-1"></div> -<div class="col-3 align-self-end"> -<img src="chw.png" alt="Task" /><h4>Task</h4> -</div> -<div class="col-1"></div> -<div class="col-3 align-self-end"> -<img src="patient-chw-facility.png" alt="Resolution" /><h4>Resolution</h4> -</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>Person is on started on TB treatment.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>A CHW visits the person daily and submits <strong>Daily Follow up Form</strong>. This will continue till the person is cured and CHW submits TB exit form.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>The CHW submits <strong>TB Daily Follow-Up Form</strong> demonstrating that she provided adherence counselling, screened for danger signs/side effects and reminded the person of upcoming facility visits.</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>A person walks into health facility without CHW referral and is enrolled into TB program. Site Supervisor submits a <strong>TB Enrollment Form</strong> indicating person has been enrolled in TB program.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>A <strong>TB Follow-Up Task</strong> will appear immediately on the CHW device. The task is meant to inform the CHW that the person has been enrolled in TB program and needs their follow up.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>The CHW submits <strong>TB Daily Follow-Up Form</strong> demonstrating that she provided adherence counselling, screened for danger signs/side effects and reminded the person of upcoming facility visits.</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>A TB Client defaults on their clinic visit. Site Supervisor submits Trace Report indicating the missed visit details.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>A TRACE Follow-Up Task will appear immediately on the CHW device. The task is meant to inform the CHW that the person missed their clinic visit.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>The CHW submits <strong>TRACE Follow-Up Form</strong> demonstrating that she visited the TB patient and encouraged them to visit the health facility.</div> -</div> -</div> -<h3 id="non-communicable-diseases-workflow">Non communicable diseases workflow</h3> -<p>The non communicable diseases workflows support health care workers to screen for NCD symptoms, refer suspected cases to facilities and trace clients who have defaulted treatment.</p> -<div class="container workflow align-items-center"> -<div class="row d-none d-sm-flex text-center"> -<div class="col-3 align-self-end"> -<img src="patient-chw.png" alt="Condition" /><h4>Condition</h4> -</div> -<div class="col-1"></div> -<div class="col-3 align-self-end"> -<img src="chw.png" alt="Task" /><h4>Task</h4> -</div> -<div class="col-1"></div> -<div class="col-3 align-self-end"> -<img src="patient-chw-facility.png" alt="Resolution" /><h4>Resolution</h4> -</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>CHW educates and screens over 5&rsquo;s for NCD symptoms. CHW refers any suspected person to the health facility and submits a <strong>NCD Screening Form</strong>.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>A NCD referral <strong>Follow-up Task</strong> will appear within 2 ays and is due 3 days later. The task is to remind the CHW to confirm referral attendance.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>CHW submits <strong>NCD Referral Follow-up Form</strong> verifying that she visited the person to confirm that he went to the health facility.</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>Person is to found to have either NCD (hypertension, diabetes, epilepsy, asthma, mental health or heart failure) and is enrolled into NCD program.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>Every first day of the month, CHW receives <strong>NCD Monthly Treatment Follow-up Task</strong> to remind the CHW it&rsquo;s time to check on the person.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>The CHW submits <strong>NCD Monthly Treatment Follow-up Form</strong> demonstrating that she provided adherence counselling, screened for danger signs/side effects and reminded the person of upcoming facility visits.</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>A person walks into health facility without CHW referral and is enrolled into NCD program. Site Supervisor submits a <strong>Treatment Enrollment Form</strong> indicating person has been enrolled in NCD program.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>A <strong>NCD Monthly Treatment Follow-up Task</strong> will appear immediately on the CHW device. The task is meant to inform the CHW that the person has been enrolled in NCD program and needs their follow up.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>The CHW submits <strong>Monthly Treatment Follow-up Form</strong> demonstrating that she provided adherence counselling, screened for danger signs/side effects and reminded the person upcoming facility visits.</div> -</div> -</div> -<div class="container workflow align-items-center"> -<div class="row d-none d-sm-flex text-center"> -<div class="col-3 align-self-end"> -<img src="patient-chw.png" alt="Condition" /><h4>Condition</h4> -</div> -<div class="col-1"></div> -<div class="col-3 align-self-end"> -<img src="chw.png" alt="Task" /><h4>Task</h4> -</div> -<div class="col-1"></div> -<div class="col-3 align-self-end"> -<img src="patient-chw-facility.png" alt="Resolution" /><h4>Resolution</h4> -</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>If NCD patient reports danger sign during the follow ups, the CHW immediately refers or accompanies the patient to the facility depending on severity of the danger sign.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>A <strong>NCD Danger Signs Follow-up Task</strong> will appear within 2 days and is due 3 days later. The task is to remind the CHW to confirm referral attendance.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>The CHW submits a <strong>NCD Danger Sign Follow-up Form</strong> verifying that she visited the NCD patient to confirm that she attended the facility.</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>A NCD Client defaults on their clinic visit. Site Supervisor submits <strong>Trace Report</strong> indicating the missed visit details.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>A <strong>TRACE Follow-up Task</strong> will appear immediately on the CHW device. The task is meant to inform the CHW that the person missed their clinic visit.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>The CHW submits <strong>TRACE Follow-up Form</strong> demonstrating that she visited the NCD patient and encouraged them to visit the health facility.</div> -</div> -</div> -<h3 id="human-immunodeficiency-virus-workflow">Human Immunodeficiency Virus workflow</h3> -<p>The Human Immunodeficiency Virus (HIV) workflow is designed to guide CHWs in HIV screening of household members, screening for side effects for persons on antiretroviral treatment and CHWs can use the workflows to trace defaulters who are on ART treatment.</p> -<div class="container workflow align-items-center"> -<div class="row d-none d-sm-flex text-center"> -<div class="col-3 align-self-end"> -<img src="patient-chw.png" alt="Condition" /><h4>Condition</h4> -</div> -<div class="col-1"></div> -<div class="col-3 align-self-end"> -<img src="chw.png" alt="Task" /><h4>Task</h4> -</div> -<div class="col-1"></div> -<div class="col-3 align-self-end"> -<img src="patient-chw-facility.png" alt="Resolution" /><h4>Resolution</h4> -</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>CHW educates and screens over 5&rsquo;s for duration since last HIV test. CHW refers any eligible person to the health facility and submits a <strong>HIV Screening Form</strong>.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>A HIV testing referral <strong>Follow-up Task</strong> will appear within 2 days and is due 3 days later. The task is to remind the CHW to confirm referral attendance and HIV testing status.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>CHW submits <strong>HIV Testing Referral Follow-up Form</strong> verifying that she visited the person to confirm that he went to the health facility.</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>If person tests HIV positive, he/she is enrolled into HIR/ART program and started on treatment.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>In the first year of treatment, the CHW visits the person daily and submits Daily Follow up Form.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>The CHW submits <strong>Daily Follow-up Form</strong> demonstrating that she provided adherence counselling, screened for danger signs/side effects and reminded the person of upcoming facility visits.</div> -</div> -<div class="row"> -<div class="col-12 col-sm-3 text"><h4 class="d-sm-none">Condition</h4>A person walks into health facility without CHW referral and is enrolled into ART program. Site Supervisor submits a <strong>Treatment Enrollment Form</strong> indicating person has been enrolled in ART program.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-3 text"><h4 class="d-sm-none">Task</h4>A <strong>HIV Daily Follow-up Task</strong> will appear immediately on the CHW device. The task is meant to inform the CHW that the person has been enrolled in NCD program and needs their follow up.</div> -<div class="col-1 d-none d-sm-flex align-self-center"><i class="fas fa-arrow-alt-circle-right"></i></div> -<div class="col-1 d-sm-none"><i class="fas fa-long-arrow-alt-right fa-rotate-45"></i></div> -<div class="col-11 col-sm-4 text"><h4 class="d-sm-none">Resolution</h4>The CHW submits <strong>Daily Follow-up Form</strong> demonstrating that she provided adherence counselling, screened for danger signs/side effects and reminded the person upcoming facility visits.</div> -</div> -</div> -<h2 id="resources-to-get-started">Resources to Get Started</h2> -<p>Here are a few additional resources to help get you started with the integrated health reference application.</p> -<ul> -<li>View the <a href="https://github.com/medic/cht-pih-malawi-app">configuration code for this reference app</a></li> -<li>Install the reference app following these <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/">easy installation instructions</a></li> -<li>Modify the maternal and newborn reference application for your project context using <a href="https://docs.communityhealthtoolkit.org/design/best-practices/">configuration best practices</a></li> -</ul> -<p>The open sharing of digital health apps used by CHWs is a monumental milestone in the digital health space, and for the CHT Community. Reach out on the <a href="https://forum.communityhealthtoolkit.org/">forum</a> to share how you will leverage these resources, along with your feedback and continued innovations that could benefit the larger community.</p>Apps: Remote Onboarding and Traininghttps://docs.communityhealthtoolkit.org/apps/examples/training/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/training/ -<p>The CHT’s Remote Onboarding and Training functionality enables Supervisors and Administrators to train CHWs on care workflows and related app use without being physically present. It is designed for:</p> -<ul> -<li>Safety: maintaining distance due to infectious disease</li> -<li>Speed: faster deployment when timing is a critical</li> -<li>Scalability: onboarding large numbers of users at the same time</li> -<li>Measurability: evaluation to provide added support where needed</li> -<li>Adaptability: integration with existing program and workflow structures</li> -</ul> -<h2 id="problem-being-addressed">Problem Being Addressed</h2> -<p>Providing consistent training for CHWs is critically important in the context of evolving health programs and use of digital support tools. In-person training is often not feasible (due to distance, infectious disease outbreaks, or similar), but is still required to build and maintain effective care programs. Relying on CHWs to learn to use these new digital tools and adhere to care workflows on their own is a major challenge to health program success.</p> -<h2 id="solution-overview">Solution Overview</h2> -<p>The CHT’s onboarding and training capabilities offer a remote way to provide education to CHWs and Supervisors about digital tools functionality and care workflows. It can be deployed in both SMS and App based modules. Through asynchronous communication, program administrators are able to:</p> -<ul> -<li>Create customized training modules that match program requirements</li> -<li>Capture assessments of CHW knowledge levels of training material</li> -<li>Assess user training participation abandonment</li> -<li>Identify CHWs who need additional Supervisor support</li> -</ul> -<h2 id="users-and-hierarchy-example">Users and Hierarchy Example</h2> -<table> -<thead> -<tr> -<th style="text-align:left">User</th> -<th style="text-align:left">Location</th> -<th style="text-align:left">Devices</th> -<th style="text-align:left">Role</th> -</tr> -</thead> -<tbody> -<tr> -<td style="text-align:left">National Officials and County Teams</td> -<td style="text-align:left">Central and district offices</td> -<td style="text-align:left">Desktop, laptop and smartphones</td> -<td style="text-align:left">Monitor central analytics regarding onboarding and training completion. Revisit content where needed.</td> -</tr> -<tr> -<td style="text-align:left">Sub-County Teams</td> -<td style="text-align:left">Facilities or local office</td> -<td style="text-align:left">Tablet or smartphone</td> -<td style="text-align:left">Review CHW progress on aggregate, support CHW supervisors, and monitor location specific analytics.</td> -</tr> -<tr> -<td style="text-align:left">CHW Supervisors</td> -<td style="text-align:left">Community level, based at facilities</td> -<td style="text-align:left">Smartphone</td> -<td style="text-align:left">Verify CHW onboarding and training completion, follow up with those who have not. Complete their own onboarding and training tasks to new workflows.</td> -</tr> -<tr> -<td style="text-align:left">CHWs</td> -<td style="text-align:left">Community level</td> -<td style="text-align:left">Feature phones and some smartphones</td> -<td style="text-align:left">Complete onboarding and training via SMS or app, provide feedback and ask for support where needed.</td> -</tr> -</tbody> -</table> -<h2 id="workflow-examples">Workflow Examples</h2> -<h3 id="remote-login-by-app">Remote Login by App</h3> -<p>Users may log into their app with a link sent to them via SMS. The link allows the user to directly enter their app, bypassing the need to enter their username and password.</p> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/concepts/access/#remote-login">Accessing CHT Apps</a></p> -<h3 id="remote-training-overview">Remote Training Overview</h3> -<p>These SMS and App based workflow examples illustrate how the CHT enables remote training, tasking and communication at scale. Training can be done using simple guides, audio/videos suitable for low bandwidth, or interactive experiences on personal devices. Training programs can be easily configured to suit specific health program needs.</p> -<h3 id="remote-training-by-sms">Remote Training by SMS</h3> -<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"> -<iframe src="https://www.youtube.com/embed/-24pWKckXMk" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe> -</div> -<h3 id="remote-training-by-app">Remote Training by App</h3> -<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"> -<iframe src="https://www.youtube.com/embed/pFEFIY_SA7M" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe> -</div> -<br> -<p>More background information can be found in this <a href="https://docs.google.com/presentation/d/13bFoyU2vhwPiOUiVWzUJ2urtAyR6_XKTxp0XASCLVko">summary deck</a>.</p>Apps: Stock Monitoringhttps://docs.communityhealthtoolkit.org/apps/examples/stock-monitoring/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/stock-monitoring/ -<h2 id="problem-being-addressed">Problem Being Addressed</h2> -<p>Paper based commodity management systems are prone to errors due to reliance on manually updated registers, this greatly affects data quality. Further, it is time consuming for CHWs to reference stock balances and at the same time update stocks on the commodity management sheet while providing treatment to household members. In terms of supervision, CHW’s supervisors do not know when CHWs have stock outs in time for replenishing and ordering purposes. A stock monitoring application can help CHWs update their stocks seamlessly while strengthening data integrity.</p> -<h2 id="solution-overview">Solution Overview</h2> -<p>CHT stock monitoring apps can be configured to support community drug and commodity management among CHWs and their supervisors. The workflow can be designed to:</p> -<ul> -<li><strong>Capture</strong> stocks received from and returned to the health facility</li> -<li><strong>Provide</strong> stock status of every commodity</li> -<li><strong>Automatically deduct</strong> stocks when a CHW provides treatment to the community members</li> -<li><strong>Escalate</strong> stock out alerts to the supervisors when the CHW runs low on any of the commodities</li> -</ul> -<p>Some design considerations to make include:</p> -<ul> -<li>A stock condition card will appear on the CHW profile that will show a summary of the stock status</li> -<li>In-built stock thresholds for different commodities</li> -<li>Distinct colors for different stock thresholds -<ul> -<li>Red: Low stock</li> -<li>Green: Optimal stock</li> -<li>Yellow: Medium stock</li> -</ul> -</li> -<li>Automated stock level updates when a CHW provides treatment within the workflows</li> -<li>Stock out tasks to CHW supervisors</li> -<li>CHW supervisor updates the stocks disbursed on respective CHW Area and the CHW receives a task to confirm the received stocks</li> -<li>Ensure consistency of stock units across all the sections of the app</li> -</ul> -<h2 id="users-and-hierarchy-example">Users and Hierarchy Example</h2> -<table> -<thead> -<tr> -<th style="text-align:left">User</th> -<th style="text-align:left">Location</th> -<th style="text-align:left">Devices</th> -<th style="text-align:left">Role</th> -</tr> -</thead> -<tbody> -<tr> -<td style="text-align:left">MOH</td> -<td style="text-align:left">Central and district offices</td> -<td style="text-align:left">Desktop, laptop</td> -<td style="text-align:left">Access to dashboards where they monitor program indicators. Can view aggregate targets, not patient data.</td> -</tr> -<tr> -<td style="text-align:left">District Biostatistician</td> -<td style="text-align:left">Local office</td> -<td style="text-align:left">Desktop, laptop</td> -<td style="text-align:left">Equipped with dashboards. Can view aggregate targets.</td> -</tr> -<tr> -<td style="text-align:left">Supervisor</td> -<td style="text-align:left">Community level, based at facilities</td> -<td style="text-align:left">Desktop, laptop</td> -<td style="text-align:left">Equipped with CHT app. Can view CHW areas, their tasks and aggregate targets.</td> -</tr> -<tr> -<td style="text-align:left">CHW</td> -<td style="text-align:left">Community level</td> -<td style="text-align:left">Smartphones</td> -<td style="text-align:left">Equipped with CHT app for registrations, screening and monthly reports.</td> -</tr> -</tbody> -</table> -<h2 id="workflow-examples">Workflow Examples</h2> -<figure class="right col-12 col-lg-12"><a href="stock-management-workflow.png"> -<img src="stock-management-workflow.png"/> </a> -</figure>Apps: Reference Application for CHW Supervision and Performance Managementhttps://docs.communityhealthtoolkit.org/apps/examples/supervisor-reference-app/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/supervisor-reference-app/ -<div class="pageinfo pageinfo-primary"> -<p><a href="https://medic.org/">Medic</a> has worked with <a href="https://www.d-tree.org/">D-tree International</a> to build a CHT supervisor reference app. The app supports community health worker (CHW) supervisors to continually monitor and improve the program quality for Zanzibar National community health program Jamii ni Afya. The supervisor reference application is designed to enable supervisors to access CHW performance information and any other information required to supervise, mentor, and support CHWs to provide quality community health services. This reference app provides an example that CHT app developers can easily customize to meet the needs for their specific program areas to support CHW program management.</p> -<p>The code for this application will be available soon.</p> -</div> -<h2 id="problem-being-addressed">Problem being addressed</h2> -<p>Community Health Workers (CHWs) play a critical role in delivering quality care as part of the integrated primary health system, in some settings CHWs often serve as the only connection between the health system and the vulnerable and remote populations. For many community health programs, CHWs face a lot of challenges, this includes significant workloads, delivering health services to large and dispersed communities while being supported by limited and inadequate supervision. To ensure sustained positive impacts on CHW programmes, there is a need for supervisory interventions to be embedded within the broader community health system strengthening. The supervisor reference app empowers supervisors with the ability to monitor in real time the performance of CHWs which can guide supervisors to support CHWs in provision of community health care services.</p> -<h2 id="solution-overview">Solution overview</h2> -<p>The supervisor reference app workflows enables supervisors to: provide quality assurance supervisory activities, plan and document CHW supervisory activities, support CHW activities and monitor CHW performance in real time. The supervisor reference app has been designed to:</p> -<ul> -<li>Monitor the performance of the CHW and supervisors using the in-app aggregate target and the supervisor targets functionalities respectively.</li> -<li>Document and track CHW and supervisory activities.</li> -<li>Schedule CHW monthly meetings quality monitoring visits and follow up visits.</li> -<li>Support supervisors to report on health outcomes for their supervisory area.</li> -<li>Identify CHWs who have not been visited by a supervisor.</li> -<li>Follow up on CHW who have been inactive for 3 months.</li> -</ul> -<h2 id="forms-hierarchy">Forms hierarchy</h2> -<p>The diagram indicates forms and tasks that can be filled by a supervisor on the supervisor reference app. Some of the forms are accessible as actions on the supervisor and CHW profiles while others are accessible as tasks for a supervisor and a CHW.</p> -<figure><a href="hierarchy.png"> -<img src="hierarchy.png"/> </a> -</figure> -<h2 id="workflows">Workflows</h2> -<h3 id="chw-monthly-meetings">CHW monthly meetings</h3> -<p>A Supervisor conducts a CHW monthly meeting with all the CHWs. During the meeting the supervisor mentors the CHWs, reviews the CHWs performance and discusses with the CHWs the challenges they might be facing in their provision of community health services.</p> -<table> -<thead> -<tr> -<th>CHW monthly meeting workflow</th> -<th></th> -</tr> -</thead> -<tbody> -<tr> -<td> -<figure class="right"> -<img src="img1.png"/> -</figure> -</td> -<td> -<figure> -<img src="img2.png"/> -</figure> -</td> -</tr> -<tr> -<td><strong>1a Supervisor schedules for a CHW monthly meeting</strong> A supervisor schedules for a CHW monthly meeting by submitting a CHW monthly meeting</td> -<td><strong>2a CHW monthly meeting task</strong> The task appears 3 days before the due date and the supervisor completes the task during the monthly meeting</td> -</tr> -<tr> -<td><strong>1b The Supervisor can also receive a recurring CHW monthly meeting task every month</strong></td> -<td><strong>2b CHW monthly meeting form</strong> The CHW monthly meeting form can also be accessed through the action on the profile of the supervisor</td> -</tr> -<tr> -<td></td> -<td><strong>2c Schedule Monthly Meeting</strong> The Supervisor can schedule the next CHW meeting form using the CHW monthly meeting form or task</td> -</tr> -</tbody> -</table> -<h3 id="quality-monitoring">Quality monitoring</h3> -<p>With the supervisor reference app, a supervisor can schedule a quality monitoring visit to assess the quality of services being provided by a CHW. During the quality monitoring visit a supervisor shadows CHWs as they provide services to household members. A supervisor can schedule additional quality monitoring follow up visits for CHWs who are identified to have some weak areas and may need further support and mentorship from the supervisor.</p> -<table> -<thead> -<tr> -<th>Supervision quality monitoring workflow</th> -<th></th> -<th></th> -</tr> -</thead> -<tbody> -<tr> -<td> -<figure> -<img src="img1.png"/> -</figure> -</td> -<td> -<figure class="right"> -<img src="img1.png"/> -</figure> -</td> -<td> -<figure> -<img src="img2.png"/> -</figure> -</td> -</tr> -<tr> -<td><strong>1a Supervisor checks/monitors their in-app CHW aggregate targets</strong> The supervisor checks the in app targets to monitor CHW target progress.</td> -<td><strong>2a Quality Monitoring Planning form</strong> The supervisor schedules shadowing/quality monitoring visits with the CHWs that need support through the quality monitoring planning form</td> -<td><strong>3a Quality monitoring task</strong> The task appears 2 days before the due date. During the visit the supervisor completes the quality monitoring task</td> -</tr> -<tr> -<td><strong>1b Supervisor UHC mode</strong> Using the UHC mode, the supervisor is able to identify CHWs that have not been visited by a supervisor for a while</td> -<td></td> -<td><strong>3b Shadowing reminder task</strong> - the task will appear on the CHW app two days before the visit to remind the CHW of the planned shadowing visit</td> -</tr> -<tr> -<td></td> -<td></td> -<td><strong>3c Quality monitoring form</strong> The quality monitoring form can also be accessed as an action on the profile of the CHW</td> -</tr> -<tr> -<td></td> -<td></td> -<td><strong>3d Quality Monitoring Follow up task</strong> Just in case a supervisor identifies some weak areas that a CHW need to improve on and be supported, they schedule for a quality monitoring follow up visit</td> -</tr> -</tbody> -</table> -<h3 id="group-sessions">Group sessions</h3> -<table> -<thead> -<tr> -<th>CHW Group session workflow</th> -<th></th> -</tr> -</thead> -<tbody> -<tr> -<td> -<figure class="right"> -<img src="img1_1.png"/> -</figure> -</td> -<td> -<figure> -<img src="img3.png"/> -</figure> -</td> -</tr> -<tr> -<td><strong>1a A CHW organizes for a group session with community members</strong> Group session is a meeting coordinated and organized by CHW with community members to health educate community members of various health topics</td> -<td><strong>2a Group session form</strong> Supervisor attends the group session meetings, provides supervision, supports the activity and submits the group session form</td> -</tr> -</tbody> -</table> -<h3 id="chw-inactivity">CHW Inactivity</h3> -<p>A CHW activity task is generated for a supervisor if a CHW has been inactive for 3 months. This workflow enables a supervisor to follow up CHWs who have been inactive for three months and may need to be replaced.</p> -<h2 id="additional-features">Additional Features</h2> -<h3 id="targets">Targets</h3> -<p>A Supervisor has access to the analytics tab. The widgets on the in-app analytics tab enables a supervisor to view individual supervisor performance metrics and the supervisor progress towards the expected monthly goals. The supervisor also has access to CHW aggregate targets which will help the supervisor track metrics for individual CHWs, especially for supervisors overseeing a group of CHWs.This feature allows a supervisor to identify CHWs who may need to be supported actively.</p> -<h5 id="chw-aggregate-targets">CHW aggregate targets</h5> -<figure class="left col-10"><a href="CHW%20performance.png"> -<img src="CHW%20performance.png" -alt="CHW aggregate targets"/> </a> -</figure> -<h3 id="supervisor-uhc-mode">Supervisor UHC mode</h3> -<p>A supervisor is able to use this feature to view the last time each CHW was visited so that a supervisor can prioritize visiting CHWs who have not been visited for a while. The visit detail information is updated every time a quality monitoring form is submitted.</p> -<figure class="left col-9"><a href="Supervisor_UHC_mode.png"> -<img src="Supervisor_UHC_mode.png" -alt="Supervisor UHC mode"/> </a> -</figure> \ No newline at end of file +Examples and Reference Applications on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/examples/Recent content in Examples and Reference Applications on Community Health ToolkitHugo -- gohugo.ioenMaternal and Newborn Health Reference Apphttps://docs.communityhealthtoolkit.org/apps/examples/anc/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/anc/This &ldquo;reference application&rdquo; for maternal and newborn health provides a template for structuring and organizing your Community Health Toolkit digital health app, its configuration, and test code. It can be used as is, or serve as a great way to learn about the CHT&rsquo;s foundation for forms, data fields, and analytics that can be easily customized to fit your context. +Problem Being Addressed Access to quality maternal and newborn care is the cornerstone of many community health programs.Contact Tracinghttps://docs.communityhealthtoolkit.org/apps/examples/contact-tracing/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/contact-tracing/The CHT’s Contact Tracing functionality enables effective disease surveillance within communities to help control infectious disease outbreaks. It is a community public health tool that is designed to: +Centrally register patient cases and track contacts to prevent secondary spread of diseases in communities Create a coordinated approach to contact tracing within existing health systems Communicate the importance of self-isolation and symptom screening to exposed individuals and their families Problem Being Addressed An essential part of containing disease outbreaks, such as COVID-19, requires public health organizations to rapidly notify people who have come into contact with confirmed or suspected patient cases.Primary Health Care Adaptations for COVID-19https://docs.communityhealthtoolkit.org/apps/examples/phc-covid/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/phc-covid/Primary Health Care (PHC) workflows using the CHT are easily adaptable to help communities and facilities strengthen continuity of routine primary care services during COVID-19. Adapting PHC workflows is designed to: +Address disruptions to PHC delivery within communities while keeping CHWs and patients safe Offer remote patient and CHW support through call and text-first protocols Limit physical contact between patients, CHW’s, and facility providers by modifying existing health assessments Contain transmission via embedded COVID-19 symptom screening and referral protocols Problem Being Addressed Most primary health care programs are not designed to address the specific health needs of pandemics, such as COVID-19.COVID-19 Education and Training for CHWshttps://docs.communityhealthtoolkit.org/apps/examples/covid-education/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/covid-education/The COVID-19 pandemic has created unique challenges to providing in-person Community Health Worker training. To support CHWs, three learning modules were created to rapidly and remotely train them on COVID-19 safety protocols and patient care. Through CHT app and SMS deployments, Supervisors can now train CHWs on care workflows without being physically present. This example covers the following learning modules: +Health safety protocols for preventing the spread of COVID-19 Preventing the dissemination of misinformation about COVID-19 Recognizing COVID-19 and caring for patients with suspected infection Problem Being Addressed It is important for Community Health Workers to stay safe and serve their communities by understanding effective health protocols for preventing the spread of COVID-19.COVID-19 Testing with Rapid Diagnostic Testshttps://docs.communityhealthtoolkit.org/apps/examples/covid-rdt-reference-app/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/covid-rdt-reference-app/Medic has worked with FIND to build a CHT reference application for COVID-19 point-of-care testing with Rapid Diagnostic Tests (RDT). Using the reference app as an example, CHT app developers can easily include the provisioning and capture of RDT in workflows. These workflows can include third-party applications, like Dimagi&rsquo;s RD-Toolkit, that guide health workers through the use of the RDT. +You can find the code for this application in the CHT Core repository on GitHub.Direct-to-client, two-way texting workflows on CHThttps://docs.communityhealthtoolkit.org/apps/examples/direct-to-client/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/direct-to-client/This documentation provides a guide for designing and deploying direct-to-client (D2C), two-way texting (2wT) workflows to support client follow-up care using the community health toolkit (CHT). 2wT is a mobile text messaging system built on open-source, CHT tools to engage clients in their health care; to facilitate prompt client - healthcare provider interactions; to provide low-cost telehealth; and, to improve health care outcomes through the early identification of, and referral for, potential complications.Event Based Surveillancehttps://docs.communityhealthtoolkit.org/apps/examples/ebs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/ebs/The CHT’s Event Based Surveillance (EBS) functionality enables rapid capture of information about community events that are a potential risk to public health. Deployed at the community level, this functionality is designed to: +Achieve earliest possible detection of COVID-19 cases in communities Provide visibility into signals, reports, and investigations centrally for follow-up Coordinate and support local action based on existing roles in disease surveillance Problem Being Addressed Patient reporting through traditional primary health care channels is not well suited for detecting rapidly spreading outbreaks at a community scale.Learning and Carehttps://docs.communityhealthtoolkit.org/apps/examples/learning-care/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/learning-care/The Learning &amp; Care Apps in the Community Health Toolkit are designed to onboard community health workers remotely to new digital training tools, and help them learn new information and care delivery responsibilities through customized educational modules. +The modules can be deployed both (i) via the Android integration with the Academy App (powered by OppiaMobile) described in this documentation, and (ii) within the CHT core alone, to provide a seamless online &amp; offline experience for CHWs, supervisors, and government partners.Loss to Follow Uphttps://docs.communityhealthtoolkit.org/apps/examples/interoperability/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/interoperability/Loss to Follow Up Workflow (LTFU) This workflow describes a use case where a health facility or a requesting system generates a list of patients who have missed follow-up appointments that were made through the CHT. A CHW would then follow up with the listed patients through SMS, a physical visit or a phone call. +Problem Being Addressed Data exchange between the CHT and other systems has primarily been at peer-to-peer level.Pharmacovigilance Reference apphttps://docs.communityhealthtoolkit.org/apps/examples/pharmacovigilance-reference-app/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/pharmacovigilance-reference-app/Problem Being Addressed Self-medication, unregulated medical products, and counterfeit drugs have led to a significant increase in the prevalence of adverse drug reactions (ADRs), adverse effects following Immunization (AEFI), and the proliferation of poor-quality health products and technologies. ADRs are a common cause of hospital admissions and contribute to patient mortality, placing a substantial economic burden on resource-limited healthcare systems, especially in African countries. +Need for Pharmacovigilance: Pharmacovigilance focuses on the detection, assessment, understanding and prevention of adverse effects and other potential drug-related problems.YendaNafe CHT app by PIH in Malawihttps://docs.communityhealthtoolkit.org/apps/examples/pih/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/pih/Since 2017, Partners in Health (PIH) Malawi and Medic have collaboratively co-designed and developed YendaNafe, a digital health app for community based service provision. In the spirit of openness, Medic and PIH have coordinated the release of the full application source code of Yendanafe app as first of kind ‘‘Integrated CHT Reference app’’. This Reference app provides an example that CHT Implementers can learn how they can design and configure integrated workflows.Remote Onboarding and Traininghttps://docs.communityhealthtoolkit.org/apps/examples/training/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/training/The CHT’s Remote Onboarding and Training functionality enables Supervisors and Administrators to train CHWs on care workflows and related app use without being physically present. It is designed for: +Safety: maintaining distance due to infectious disease Speed: faster deployment when timing is a critical Scalability: onboarding large numbers of users at the same time Measurability: evaluation to provide added support where needed Adaptability: integration with existing program and workflow structures Problem Being Addressed Providing consistent training for CHWs is critically important in the context of evolving health programs and use of digital support tools.Stock Monitoringhttps://docs.communityhealthtoolkit.org/apps/examples/stock-monitoring/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/stock-monitoring/Problem Being Addressed Paper based commodity management systems are prone to errors due to reliance on manually updated registers, this greatly affects data quality. Further, it is time consuming for CHWs to reference stock balances and at the same time update stocks on the commodity management sheet while providing treatment to household members. In terms of supervision, CHW’s supervisors do not know when CHWs have stock outs in time for replenishing and ordering purposes.Reference Application for CHW Supervision and Performance Managementhttps://docs.communityhealthtoolkit.org/apps/examples/supervisor-reference-app/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/supervisor-reference-app/Medic has worked with D-tree International to build a CHT supervisor reference app. The app supports community health worker (CHW) supervisors to continually monitor and improve the program quality for Zanzibar National community health program Jamii ni Afya. The supervisor reference application is designed to enable supervisors to access CHW performance information and any other information required to supervise, mentor, and support CHWs to provide quality community health services. This reference app provides an example that CHT app developers can easily customize to meet the needs for their specific program areas to support CHW program management. \ No newline at end of file diff --git a/apps/examples/interoperability/index.html b/apps/examples/interoperability/index.html index 7c619842e6..1b8e3bde07 100644 --- a/apps/examples/interoperability/index.html +++ b/apps/examples/interoperability/index.html @@ -1,9 +1,9 @@ -Loss to Follow Up | Community Health Toolkit +Loss to Follow Up | Community Health Toolkit

Loss to Follow Up

Generating patients with missed follow-up appointments between CHT and requesting system

Loss to Follow Up Workflow (LTFU)

This workflow describes a use case where a health facility or a requesting system generates a list of patients who have missed follow-up appointments that were made through the CHT. A CHW would then follow up with the listed patients through SMS, a physical visit or a phone call.

Problem Being Addressed

Data exchange between the CHT and other systems has primarily been at peer-to-peer level. This means that the integration is built to meet the specific need in the unique scenarios. This presents a problem during maintenance and scalability as there are no defined standards that have been used. Interoperability allows technical teams to scale in an efficient and repeatable manner due to the already predefined standards.

Solution Overview

This is a model for interoperability that can be used for Loss to Follow up flows between the CHT and a health facility or a requesting system. Community health workers routinely follow up on patients physically at their residences or place of work. In instances where the patient needs to visit a health facility for routine checkups or specialized care, the CHW has no visibility of the process outside the CHT. The interoperability layer built allows other systems including health facilities to exchange data in a standardized format.

Intended Users

The intended users of the interoperable systems are CHWs on the CHT side and Healthcare Givers on the requesting system side. System administrators can access the mediator on the administration console.

See the sequence diagram below showcasing the with the loss to follow up flow:

The Interoperating Systems:

  • CHT: Community Health Toolkit.
  • OpenHIM: Mediator (middleware).
  • Requesting System: For testing purposes, we used HTTP Requests.

OpenHIM was chosen as the main component of the interoperability layer and custom mediators were built to deal with the different workflows as it provides a central point of control for managing data exchange and security.

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/examples/learning-care/index.html b/apps/examples/learning-care/index.html index 6168623eb8..0de1f16008 100644 --- a/apps/examples/learning-care/index.html +++ b/apps/examples/learning-care/index.html @@ -1,9 +1,9 @@ -Learning and Care | Community Health Toolkit +Learning and Care | Community Health Toolkit

Learning and Care

An integration built to pilot the integrated workflows focused on CHW remote learning and care support for COVID-19.

The Learning & Care Apps in the Community Health Toolkit are designed to onboard community health workers remotely to new digital training tools, and help them learn new information and care delivery responsibilities through customized educational modules.

The modules can be deployed both (i) via the Android integration with the Academy App (powered by OppiaMobile) described in this documentation, and (ii) within the CHT core alone, to provide a seamless online & offline experience for CHWs, supervisors, and government partners.

Problem Being Addressed

The Community Health Academy at Last Mile Health, with a range of content partners including Digital Medic, is building a library of open-access learning materials for community health workers.

With the spread of COVID-19, it is imperative that access to learning, integrated with care protocols, routine task and care workflows, is available to CHWs without relying on face-to-face training.

Solution Overview

The Community Health Academy and Medic set out to deliver a fully-integrated Learning and Care solution, with targeted COVID-19 learning experiences as a global public good.

This integrated Learning & Care App leverages the remote onboarding, task & scheduling, and target features of the CHT core framework with the curated, multimedia educational content from the Academy’s COVID-19 library.

Users and Hierarchy Example

UserLocationDevicesRole
CHWCommunity levelFeature phone or smartphoneReceive notifications about new courses, complete modules and educational assessments; provide feedback and ask for support where needed; discuss content and quizzes with supervisors; support fellow CHWs in onboarding & modules.
CHW SupervisorsCommunity level, based at facilitiesSmartphones and personal phonesMonitor progress & ensure CHWs complete modules and quizzes; liaise with local government partners to be aware of policy updates, and to share CHW training progress & implementation feedback; view customize dashboards and provide support to CHW users.
Government & Local Implementing PartnersCentral and district officesDesktop, laptop and smartphonesMonitor progress of catchment area CHWs and supervisors through analytics & dashboards, provide up-to-date information on country-wide guidelines & global best practices (e.g., from WHO).

Workflow Examples

COVID-19 Learning and Care Supported by the CHT

Integrated Learning and Care Apps COVID-19 Supported by the CHT and OppiaMobile


More background information can be found in this summary deck


CHT Applications > + Create project issue

Learning and Care

An integration built to pilot the integrated workflows focused on CHW remote learning and care support for COVID-19.

The Learning & Care Apps in the Community Health Toolkit are designed to onboard community health workers remotely to new digital training tools, and help them learn new information and care delivery responsibilities through customized educational modules.

The modules can be deployed both (i) via the Android integration with the Academy App (powered by OppiaMobile) described in this documentation, and (ii) within the CHT core alone, to provide a seamless online & offline experience for CHWs, supervisors, and government partners.

Problem Being Addressed

The Community Health Academy at Last Mile Health, with a range of content partners including Digital Medic, is building a library of open-access learning materials for community health workers.

With the spread of COVID-19, it is imperative that access to learning, integrated with care protocols, routine task and care workflows, is available to CHWs without relying on face-to-face training.

Solution Overview

The Community Health Academy and Medic set out to deliver a fully-integrated Learning and Care solution, with targeted COVID-19 learning experiences as a global public good.

This integrated Learning & Care App leverages the remote onboarding, task & scheduling, and target features of the CHT core framework with the curated, multimedia educational content from the Academy’s COVID-19 library.

Users and Hierarchy Example

UserLocationDevicesRole
CHWCommunity levelFeature phone or smartphoneReceive notifications about new courses, complete modules and educational assessments; provide feedback and ask for support where needed; discuss content and quizzes with supervisors; support fellow CHWs in onboarding & modules.
CHW SupervisorsCommunity level, based at facilitiesSmartphones and personal phonesMonitor progress & ensure CHWs complete modules and quizzes; liaise with local government partners to be aware of policy updates, and to share CHW training progress & implementation feedback; view customize dashboards and provide support to CHW users.
Government & Local Implementing PartnersCentral and district officesDesktop, laptop and smartphonesMonitor progress of catchment area CHWs and supervisors through analytics & dashboards, provide up-to-date information on country-wide guidelines & global best practices (e.g., from WHO).

Workflow Examples

COVID-19 Learning and Care Supported by the CHT

Integrated Learning and Care Apps COVID-19 Supported by the CHT and OppiaMobile


More background information can be found in this summary deck


CHT Applications > Features > Integrations > OppiaMobile

Integrate CHT core with OppiaMobile’s learning management platform

CHT Applications > Quick Guides > Integrations > OppiaMobile

Components & configuration

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/examples/pharmacovigilance-reference-app/index.html b/apps/examples/pharmacovigilance-reference-app/index.html index 732c392a81..56bb9812e6 100644 --- a/apps/examples/pharmacovigilance-reference-app/index.html +++ b/apps/examples/pharmacovigilance-reference-app/index.html @@ -1,9 +1,9 @@ -Pharmacovigilance Reference app | Community Health Toolkit +Pharmacovigilance Reference app | Community Health Toolkit

Pharmacovigilance Reference app

CHT example application that supports pharmacovigilance in a community setting.

Problem Being Addressed

Self-medication, unregulated medical products, and counterfeit drugs have led to a significant increase in the prevalence of adverse drug reactions (ADRs), adverse effects following Immunization (AEFI), and the proliferation of poor-quality health products and technologies. ADRs are a common cause of hospital admissions and contribute to patient mortality, placing a substantial economic burden on resource-limited healthcare systems, especially in African countries.

Need for Pharmacovigilance: Pharmacovigilance focuses on the detection, assessment, understanding and prevention of adverse effects and other potential drug-related problems. Pharmacovigilance facilitates early detection of unknown adverse reactions, identifies increases in frequency of known adverse reactions and helps disseminate information to improve drug prescription and regulation. To address these challenges, there is a crucial need for a robust pharmacovigilance system that enables the detection, reporting, and mitigation of adverse drug reactions and incidents.

Solution Overview

The CHT Pharmacovigilance Reference app is designed and developed by IntelliSOFT Consulting LTD. Components of Kenya’s Pharmacovigilance Electronic Reporting System have been integrated into the Community Health Toolkit (CHT) to support community health providers (CHPs) and Community Health Assistants (CHAs) to efficiently identify, report, refer and manage ADRs and AEFI while delivering healthcare.

In the spirit of openness and as a contribution to the CHT open source project, Medic and IntelliSOFT have coordinated to provide the source code and documentation of the workflows of the CHT Pharmacovigilance Reference app. + Create project issue

Pharmacovigilance Reference app

CHT example application that supports pharmacovigilance in a community setting.

Problem Being Addressed

Self-medication, unregulated medical products, and counterfeit drugs have led to a significant increase in the prevalence of adverse drug reactions (ADRs), adverse effects following Immunization (AEFI), and the proliferation of poor-quality health products and technologies. ADRs are a common cause of hospital admissions and contribute to patient mortality, placing a substantial economic burden on resource-limited healthcare systems, especially in African countries.

Need for Pharmacovigilance: Pharmacovigilance focuses on the detection, assessment, understanding and prevention of adverse effects and other potential drug-related problems. Pharmacovigilance facilitates early detection of unknown adverse reactions, identifies increases in frequency of known adverse reactions and helps disseminate information to improve drug prescription and regulation. To address these challenges, there is a crucial need for a robust pharmacovigilance system that enables the detection, reporting, and mitigation of adverse drug reactions and incidents.

Solution Overview

The CHT Pharmacovigilance Reference app is designed and developed by IntelliSOFT Consulting LTD. Components of Kenya’s Pharmacovigilance Electronic Reporting System have been integrated into the Community Health Toolkit (CHT) to support community health providers (CHPs) and Community Health Assistants (CHAs) to efficiently identify, report, refer and manage ADRs and AEFI while delivering healthcare.

In the spirit of openness and as a contribution to the CHT open source project, Medic and IntelliSOFT have coordinated to provide the source code and documentation of the workflows of the CHT Pharmacovigilance Reference app. This reference app serves as an example application that other CHT Implementers can use to learn how to build pharmacovigilance workflows to help improve patient safety for the community members they serve.

Key Pharmacovigilance Areas Supported

  1. Adverse Drug Reactions (ADRs): detect and report ADRs resulting from pharmaceutical products.
  2. Adverse Effects Following Immunization (AEFI): monitor and report adverse effects following immunization.
  3. Poor Quality Health Products and Technologies: identify and report incidents related to the use of poor-quality health products and technologies.
  4. Death Monitoring: Monitor, identify, report and record deaths resulting from monitored conditions within household members in the Pharmacovigilance application.

Users and hierarchy

Pharmacovigilance workflows

Reporting, referral and follow up of patients

During a household visit, a CHP uses the pharmacovigilance app to assess household members for suspected severe adverse reactions to medication or recent vaccinations/immunizations. Using the assessment form, a CHP records any suspicious issues with the medication, such as unusual colour, smell, or packaging and adverse drug effects from medication and immunizations.

A public report task is generated for a CHA once a CHP submits a suspected case of adverse reaction. The public report form captures information about the adverse effects and medication including side effects, onset date, and current status of the reaction. Additionally, the form captures medication details such as name of the medicine, manufacturer, purchase location, start date, stop date, and expiry date. In case of a variety of medicines, the form allows a CHA to perform multiple entries.

The CHA also uses the public report to record outcome details, including outcome specification and recovery status. Using the public report form, the CHA is able to refer household members to the nearest facility for further review and care, this action generates a referral follow up task for the CHP. The referral follow up task appears 7 days after facility referral and guides the CHP to follow up referred cases to confirm if patients attended the referral . The CHP can also check the condition of patients and once they complete the referral follow up task, the CHA gets patient status notification task.

Death reporting and confirmation workflow

A CHP uses the assessment form to report a death caused by an adverse drug reaction, vaccination/immunization, or poor quality medicine; submission of a death report triggers a death confirmation task for a CHA.

Resources to Get Started

Here are a few additional resources to help get you started with the pharmacovigilance health reference application.

The open sharing of digital health apps used by CHWs is a monumental milestone in the digital health space, and for the CHT Community. Reach out on the forum to share how you will leverage these resources, along with your feedback and continued innovations that could benefit the larger community.

-

\ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/examples/phc-covid/index.html b/apps/examples/phc-covid/index.html index 1046076dfa..504c24675b 100644 --- a/apps/examples/phc-covid/index.html +++ b/apps/examples/phc-covid/index.html @@ -1,9 +1,9 @@ -Primary Health Care Adaptations for COVID-19 | Community Health Toolkit +Primary Health Care Adaptations for COVID-19 | Community Health Toolkit

Primary Health Care Adaptations for COVID-19

Primary health care workflow and training adaptations for COVID-19

Primary Health Care (PHC) workflows using the CHT are easily adaptable to help communities and facilities strengthen continuity of routine primary care services during COVID-19. Adapting PHC workflows is designed to:

  • Address disruptions to PHC delivery within communities while keeping CHWs and patients safe
  • Offer remote patient and CHW support through call and text-first protocols
  • Limit physical contact between patients, CHW’s, and facility providers by modifying existing health assessments
  • Contain transmission via embedded COVID-19 symptom screening and referral protocols

Problem Being Addressed

Most primary health care programs are not designed to address the specific health needs of pandemics, such as COVID-19. For CHWs and the communities they support, the impact of the pandemic has resulted in disruptions to much needed primary health care services, such as antenatal and postnatal care. This is particularly devastating for already vulnerable populations, such as patients who live in places that lack robust health care infrastructure or have co-morbidities, who face increased challenges when CHWs and care facilities are unable to provide adequate services.

In response, health program administrators are rapidly adapting digitally supported PHC programs to help respond to immediate patient and community health needs. Relying on CHWs and Supervisors to quickly learn and adhere to new care workflows on their own is a major challenge to successfully modifying PHC health programs to impact patients quickly and maintain the safety of health workers.

Solution Overview

Maintaining primary health care is critical both in slowing the spread of COVID-19 and managing the care of patients at home with moderate cases, as well as ensuring that disruptions to PHC are addressed and that routine care is supported during the pandemic. Building on the success of existing digitally-supported health programs and CHW patient relationships enables more effective responses to the specific health needs of their communities.

Through adaptations to PHC workflows, health program administrators are able to:

  • Support COVID-19 prevention, detection, and containment efforts within communities
  • Create a coordinated approach that safely serves health workers, facilities, and communities
  • Support home-based care for non-emergent COVID-19 cases to reduce overburdening on hospital systems
  • Capture program learnings to create more robust and resilient health systems beyond supporting the immediate COVID-19 response

Users and Hierarchy Example

UserLocationDevicesRole
Program StaffCentral, district or local officesDesktop, laptop and smartphonesOversee delivery of routine PHC services. Continually monitor care delivery for disruptions. Ensure compliance with program-specific, national and global care protocols.
CHW SupervisorCommunity level, based at facilitiesSmartphone and personal phoneOversee CHWs and track completion of their remote training modules. Follow up with CHWs needing extra support.
CHWCommunity levelFeature phone or smartphoneProvide care to community using adapted PHC workflows. Complete remote training for new workflows. Enable home-based care and offer remote support through call and text-first protocols.
PatientCommunity levelSeek care for non-emergent conditions. Examples: maternal and child health, other infectious diseases, non-communicable diseases.

Workflow Example

This demo illustrates how a CHT workflow that currently supports PHC can be easily adapted to reflect a COVID-19 health program response. Remote support allows for the integration of new care protocols that follow “no touch” or “visual only” reviews, transition to call/text-first protocols, and embedding COVID-19 symptom screening within PHC workflows.


More background information can be found in this summary deck.


CHT Applications > + Create project issue

Primary Health Care Adaptations for COVID-19

Primary health care workflow and training adaptations for COVID-19

Primary Health Care (PHC) workflows using the CHT are easily adaptable to help communities and facilities strengthen continuity of routine primary care services during COVID-19. Adapting PHC workflows is designed to:

  • Address disruptions to PHC delivery within communities while keeping CHWs and patients safe
  • Offer remote patient and CHW support through call and text-first protocols
  • Limit physical contact between patients, CHW’s, and facility providers by modifying existing health assessments
  • Contain transmission via embedded COVID-19 symptom screening and referral protocols

Problem Being Addressed

Most primary health care programs are not designed to address the specific health needs of pandemics, such as COVID-19. For CHWs and the communities they support, the impact of the pandemic has resulted in disruptions to much needed primary health care services, such as antenatal and postnatal care. This is particularly devastating for already vulnerable populations, such as patients who live in places that lack robust health care infrastructure or have co-morbidities, who face increased challenges when CHWs and care facilities are unable to provide adequate services.

In response, health program administrators are rapidly adapting digitally supported PHC programs to help respond to immediate patient and community health needs. Relying on CHWs and Supervisors to quickly learn and adhere to new care workflows on their own is a major challenge to successfully modifying PHC health programs to impact patients quickly and maintain the safety of health workers.

Solution Overview

Maintaining primary health care is critical both in slowing the spread of COVID-19 and managing the care of patients at home with moderate cases, as well as ensuring that disruptions to PHC are addressed and that routine care is supported during the pandemic. Building on the success of existing digitally-supported health programs and CHW patient relationships enables more effective responses to the specific health needs of their communities.

Through adaptations to PHC workflows, health program administrators are able to:

  • Support COVID-19 prevention, detection, and containment efforts within communities
  • Create a coordinated approach that safely serves health workers, facilities, and communities
  • Support home-based care for non-emergent COVID-19 cases to reduce overburdening on hospital systems
  • Capture program learnings to create more robust and resilient health systems beyond supporting the immediate COVID-19 response

Users and Hierarchy Example

UserLocationDevicesRole
Program StaffCentral, district or local officesDesktop, laptop and smartphonesOversee delivery of routine PHC services. Continually monitor care delivery for disruptions. Ensure compliance with program-specific, national and global care protocols.
CHW SupervisorCommunity level, based at facilitiesSmartphone and personal phoneOversee CHWs and track completion of their remote training modules. Follow up with CHWs needing extra support.
CHWCommunity levelFeature phone or smartphoneProvide care to community using adapted PHC workflows. Complete remote training for new workflows. Enable home-based care and offer remote support through call and text-first protocols.
PatientCommunity levelSeek care for non-emergent conditions. Examples: maternal and child health, other infectious diseases, non-communicable diseases.

Workflow Example

This demo illustrates how a CHT workflow that currently supports PHC can be easily adapted to reflect a COVID-19 health program response. Remote support allows for the integration of new care protocols that follow “no touch” or “visual only” reviews, transition to call/text-first protocols, and embedding COVID-19 symptom screening within PHC workflows.


More background information can be found in this summary deck.


CHT Applications > Examples > Remote Training

App and care workflow training using remote capabilities.

CHT Applications > Concepts > Workflows

Building connections between people, actions, and data systems

CHT Applications > Concepts > Care Guides

Taking health workers through care protocols and providing decision support

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/examples/pih/index.html b/apps/examples/pih/index.html index d1471ff406..b5008066b0 100644 --- a/apps/examples/pih/index.html +++ b/apps/examples/pih/index.html @@ -1,9 +1,9 @@ -YendaNafe CHT app by PIH in Malawi | Community Health Toolkit +YendaNafe CHT app by PIH in Malawi | Community Health Toolkit

YendaNafe CHT app by PIH in Malawi

The YendaNafe app co-designed by PIH Malawi and Medic

Since 2017, Partners in Health (PIH) Malawi and Medic have collaboratively co-designed and developed YendaNafe, a digital health app for community based service provision. In the spirit of openness, Medic and PIH have coordinated the release of the full application source code of Yendanafe app as first of kind ‘‘Integrated CHT Reference app’’. This Reference app provides an example that CHT Implementers can learn how they can design and configure integrated workflows.

Problem being addressed

Malawi is one of the countries with a high prevalence of HIV, high rates of infant and maternal mortality and a high burden of non communicable diseases (NCD) and tuberculosis. To help respond to community needs and reduce the disease burden, PIH in partnership with the Ministry of Health are implementing an integrated household model in Neno District. Under the model, community health workers (CHWs) and CHW supervisors have been equipped with the YendaNafe app, the app supports the health care workers to provide integrated community based health care services and to coordinate care.

Solution overview

The YendaNafe app workflows support CHWs to conduct integrated disease screening, provide health care services, refer people in communities to facilities especially those who require facility based care and raise community awareness on health. The app is designed to support the following key health areas:

  • Maternal child health (antenatal care and postnatal care)
  • Family planning (FP)
  • Malnutrition
  • Immunization
  • Integrated management of childhood illness (IMCI)
  • Human Immunodeficiency Virus (HIV)
  • Tuberculosis (TB)
  • Non communicable diseases (NCDs)

Users and hierarchy

UsersLocationDevicesRoles
Community Health Directoradmin levelLaptop or desktopStaff at this level will be given access to dashboards where they can monitor program indicators. Online only access to the app.
CHW Manager and Officersadmin levelLaptop or desktopStaff at this level will be given access to dashboards where they can monitor program indicators. Online only access to the app.
Site Supervisors (SS)admin levelLaptop or desktopStaff at this level will be given access to dashboards where they can monitor program indicators. Online only access to the app.
Senior CHWs (SCHW) - Supervisory roleFacility and community levelsSmartphoneThey supervise CHW and mentor CHWs, collect sputum for probable TB cases. They are offline users.
CHWsCommunity levelSmartphoneCHWs register households, conduct household visits, case screening, referrals, follow-ups, and defaulter tracing. They are offline users.

Workflows

Maternal neonatal health workflow

This workflow consists of the pregnancy, delivery and postnatal workflows.The pregnancy workflow enables CHWs to register new pregnancies, screen pregnant mothers for danger signs and follow up pregnant mother to remind them to attend the scheduled ANC clinic appointments. The postnatal (PNC) workflow supports CHWs to follow up PNC women and newborns for danger signs screening and refer PNC women with danger signs to facilities to receive more care.

Pregnancy workflow

Condition

Condition

Task

Task

Resolution

Resolution

Condition

Upon discovering a suspected pregnancy, a CHW escorts the woman to a health facility for a pregnancy test and submits a Pregnancy Screening Form.

Task

A Pregnancy Confirmation and Referral Follow-up Task will appear immediately and is due 3 days later. The task is to remind the CHW to confirm referral attendance and pregnancy status.

Resolution

The woman visits the health facility, gets a pregnancy test and starts ANC clinic.

Condition

In case of a confirmed pregnancy(woman has started ANC visits) the CHW submits a Pregnancy Registration Form with gestational age and facility EDD if available.

Task

Every first day of the month, the CHW receives Pregnancy Follow-Up Task to remind that it is time to check into pregnant mother. The tasks continue for 42 weeks.

Resolution

The CHW submits Pregnancy Follow-Up Form demonstrating that she provided ANC counseling, gathered information from prior facility visits, screened for danger signs and reminded the woman of her upcoming facility ANC visit.

Condition

If the CHW notices danger signs at any time, she submits a Danger Sign Screening Form and immediately refers or accompanies the patient to the facility depending on severity.

Task

A Danger Sign Follow-Up Task will appear immediately and is due 3 days later. Tasks persist for 7 days after due date.

Resolution

CHW submits a Danger Sign Follow-Up Form, verifying that she visited the woman to confirm that she attended the facility.

Delivery workflow

Condition

Condition

Task

Task

Resolution

Resolution

Condition

A currently registered pregnant person has reached a gestational age of 42 weeks and has not had a miscarriage or a delivery reported.

Task

A Delivery Task requesting that the CHW check in on the woman to see whether she has delivered. Task appears from 38 weeks.

Resolution

If the woman has delivered and is available at home, CHW submits Delivery Form, confirming the pregnancy outcomes. Profiles are created for each baby that is alive. This “ends” the pregnancy workflow.

Condition

When a woman delivers and is discharged from the health facility, a health facility staff (Site Supervisor) submits a Delivery Discharge Form.

Task

CHW receives a Delivery Report Task to inform them to visit the mother and report the delivery.

Resolution

CHW visits the mother, submits a Delivery Form, confirming the pregnancy outcomes. Profiles are created for each baby that is alive. This “ends” the pregnancy workflow.

Postnatal workflow

Condition

Condition

Task

Task

Resolution

Resolution

Condition

A woman delivers and CHW submits a Delivery Report.

Task

A PNC Follow Up Task for both mother and baby appear on the 3rd and 5th day post delivery. The task is to remind the CHW to visit the postnatal mother.

Resolution

CHW visits the mother and baby, submits a PNC Follow Up Task confirming that they visited the mother and/or baby, screened for danger and reminded them of upcoming facility appointment.

Condition

If the CHW notices danger signs at any time for either mother or baby, she submits a Danger Sign Form and immediately refers to the facility depending on severity.

Task

A Danger Sign Follow-Up Task will appear within 2 days and is due 3 days later. Tasks persists for 7 days after due date.

Resolution

CHW submits a Danger Sign Follow-Up Form, verifying that she visited the woman/child to confirm that she attended the facility.

Integrated management of childhood illness (IMCI) workflow

This workflow is designed to support CHWs to identify symptomatic children at the household level, refer symptomatic children and conduct on-time follow ups for children through the follow up tasks

IMCI workflow

Condition

Condition

Task

Task

Resolution

Resolution

Condition

CHW visits, educates, and screens under 5 for cough, diarrhea, fast breathing, fever, and fever with convulsions. CHW submits a IMCHI Screening Form and refers to the health facility if necessary.

Task

A IMCI Referral Follow-up Task will appear within 2 days and is due 3 days later. The task is to remind the CHW to confirm referral attendance.

Resolution

A IMCI Referral Follow-up Form, verifying that she visited the child to confirm referral completion and offer adherence counselling or remind on upcoming appointments.

Malnutrition workflow

The workflow supports CHWs to assess and identify malnourished children, refer them to a health facility and conduct on time follow ups.

Malnutrition workflow

Condition

Condition

Task

Task

Resolution

Resolution

Condition

CHW counsels and screens under 5 for malnutrition using MUAC tape and danger signs. If suspected to be malnourished, the child is referred to the health facility. CHW submits a Malnutrition Screening Form.

Task

A Malnutrition Referral Follow-up Task will appear within 2 days and is due 3 days later. The task is to remind the CHW to confirm referral attendance.

Resolution

CHW submits Malnutrition Referral Follow up Form verifying that she visited the child to confirm that he was taken to health facility.

Condition

If Child is malnourished, he/she is enrolled into malnutrition program either Outpatient Therapeutic Programme (OTP) or Supplementary Feeding Programme (SFP).

Task

On the first day of every month, the CHW receives Malnutrition Treatment Follow-Up Task to remind them that it is time to check on the child. The task will continue showing till CHW indicates that the child has been exited from malnutrition programme.

Resolution

The CHW submits Malnutrition Treatment Follow-Up Form demonstrating that she provided adherence counselling, and reminded the caregiver of the child’s upcoming facility visit.

Family planning workflow

The FP workflow ensures that eligible women receive their FP needs with support from the CHWs. The FP workflow supports CHWs to counsel eligible women on FP, refer women to facilities for FP services screen FP clients for side effects and refer and ensure ontime FP renewals.

Family planning workflow

Condition

Condition

Task

Task

Resolution

Resolution

Condition

Upon discovering a woman who is not pregnant and not on FP, the CHW provides FP education, refers them to a health facility if necessary and submits a Family Planning Screening Form.

Task

A Pregnancy Confirmation and FP Follow-up Task will appear 2 days and is due 3 days later. The task reminds the CHW to confirm referral attendance and FP status.

Resolution

The woman visits the health facility and is started on FP if eligible.

Condition

A woman is found to be already on FP, the CHW records FP method, screen for side effects and counsel on adherence to method and clinic appointments.

Task

Every first day of the month, the CHW receives Family Planning Follow-Up Task to remind that it is time to check on the woman.

Resolution

The CHW submits Family Planning Follow-Up Form demonstrating that she screened for side effects, need to change FP methods, offered adherence counselling and remind the woman of her upcoming appointment.

Condition

For women on long term FP methods either IUCD or implant, the CHW records the FP expiry date.

Task

One month to the expiry date the CHW receives FP Expiry Follow-Up Task to remind the woman to renew their FP. Tasks persists for 30 days past the due date.

Resolution

CHW submits a FP Expiry Follow-Up Form, verifying that she visited the woman to confirm that she attended the facility for either renewal or change of FP method.

TB workflows

The TB workflow enables CHWs to screen patients for TB, refer suspected TB cases to senior CHW for sputum collection and follow up TB patients. Senior CHWs who are based at the community level submit the sputum collection form after collecting the sputum samples for TB testing and the Site supervisors who are based at the facility level support in notifying CHWs when the TB results are out and in tracing TB defaulters.

TB workflow for TB suspected cases identified in a community setting

Condition

Condition

Task

Task

Resolution

Resolution

Condition

CHW educates and screens over 5’s for TB using the cardinal signs. CHW refers any suspected person to the Senior CHW for sputum collection and submits a TB Screening Form.

Task

A Sputum Collection Task will appear immediately on the SCHW device. The task is to inform the SCHW to collect sputum from the referred person.

Resolution

SCHW submits Sputum Collection Form demonstrating that she collected the sputum from the referred person and submitted the samples to the health facility.

Condition

When the TB results are ready, a Site Supervisors who is based at the health facility submits TB Results Form detailings the TB results outcome.

Task

A TB Results Task will appear immediately on the CHW device while TB results Notification Task will appear on the SCHW device.

Resolution

The TB Results task indicates the TB results outcome: positive, negative, or rejected. The SCHW submits TB results notification form demonstrating that they have updated the TB results in the paper based cough register.

Condition

Person is found to be TB positive. CHW refers the person to the health facility for TB treatment commencement

Task

A TB Referral Follow-Up Task will appear within 2 days on the CHW device and is due 3 days later. The task is meant to inform the CHW that it’s time to follow up on the person to confirm referral attendance.

Resolution

The CHW submits TB Follow-Up Form demonstrating that she visited the person to confirm if she went to the health facility. CHW refers other household members to health facility for TB contact tracing.

TB workflow for TB confirmed cases and TB defaulters

Condition

Condition

Task

Task

Resolution

Resolution

Condition

Person is on started on TB treatment.

Task

A CHW visits the person daily and submits Daily Follow up Form. This will continue till the person is cured and CHW submits TB exit form.

Resolution

The CHW submits TB Daily Follow-Up Form demonstrating that she provided adherence counselling, screened for danger signs/side effects and reminded the person of upcoming facility visits.

Condition

A person walks into health facility without CHW referral and is enrolled into TB program. Site Supervisor submits a TB Enrollment Form indicating person has been enrolled in TB program.

Task

A TB Follow-Up Task will appear immediately on the CHW device. The task is meant to inform the CHW that the person has been enrolled in TB program and needs their follow up.

Resolution

The CHW submits TB Daily Follow-Up Form demonstrating that she provided adherence counselling, screened for danger signs/side effects and reminded the person of upcoming facility visits.

Condition

A TB Client defaults on their clinic visit. Site Supervisor submits Trace Report indicating the missed visit details.

Task

A TRACE Follow-Up Task will appear immediately on the CHW device. The task is meant to inform the CHW that the person missed their clinic visit.

Resolution

The CHW submits TRACE Follow-Up Form demonstrating that she visited the TB patient and encouraged them to visit the health facility.

Non communicable diseases workflow

The non communicable diseases workflows support health care workers to screen for NCD symptoms, refer suspected cases to facilities and trace clients who have defaulted treatment.

Condition

Condition

Task

Task

Resolution

Resolution

Condition

CHW educates and screens over 5’s for NCD symptoms. CHW refers any suspected person to the health facility and submits a NCD Screening Form.

Task

A NCD referral Follow-up Task will appear within 2 ays and is due 3 days later. The task is to remind the CHW to confirm referral attendance.

Resolution

CHW submits NCD Referral Follow-up Form verifying that she visited the person to confirm that he went to the health facility.

Condition

Person is to found to have either NCD (hypertension, diabetes, epilepsy, asthma, mental health or heart failure) and is enrolled into NCD program.

Task

Every first day of the month, CHW receives NCD Monthly Treatment Follow-up Task to remind the CHW it’s time to check on the person.

Resolution

The CHW submits NCD Monthly Treatment Follow-up Form demonstrating that she provided adherence counselling, screened for danger signs/side effects and reminded the person of upcoming facility visits.

Condition

A person walks into health facility without CHW referral and is enrolled into NCD program. Site Supervisor submits a Treatment Enrollment Form indicating person has been enrolled in NCD program.

Task

A NCD Monthly Treatment Follow-up Task will appear immediately on the CHW device. The task is meant to inform the CHW that the person has been enrolled in NCD program and needs their follow up.

Resolution

The CHW submits Monthly Treatment Follow-up Form demonstrating that she provided adherence counselling, screened for danger signs/side effects and reminded the person upcoming facility visits.
Condition

Condition

Task

Task

Resolution

Resolution

Condition

If NCD patient reports danger sign during the follow ups, the CHW immediately refers or accompanies the patient to the facility depending on severity of the danger sign.

Task

A NCD Danger Signs Follow-up Task will appear within 2 days and is due 3 days later. The task is to remind the CHW to confirm referral attendance.

Resolution

The CHW submits a NCD Danger Sign Follow-up Form verifying that she visited the NCD patient to confirm that she attended the facility.

Condition

A NCD Client defaults on their clinic visit. Site Supervisor submits Trace Report indicating the missed visit details.

Task

A TRACE Follow-up Task will appear immediately on the CHW device. The task is meant to inform the CHW that the person missed their clinic visit.

Resolution

The CHW submits TRACE Follow-up Form demonstrating that she visited the NCD patient and encouraged them to visit the health facility.

Human Immunodeficiency Virus workflow

The Human Immunodeficiency Virus (HIV) workflow is designed to guide CHWs in HIV screening of household members, screening for side effects for persons on antiretroviral treatment and CHWs can use the workflows to trace defaulters who are on ART treatment.

Condition

Condition

Task

Task

Resolution

Resolution

Condition

CHW educates and screens over 5’s for duration since last HIV test. CHW refers any eligible person to the health facility and submits a HIV Screening Form.

Task

A HIV testing referral Follow-up Task will appear within 2 days and is due 3 days later. The task is to remind the CHW to confirm referral attendance and HIV testing status.

Resolution

CHW submits HIV Testing Referral Follow-up Form verifying that she visited the person to confirm that he went to the health facility.

Condition

If person tests HIV positive, he/she is enrolled into HIR/ART program and started on treatment.

Task

In the first year of treatment, the CHW visits the person daily and submits Daily Follow up Form.

Resolution

The CHW submits Daily Follow-up Form demonstrating that she provided adherence counselling, screened for danger signs/side effects and reminded the person of upcoming facility visits.

Condition

A person walks into health facility without CHW referral and is enrolled into ART program. Site Supervisor submits a Treatment Enrollment Form indicating person has been enrolled in ART program.

Task

A HIV Daily Follow-up Task will appear immediately on the CHW device. The task is meant to inform the CHW that the person has been enrolled in NCD program and needs their follow up.

Resolution

The CHW submits Daily Follow-up Form demonstrating that she provided adherence counselling, screened for danger signs/side effects and reminded the person upcoming facility visits.

Resources to Get Started

Here are a few additional resources to help get you started with the integrated health reference application.

The open sharing of digital health apps used by CHWs is a monumental milestone in the digital health space, and for the CHT Community. Reach out on the forum to share how you will leverage these resources, along with your feedback and continued innovations that could benefit the larger community.

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/examples/stock-monitoring/index.html b/apps/examples/stock-monitoring/index.html index 9ba15811b8..e9cea51bc7 100644 --- a/apps/examples/stock-monitoring/index.html +++ b/apps/examples/stock-monitoring/index.html @@ -1,9 +1,9 @@ -Stock Monitoring | Community Health Toolkit +Stock Monitoring | Community Health Toolkit

Stock Monitoring

Guidance on design and development of stock monitoring workflows.

Problem Being Addressed

Paper based commodity management systems are prone to errors due to reliance on manually updated registers, this greatly affects data quality. Further, it is time consuming for CHWs to reference stock balances and at the same time update stocks on the commodity management sheet while providing treatment to household members. In terms of supervision, CHW’s supervisors do not know when CHWs have stock outs in time for replenishing and ordering purposes. A stock monitoring application can help CHWs update their stocks seamlessly while strengthening data integrity.

Solution Overview

CHT stock monitoring apps can be configured to support community drug and commodity management among CHWs and their supervisors. The workflow can be designed to:

  • Capture stocks received from and returned to the health facility
  • Provide stock status of every commodity
  • Automatically deduct stocks when a CHW provides treatment to the community members
  • Escalate stock out alerts to the supervisors when the CHW runs low on any of the commodities

Some design considerations to make include:

  • A stock condition card will appear on the CHW profile that will show a summary of the stock status
  • In-built stock thresholds for different commodities
  • Distinct colors for different stock thresholds
    • Red: Low stock
    • Green: Optimal stock
    • Yellow: Medium stock
  • Automated stock level updates when a CHW provides treatment within the workflows
  • Stock out tasks to CHW supervisors
  • CHW supervisor updates the stocks disbursed on respective CHW Area and the CHW receives a task to confirm the received stocks
  • Ensure consistency of stock units across all the sections of the app

Users and Hierarchy Example

UserLocationDevicesRole
MOHCentral and district officesDesktop, laptopAccess to dashboards where they monitor program indicators. Can view aggregate targets, not patient data.
District BiostatisticianLocal officeDesktop, laptopEquipped with dashboards. Can view aggregate targets.
SupervisorCommunity level, based at facilitiesDesktop, laptopEquipped with CHT app. Can view CHW areas, their tasks and aggregate targets.
CHWCommunity levelSmartphonesEquipped with CHT app for registrations, screening and monthly reports.

Workflow Examples

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/examples/supervisor-reference-app/index.html b/apps/examples/supervisor-reference-app/index.html index d6ead51583..56796cf309 100644 --- a/apps/examples/supervisor-reference-app/index.html +++ b/apps/examples/supervisor-reference-app/index.html @@ -1,9 +1,9 @@ -Reference Application for CHW Supervision and Performance Management | Community Health Toolkit +Reference Application for CHW Supervision and Performance Management | Community Health Toolkit

Reference Application for CHW Supervision and Performance Management

A reference app for CHW supervisors to support performance management of CHWs using a mobile app.

Medic has worked with D-tree International to build a CHT supervisor reference app. The app supports community health worker (CHW) supervisors to continually monitor and improve the program quality for Zanzibar National community health program Jamii ni Afya. The supervisor reference application is designed to enable supervisors to access CHW performance information and any other information required to supervise, mentor, and support CHWs to provide quality community health services. This reference app provides an example that CHT app developers can easily customize to meet the needs for their specific program areas to support CHW program management.

The code for this application will be available soon.

Problem being addressed

Community Health Workers (CHWs) play a critical role in delivering quality care as part of the integrated primary health system, in some settings CHWs often serve as the only connection between the health system and the vulnerable and remote populations. For many community health programs, CHWs face a lot of challenges, this includes significant workloads, delivering health services to large and dispersed communities while being supported by limited and inadequate supervision. To ensure sustained positive impacts on CHW programmes, there is a need for supervisory interventions to be embedded within the broader community health system strengthening. The supervisor reference app empowers supervisors with the ability to monitor in real time the performance of CHWs which can guide supervisors to support CHWs in provision of community health care services.

Solution overview

The supervisor reference app workflows enables supervisors to: provide quality assurance supervisory activities, plan and document CHW supervisory activities, support CHW activities and monitor CHW performance in real time. The supervisor reference app has been designed to:

  • Monitor the performance of the CHW and supervisors using the in-app aggregate target and the supervisor targets functionalities respectively.
  • Document and track CHW and supervisory activities.
  • Schedule CHW monthly meetings quality monitoring visits and follow up visits.
  • Support supervisors to report on health outcomes for their supervisory area.
  • Identify CHWs who have not been visited by a supervisor.
  • Follow up on CHW who have been inactive for 3 months.

Forms hierarchy

The diagram indicates forms and tasks that can be filled by a supervisor on the supervisor reference app. Some of the forms are accessible as actions on the supervisor and CHW profiles while others are accessible as tasks for a supervisor and a CHW.

Workflows

CHW monthly meetings

A Supervisor conducts a CHW monthly meeting with all the CHWs. During the meeting the supervisor mentors the CHWs, reviews the CHWs performance and discusses with the CHWs the challenges they might be facing in their provision of community health services.

CHW monthly meeting workflow
1a Supervisor schedules for a CHW monthly meeting A supervisor schedules for a CHW monthly meeting by submitting a CHW monthly meeting2a CHW monthly meeting task The task appears 3 days before the due date and the supervisor completes the task during the monthly meeting
1b The Supervisor can also receive a recurring CHW monthly meeting task every month2b CHW monthly meeting form The CHW monthly meeting form can also be accessed through the action on the profile of the supervisor
2c Schedule Monthly Meeting The Supervisor can schedule the next CHW meeting form using the CHW monthly meeting form or task

Quality monitoring

With the supervisor reference app, a supervisor can schedule a quality monitoring visit to assess the quality of services being provided by a CHW. During the quality monitoring visit a supervisor shadows CHWs as they provide services to household members. A supervisor can schedule additional quality monitoring follow up visits for CHWs who are identified to have some weak areas and may need further support and mentorship from the supervisor.

Supervision quality monitoring workflow
1a Supervisor checks/monitors their in-app CHW aggregate targets The supervisor checks the in app targets to monitor CHW target progress.2a Quality Monitoring Planning form The supervisor schedules shadowing/quality monitoring visits with the CHWs that need support through the quality monitoring planning form3a Quality monitoring task The task appears 2 days before the due date. During the visit the supervisor completes the quality monitoring task
1b Supervisor UHC mode Using the UHC mode, the supervisor is able to identify CHWs that have not been visited by a supervisor for a while3b Shadowing reminder task - the task will appear on the CHW app two days before the visit to remind the CHW of the planned shadowing visit
3c Quality monitoring form The quality monitoring form can also be accessed as an action on the profile of the CHW
3d Quality Monitoring Follow up task Just in case a supervisor identifies some weak areas that a CHW need to improve on and be supported, they schedule for a quality monitoring follow up visit

Group sessions

CHW Group session workflow
1a A CHW organizes for a group session with community members Group session is a meeting coordinated and organized by CHW with community members to health educate community members of various health topics2a Group session form Supervisor attends the group session meetings, provides supervision, supports the activity and submits the group session form

CHW Inactivity

A CHW activity task is generated for a supervisor if a CHW has been inactive for 3 months. This workflow enables a supervisor to follow up CHWs who have been inactive for three months and may need to be replaced.

Additional Features

Targets

A Supervisor has access to the analytics tab. The widgets on the in-app analytics tab enables a supervisor to view individual supervisor performance metrics and the supervisor progress towards the expected monthly goals. The supervisor also has access to CHW aggregate targets which will help the supervisor track metrics for individual CHWs, especially for supervisors overseeing a group of CHWs.This feature allows a supervisor to identify CHWs who may need to be supported actively.

CHW aggregate targets
CHW aggregate targets

Supervisor UHC mode

A supervisor is able to use this feature to view the last time each CHW was visited so that a supervisor can prioritize visiting CHWs who have not been visited for a while. The visit detail information is updated every time a quality monitoring form is submitted.

Supervisor UHC mode

CHT Applications > + Create project issue

Reference Application for CHW Supervision and Performance Management

A reference app for CHW supervisors to support performance management of CHWs using a mobile app.

Medic has worked with D-tree International to build a CHT supervisor reference app. The app supports community health worker (CHW) supervisors to continually monitor and improve the program quality for Zanzibar National community health program Jamii ni Afya. The supervisor reference application is designed to enable supervisors to access CHW performance information and any other information required to supervise, mentor, and support CHWs to provide quality community health services. This reference app provides an example that CHT app developers can easily customize to meet the needs for their specific program areas to support CHW program management.

The code for this application will be available soon.

Problem being addressed

Community Health Workers (CHWs) play a critical role in delivering quality care as part of the integrated primary health system, in some settings CHWs often serve as the only connection between the health system and the vulnerable and remote populations. For many community health programs, CHWs face a lot of challenges, this includes significant workloads, delivering health services to large and dispersed communities while being supported by limited and inadequate supervision. To ensure sustained positive impacts on CHW programmes, there is a need for supervisory interventions to be embedded within the broader community health system strengthening. The supervisor reference app empowers supervisors with the ability to monitor in real time the performance of CHWs which can guide supervisors to support CHWs in provision of community health care services.

Solution overview

The supervisor reference app workflows enables supervisors to: provide quality assurance supervisory activities, plan and document CHW supervisory activities, support CHW activities and monitor CHW performance in real time. The supervisor reference app has been designed to:

  • Monitor the performance of the CHW and supervisors using the in-app aggregate target and the supervisor targets functionalities respectively.
  • Document and track CHW and supervisory activities.
  • Schedule CHW monthly meetings quality monitoring visits and follow up visits.
  • Support supervisors to report on health outcomes for their supervisory area.
  • Identify CHWs who have not been visited by a supervisor.
  • Follow up on CHW who have been inactive for 3 months.

Forms hierarchy

The diagram indicates forms and tasks that can be filled by a supervisor on the supervisor reference app. Some of the forms are accessible as actions on the supervisor and CHW profiles while others are accessible as tasks for a supervisor and a CHW.

Workflows

CHW monthly meetings

A Supervisor conducts a CHW monthly meeting with all the CHWs. During the meeting the supervisor mentors the CHWs, reviews the CHWs performance and discusses with the CHWs the challenges they might be facing in their provision of community health services.

CHW monthly meeting workflow
1a Supervisor schedules for a CHW monthly meeting A supervisor schedules for a CHW monthly meeting by submitting a CHW monthly meeting2a CHW monthly meeting task The task appears 3 days before the due date and the supervisor completes the task during the monthly meeting
1b The Supervisor can also receive a recurring CHW monthly meeting task every month2b CHW monthly meeting form The CHW monthly meeting form can also be accessed through the action on the profile of the supervisor
2c Schedule Monthly Meeting The Supervisor can schedule the next CHW meeting form using the CHW monthly meeting form or task

Quality monitoring

With the supervisor reference app, a supervisor can schedule a quality monitoring visit to assess the quality of services being provided by a CHW. During the quality monitoring visit a supervisor shadows CHWs as they provide services to household members. A supervisor can schedule additional quality monitoring follow up visits for CHWs who are identified to have some weak areas and may need further support and mentorship from the supervisor.

Supervision quality monitoring workflow
1a Supervisor checks/monitors their in-app CHW aggregate targets The supervisor checks the in app targets to monitor CHW target progress.2a Quality Monitoring Planning form The supervisor schedules shadowing/quality monitoring visits with the CHWs that need support through the quality monitoring planning form3a Quality monitoring task The task appears 2 days before the due date. During the visit the supervisor completes the quality monitoring task
1b Supervisor UHC mode Using the UHC mode, the supervisor is able to identify CHWs that have not been visited by a supervisor for a while3b Shadowing reminder task - the task will appear on the CHW app two days before the visit to remind the CHW of the planned shadowing visit
3c Quality monitoring form The quality monitoring form can also be accessed as an action on the profile of the CHW
3d Quality Monitoring Follow up task Just in case a supervisor identifies some weak areas that a CHW need to improve on and be supported, they schedule for a quality monitoring follow up visit

Group sessions

CHW Group session workflow
1a A CHW organizes for a group session with community members Group session is a meeting coordinated and organized by CHW with community members to health educate community members of various health topics2a Group session form Supervisor attends the group session meetings, provides supervision, supports the activity and submits the group session form

CHW Inactivity

A CHW activity task is generated for a supervisor if a CHW has been inactive for 3 months. This workflow enables a supervisor to follow up CHWs who have been inactive for three months and may need to be replaced.

Additional Features

Targets

A Supervisor has access to the analytics tab. The widgets on the in-app analytics tab enables a supervisor to view individual supervisor performance metrics and the supervisor progress towards the expected monthly goals. The supervisor also has access to CHW aggregate targets which will help the supervisor track metrics for individual CHWs, especially for supervisors overseeing a group of CHWs.This feature allows a supervisor to identify CHWs who may need to be supported actively.

CHW aggregate targets
CHW aggregate targets

Supervisor UHC mode

A supervisor is able to use this feature to view the last time each CHW was visited so that a supervisor can prioritize visiting CHWs who have not been visited for a while. The visit detail information is updated every time a quality monitoring form is submitted.

Supervisor UHC mode

CHT Applications > Concepts

Basic concepts that will help you understand how CHT applications are built

CHT Applications > Features > Supervision

Supervision and workforce management to strengthen health systems

CHT Applications > @@ -309,7 +309,8 @@ app

App Forms: Used to complete reports, tasks, and actions in the app

CHT Applications > Reference > tasks.js

Tasks: Definition of tasks shown to app users

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/examples/training/index.html b/apps/examples/training/index.html index 200a9c7ef7..7a453850b1 100644 --- a/apps/examples/training/index.html +++ b/apps/examples/training/index.html @@ -1,9 +1,9 @@ -Remote Onboarding and Training | Community Health Toolkit +Remote Onboarding and Training | Community Health Toolkit

Remote Onboarding and Training

App and care workflow training using remote capabilities.

The CHT’s Remote Onboarding and Training functionality enables Supervisors and Administrators to train CHWs on care workflows and related app use without being physically present. It is designed for:

  • Safety: maintaining distance due to infectious disease
  • Speed: faster deployment when timing is a critical
  • Scalability: onboarding large numbers of users at the same time
  • Measurability: evaluation to provide added support where needed
  • Adaptability: integration with existing program and workflow structures

Problem Being Addressed

Providing consistent training for CHWs is critically important in the context of evolving health programs and use of digital support tools. In-person training is often not feasible (due to distance, infectious disease outbreaks, or similar), but is still required to build and maintain effective care programs. Relying on CHWs to learn to use these new digital tools and adhere to care workflows on their own is a major challenge to health program success.

Solution Overview

The CHT’s onboarding and training capabilities offer a remote way to provide education to CHWs and Supervisors about digital tools functionality and care workflows. It can be deployed in both SMS and App based modules. Through asynchronous communication, program administrators are able to:

  • Create customized training modules that match program requirements
  • Capture assessments of CHW knowledge levels of training material
  • Assess user training participation abandonment
  • Identify CHWs who need additional Supervisor support

Users and Hierarchy Example

UserLocationDevicesRole
National Officials and County TeamsCentral and district officesDesktop, laptop and smartphonesMonitor central analytics regarding onboarding and training completion. Revisit content where needed.
Sub-County TeamsFacilities or local officeTablet or smartphoneReview CHW progress on aggregate, support CHW supervisors, and monitor location specific analytics.
CHW SupervisorsCommunity level, based at facilitiesSmartphoneVerify CHW onboarding and training completion, follow up with those who have not. Complete their own onboarding and training tasks to new workflows.
CHWsCommunity levelFeature phones and some smartphonesComplete onboarding and training via SMS or app, provide feedback and ask for support where needed.

Workflow Examples

Remote Login by App

Users may log into their app with a link sent to them via SMS. The link allows the user to directly enter their app, bypassing the need to enter their username and password.

See Also: Accessing CHT Apps

Remote Training Overview

These SMS and App based workflow examples illustrate how the CHT enables remote training, tasking and communication at scale. Training can be done using simple guides, audio/videos suitable for low bandwidth, or interactive experiences on personal devices. Training programs can be easily configured to suit specific health program needs.

Remote Training by SMS

Remote Training by App


More background information can be found in this summary deck.


CHT Applications > + Create project issue

Remote Onboarding and Training

App and care workflow training using remote capabilities.

The CHT’s Remote Onboarding and Training functionality enables Supervisors and Administrators to train CHWs on care workflows and related app use without being physically present. It is designed for:

  • Safety: maintaining distance due to infectious disease
  • Speed: faster deployment when timing is a critical
  • Scalability: onboarding large numbers of users at the same time
  • Measurability: evaluation to provide added support where needed
  • Adaptability: integration with existing program and workflow structures

Problem Being Addressed

Providing consistent training for CHWs is critically important in the context of evolving health programs and use of digital support tools. In-person training is often not feasible (due to distance, infectious disease outbreaks, or similar), but is still required to build and maintain effective care programs. Relying on CHWs to learn to use these new digital tools and adhere to care workflows on their own is a major challenge to health program success.

Solution Overview

The CHT’s onboarding and training capabilities offer a remote way to provide education to CHWs and Supervisors about digital tools functionality and care workflows. It can be deployed in both SMS and App based modules. Through asynchronous communication, program administrators are able to:

  • Create customized training modules that match program requirements
  • Capture assessments of CHW knowledge levels of training material
  • Assess user training participation abandonment
  • Identify CHWs who need additional Supervisor support

Users and Hierarchy Example

UserLocationDevicesRole
National Officials and County TeamsCentral and district officesDesktop, laptop and smartphonesMonitor central analytics regarding onboarding and training completion. Revisit content where needed.
Sub-County TeamsFacilities or local officeTablet or smartphoneReview CHW progress on aggregate, support CHW supervisors, and monitor location specific analytics.
CHW SupervisorsCommunity level, based at facilitiesSmartphoneVerify CHW onboarding and training completion, follow up with those who have not. Complete their own onboarding and training tasks to new workflows.
CHWsCommunity levelFeature phones and some smartphonesComplete onboarding and training via SMS or app, provide feedback and ask for support where needed.

Workflow Examples

Remote Login by App

Users may log into their app with a link sent to them via SMS. The link allows the user to directly enter their app, bypassing the need to enter their username and password.

See Also: Accessing CHT Apps

Remote Training Overview

These SMS and App based workflow examples illustrate how the CHT enables remote training, tasking and communication at scale. Training can be done using simple guides, audio/videos suitable for low bandwidth, or interactive experiences on personal devices. Training programs can be easily configured to suit specific health program needs.

Remote Training by SMS

Remote Training by App


More background information can be found in this summary deck.


CHT Applications > Reference > app_settings.json > .token_login

Token login: Instructions and schema for Login by SMS

CHT Applications > @@ -309,7 +309,8 @@ Quick Guides > Training > Onboarding

Best practices when using a Training App to keep training and production data apart

-

Last modified 09.03.2023: Training card guide (#970) (fb07ed89)
\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/features/admin/index.html b/apps/features/admin/index.html index 4bdf189c64..4ad7ebd9d4 100644 --- a/apps/features/admin/index.html +++ b/apps/features/admin/index.html @@ -1,9 +1,9 @@ -App Management | Community Health Toolkit +App Management | Community Health Toolkit

App Management

An interface for non-technical administrative users to manage users and settings

App Management is an interface for non-technical administrative users. With it you can manage users and make minor changes to the app, such as setting the SMS gateway phone number, and changing the default language for the app.

The App Management pages are a desktop-only interface meant for users with a reliable internet connection.


Page Tabs

These sections of the App can be configured from within the Admin Console:

  • Settings: Change basic settings like gateway phone number & country code
  • Languages: Set default app language, update translations
  • Forms: Upload XML and JSON forms
  • Import & Export: Import and export settings
  • Upgrade Instance: Install a newer app version
  • Users: View and edit users of the system
  • Icons: View and edit icons used in the app
  • Targets: Modify performance or activity targets
  • Roles & Permissions: Fine tuned control of user roles and permissions

App Management vs cht-conf

In general, everything that can be done in the Admin Console can also be done in command line tools, but not everything in the command line tools can be done in the Admin Console.

The Admin Console does not track changes. For most app development, using command line tools such as cht-conf and tracking files using a version control system is recommended

In Admin Console But Not Command Line Tools:

  • User management
  • ~Upgrades~

In Command Line Tools But Not Admin Console:

  • Most of the JSON settings
  • XLS → Xform conversion

CHT Applications > + Create project issue

App Management

An interface for non-technical administrative users to manage users and settings

App Management is an interface for non-technical administrative users. With it you can manage users and make minor changes to the app, such as setting the SMS gateway phone number, and changing the default language for the app.

The App Management pages are a desktop-only interface meant for users with a reliable internet connection.


Page Tabs

These sections of the App can be configured from within the Admin Console:

  • Settings: Change basic settings like gateway phone number & country code
  • Languages: Set default app language, update translations
  • Forms: Upload XML and JSON forms
  • Import & Export: Import and export settings
  • Upgrade Instance: Install a newer app version
  • Users: View and edit users of the system
  • Icons: View and edit icons used in the app
  • Targets: Modify performance or activity targets
  • Roles & Permissions: Fine tuned control of user roles and permissions

App Management vs cht-conf

In general, everything that can be done in the Admin Console can also be done in command line tools, but not everything in the command line tools can be done in the Admin Console.

The Admin Console does not track changes. For most app development, using command line tools such as cht-conf and tracking files using a version control system is recommended

In Admin Console But Not Command Line Tools:

  • User management
  • ~Upgrades~

In Command Line Tools But Not Admin Console:

  • Most of the JSON settings
  • XLS → Xform conversion

CHT Applications > Concepts > Prerequisites

Tools and background skills that are helpful for developing CHT apps

-

Last modified 09.03.2023: Training card guide (#970) (fb07ed89)
\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/features/admin/index.xml b/apps/features/admin/index.xml index 7177b62c37..8a7cf6f4e1 100644 --- a/apps/features/admin/index.xml +++ b/apps/features/admin/index.xml @@ -1 +1 @@ -Community Health Toolkit – App Managementhttps://docs.communityhealthtoolkit.org/apps/features/admin/Recent content in App Management on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file +App Management on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/features/admin/Recent content in App Management on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file diff --git a/apps/features/contacts/index.html b/apps/features/contacts/index.html index cbda0c2cb0..d0e6ee44e6 100644 --- a/apps/features/contacts/index.html +++ b/apps/features/contacts/index.html @@ -1,9 +1,9 @@ -Contacts | Community Health Toolkit +Contacts | Community Health Toolkit

Contacts

The people and places that are being cared for

“People” is the generic name used for individuals in apps built with the Core Framework. They can be patients, family members, nurses or health workers. Anyone with a profile in your app is a person.

“Places” is the generic name that represents a level in the hierarchy. “People” belong to “places”, and “places” belong to other higher level “places” in the hierarchy. A “place” could be a geographic area, like a district with the “people” associated to it being health officers. A “place” could also be a structure in the health system, such as a health facility, and the “people” associated to it being nurses. In deployments with CHWs, the lowest “place” in the hierarchy often represents individual households or families, and the individual members of that household are the “people” associated to it.

Users can access their “people” and “places” from the People tab. The permissions set for your role and your placement in the hierarchy will determine which contacts you’re able to see. Advanced configuration options are available for a specific offline user role to manage what level of contact data is downloaded and stored on their device.

Main List

Contact sorting screenshot

The list view on the leftmost screenshot is what a logged-in CHW would see when they access the “People” tab on a small screen.

The item at the top of the list is the “place” the user belongs to. Below that is a list of the “places” they serve, represented by families. Individual “people” are not shown here, but will appear in search results.

Because this list defaults to show the “places” below the user in the hierarchy, a CHW supervisor would see a different view. Instead of families, they might see a list of CHW Areas they manage.

New “places” can be added to this level of the hierarchy by clicking on the “Add new +” button at the bottom of the screen. This allows a CHW to add a new family to their list, or a CHW supervisor to add a new Area they manage.

With the UHC Mode configured, the main list of households is displayed as shown on the rightmost screenshot to help health workers ensure that all households are visited regularly.


Searching

Click on the search icon at the top of the screen to search for a “person” or “place”. The freetext search works on all fields included in the “person” or “place” document such as patient name or patient ID. The exact fields depends on which information you’ve configured your app to collect.

After typing a search term, press the “Enter” key on your keyboard, then the list filters to show matching items. Searching will only return items that are lower than you in the hierarchy and that you have permission to view.

To clear the search and return to the default view, click on the arrow icon located to the left of the search box.

Profiles

Clicking an item on the main list will open a profile where you can see detailed information about that person or place. At the top is general information like name and phone number.

If you’re viewing a place profile, you’ll see a list of people or places that belong to this place in the app hierarchy, such as family members. The star signifies the primary contact.

Beneath that, you will find tasks for this person or place. At the very bottom is a history of submitted reports for this person or place.

From profiles, users can edit contact information, take actions, and, if viewing a place profile, add new people and assign a primary contact person. If a place is not at the bottom of the hierarchy, a user can add new places to the level below this.

Fields

Contact Summary

The top card on all profiles contains general information for the contact. All the fields shown in this summary card are configurable.

See Also: Defining Contact Summary

Condition Cards

A “condition” card displays data on a profile that’s been submitted in a report about that person or place. Data can be pulled from one report or summarize many reports.

Condition cards can be permanent or conditional; set to appear only when a specific type of report is submitted. They can also be set to disappear when a condition is resolved or a certain amount of time has passed. You can have as many condition cards as you like, though we recommend keeping the user’s experience in mind.

Configurable elements include:

  • Title
  • Label for each data point displayed
  • Data point for the field
  • Icon for the field, if desired
  • Conditions under which to display

See Also: Defining Condition Cards

Care Guides

“Care Guides” are dynamic forms that you can fill out for a person or place. You can access Care Guides by clicking on the + button at the bottom of a profile. For more info, see the Care Guides overview page.

You’ll see different forms here depending on which person or place you’re viewing. For example, forms for families might include a “Family Survey.” Forms for adult women might include “New Pregnancy.” Forms for adult women who have had a pregnancy report, and no delivery yet reported, would also see “ANC visit.” Forms for children might include “Under-5 Assessment” or “Growth Monitoring.”

Health workers can use these Care Guides at any time. If the app has scheduled a care visit or follow up, it will be listed under “Tasks.”

See Also: Defining Care Guides



CHT Applications > + Create project issue

Contacts

The people and places that are being cared for

“People” is the generic name used for individuals in apps built with the Core Framework. They can be patients, family members, nurses or health workers. Anyone with a profile in your app is a person.

“Places” is the generic name that represents a level in the hierarchy. “People” belong to “places”, and “places” belong to other higher level “places” in the hierarchy. A “place” could be a geographic area, like a district with the “people” associated to it being health officers. A “place” could also be a structure in the health system, such as a health facility, and the “people” associated to it being nurses. In deployments with CHWs, the lowest “place” in the hierarchy often represents individual households or families, and the individual members of that household are the “people” associated to it.

Users can access their “people” and “places” from the People tab. The permissions set for your role and your placement in the hierarchy will determine which contacts you’re able to see. Advanced configuration options are available for a specific offline user role to manage what level of contact data is downloaded and stored on their device.

Main List

Contact sorting screenshot

The list view on the leftmost screenshot is what a logged-in CHW would see when they access the “People” tab on a small screen.

The item at the top of the list is the “place” the user belongs to. Below that is a list of the “places” they serve, represented by families. Individual “people” are not shown here, but will appear in search results.

Because this list defaults to show the “places” below the user in the hierarchy, a CHW supervisor would see a different view. Instead of families, they might see a list of CHW Areas they manage.

New “places” can be added to this level of the hierarchy by clicking on the “Add new +” button at the bottom of the screen. This allows a CHW to add a new family to their list, or a CHW supervisor to add a new Area they manage.

With the UHC Mode configured, the main list of households is displayed as shown on the rightmost screenshot to help health workers ensure that all households are visited regularly.


Searching

Click on the search icon at the top of the screen to search for a “person” or “place”. The freetext search works on all fields included in the “person” or “place” document such as patient name or patient ID. The exact fields depends on which information you’ve configured your app to collect.

After typing a search term, press the “Enter” key on your keyboard, then the list filters to show matching items. Searching will only return items that are lower than you in the hierarchy and that you have permission to view.

To clear the search and return to the default view, click on the arrow icon located to the left of the search box.

Profiles

Clicking an item on the main list will open a profile where you can see detailed information about that person or place. At the top is general information like name and phone number.

If you’re viewing a place profile, you’ll see a list of people or places that belong to this place in the app hierarchy, such as family members. The star signifies the primary contact.

Beneath that, you will find tasks for this person or place. At the very bottom is a history of submitted reports for this person or place.

From profiles, users can edit contact information, take actions, and, if viewing a place profile, add new people and assign a primary contact person. If a place is not at the bottom of the hierarchy, a user can add new places to the level below this.

Fields

Contact Summary

The top card on all profiles contains general information for the contact. All the fields shown in this summary card are configurable.

See Also: Defining Contact Summary

Condition Cards

A “condition” card displays data on a profile that’s been submitted in a report about that person or place. Data can be pulled from one report or summarize many reports.

Condition cards can be permanent or conditional; set to appear only when a specific type of report is submitted. They can also be set to disappear when a condition is resolved or a certain amount of time has passed. You can have as many condition cards as you like, though we recommend keeping the user’s experience in mind.

Configurable elements include:

  • Title
  • Label for each data point displayed
  • Data point for the field
  • Icon for the field, if desired
  • Conditions under which to display

See Also: Defining Condition Cards

Care Guides

“Care Guides” are dynamic forms that you can fill out for a person or place. You can access Care Guides by clicking on the + button at the bottom of a profile. For more info, see the Care Guides overview page.

You’ll see different forms here depending on which person or place you’re viewing. For example, forms for families might include a “Family Survey.” Forms for adult women might include “New Pregnancy.” Forms for adult women who have had a pregnancy report, and no delivery yet reported, would also see “ANC visit.” Forms for children might include “Under-5 Assessment” or “Growth Monitoring.”

Health workers can use these Care Guides at any time. If the app has scheduled a care visit or follow up, it will be listed under “Tasks.”

See Also: Defining Care Guides



CHT Applications > Reference > forms/ > contact

Contact Forms: Used for creating and editing people and places

CHT Applications > @@ -313,7 +313,8 @@ Contacts + Users 1

Creating and editing contacts and users in the CHT UI

CHT Applications > Examples > Contact Tracing

A community surveillance tool to help control infectious disease outbreaks and mitigate secondary disease transmission

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/features/contacts/index.xml b/apps/features/contacts/index.xml index f943ebba9a..1550462c02 100644 --- a/apps/features/contacts/index.xml +++ b/apps/features/contacts/index.xml @@ -1 +1 @@ -Community Health Toolkit – Contactshttps://docs.communityhealthtoolkit.org/apps/features/contacts/Recent content in Contacts on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file +Contacts on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/features/contacts/Recent content in Contacts on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file diff --git a/apps/features/index.html b/apps/features/index.html index 1a4b53f2bb..b6c074962d 100644 --- a/apps/features/index.html +++ b/apps/features/index.html @@ -1,9 +1,9 @@ -Features of CHT Applications | Community Health Toolkit +Features of CHT Applications | Community Health Toolkit

Features of CHT Applications

Overview of features of CHT applications built with CHT Core

Contacts

The people and places that are being cared for

Messaging

Messaging for Care Coordination, Alerts, and Notifications

Reports

Reports for Data & Report Management

Supervision

Supervision and workforce management to strengthen health systems

Tasks

Ensuring that the right actions are taken for the right people at the right time

Targets

Dashboards to track metrics for an individual CHW or for an entire health facility

Training

Remotely train health workers

Muting

Temporarily silence tasks and SMS schedules

Universal Health Coverage Mode

Supporting equitable and timely care to families to increase Universal Health Coverage (UHC)

App Management

An interface for non-technical administrative users to manage users and settings

Integrations

Exchange data with other systems

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/features/index.xml b/apps/features/index.xml index a6f46d2172..fa87ab1c1a 100644 --- a/apps/features/index.xml +++ b/apps/features/index.xml @@ -1,446 +1 @@ -Community Health Toolkit – Features of CHT Applicationshttps://docs.communityhealthtoolkit.org/apps/features/Recent content in Features of CHT Applications on Community Health ToolkitHugo -- gohugo.ioenApps: Contactshttps://docs.communityhealthtoolkit.org/apps/features/contacts/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/contacts/ -<!-- ## Contacts: Person and Family Profiles --> -<!-- TODO Refine screenshots, and add desktop view. --> -<p>“People” is the generic name used for individuals in apps built with the Core Framework. They can be patients, family members, nurses or health workers. Anyone with a profile in your app is a person.</p> -<p>“Places” is the generic name that represents a level in the <a href="https://docs.communityhealthtoolkit.org/apps/concepts/hierarchy/">hierarchy</a>. “People” belong to “places”, and “places” belong to other higher level “places” in the hierarchy. A “place” could be a geographic area, like a district with the &ldquo;people&rdquo; associated to it being health officers. A &ldquo;place&rdquo; could also be a structure in the health system, such as a health facility, and the &ldquo;people&rdquo; associated to it being nurses. In deployments with CHWs, the lowest &ldquo;place&rdquo; in the hierarchy often represents individual households or families, and the individual members of that household are the &ldquo;people&rdquo; associated to it.</p> -<p>Users can access their “people” and “places” from the <strong>People</strong> tab. The permissions set for your role and your placement in the hierarchy will determine which contacts you’re able to see. Advanced configuration options are available for a specific offline user role to manage what <a href="https://docs.communityhealthtoolkit.org/apps/guides/performance/replication/#contact-depth">level of contact data</a> is downloaded and stored on their device.</p> -<figure class="left col-3 col-lg-3"><a href="people-mobile.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/contacts/people-mobile.png"/> </a> -</figure> -<figure class="left col-9 col-lg-9"><a href="people-desktop.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/contacts/people-desktop.png"/> </a> -</figure> -<h2 id="main-list">Main List</h2> -<p> -<figure class="right col-6 col-lg-3"><a href="sort-dropdown.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/contacts/sort-dropdown.png" -alt="Contact sorting screenshot"/> </a> -</figure> -<figure class="right col-6 col-lg-3"><a href="people-mobile.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/contacts/people-mobile.png"/> </a> -</figure> -</p> -<p>The list view on the leftmost screenshot is what a logged-in CHW would see when they access the “People” tab on a small screen.</p> -<p>The item at the top of the list is the “place” the user belongs to. Below that is a list of the “places” they serve, represented by families. Individual “people” are not shown here, but will appear in search results.</p> -<p>Because this list defaults to show the “places” below the user in the hierarchy, a CHW supervisor would see a different view. Instead of families, they might see a list of CHW Areas they manage.</p> -<p>New “places” can be added to this level of the hierarchy by clicking on the “Add new +” button at the bottom of the screen. This allows a CHW to add a new family to their list, or a CHW supervisor to add a new Area they manage.</p> -<p>With the <a href="https://docs.communityhealthtoolkit.org/apps/features/uhc-mode/"><em>UHC Mode</em></a> configured, the main list of households is displayed as shown on the rightmost screenshot to help health workers ensure that all households are visited regularly.</p> -<br clear="all"> -<h2 id="searching">Searching</h2> -<figure class="right col-6 col-lg-3"><a href="search-mobile.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/contacts/search-mobile.png"/> </a> -</figure> -<p>Click on the search icon at the top of the screen to search for a “person” or “place”. The freetext search works on all fields included in the “person” or “place” document such as patient name or patient ID. The exact fields depends on which information you’ve configured your app to collect.</p> -<p>After typing a search term, press the &ldquo;Enter&rdquo; key on your keyboard, then the list filters to show matching items. Searching will only return items that are lower than you in the hierarchy and that you have permission to view.</p> -<p>To clear the search and return to the default view, click on the arrow icon located to the left of the search box.</p> -<h2 id="profiles">Profiles</h2> -<p>Clicking an item on the main list will open a profile where you can see detailed information about that person or place. At the top is general information like name and phone number.</p> -<p>If you’re viewing a place profile, you’ll see a list of people or places that belong to this place in the app hierarchy, such as family members. The star signifies the primary contact.</p> -<p>Beneath that, you will find tasks for this person or place. At the very bottom is a history of submitted reports for this person or place.</p> -<div class="container"> -<div class="row"> -<figure class="col-6 col-lg-3"><a href="profile1.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/contacts/profile1.png"/> </a> -</figure> -<figure class="col-6 col-lg-3"><a href="profile2.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/contacts/profile2.png"/> </a> -</figure> -<figure class="col-6 col-lg-3"><a href="profile3.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/contacts/profile3.png"/> </a> -</figure> -<figure class="col-6 col-lg-3"><a href="profile4.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/contacts/profile4.png"/> </a> -</figure> -</div> -</div> -<p>From profiles, users can edit contact information, take actions, and, if viewing a place profile, add new people and assign a primary contact person. If a place is not at the bottom of the hierarchy, a user can add new places to the level below this.</p> -<h2 id="fields">Fields</h2> -<h3 id="contact-summary">Contact Summary</h3> -<p>The top card on all profiles contains general information for the contact. All the fields shown in this summary card are configurable.</p> -<div class="container"> -<div class="row"> -<figure class="col-6 col-lg-4"><a href="bio1.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/contacts/bio1.png"/> </a> -</figure> -<figure class="col-6 col-lg-4"><a href="bio2.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/contacts/bio2.png"/> </a> -</figure> -</div> -</div> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/reference/contact-page/#contact-summary">Defining Contact Summary</a></p> -<h3 id="condition-cards">Condition Cards</h3> -<p>A “condition” card displays data on a profile that’s been submitted in a report about that person or place. Data can be pulled from one report or summarize many reports.</p> -<p>Condition cards can be permanent or conditional; set to appear only when a specific type of report is submitted. They can also be set to disappear when a condition is resolved or a certain amount of time has passed. You can have as many condition cards as you like, though we recommend keeping the user’s experience in mind.</p> -<p>Configurable elements include:</p> -<ul> -<li>Title</li> -<li>Label for each data point displayed</li> -<li>Data point for the field</li> -<li>Icon for the field, if desired</li> -<li>Conditions under which to display</li> -</ul> -<div class="container"> -<div class="row"> -<figure class="col-6 col-lg-4"><a href="condition-card1.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/contacts/condition-card1.png"/> </a> -</figure> -<figure class="col-6 col-lg-4"><a href="condition-card2.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/contacts/condition-card2.png"/> </a> -</figure> -</div> -</div> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/reference/contact-page/#condition-cards">Defining Condition Cards</a></p> -<h2 id="care-guides">Care Guides</h2> -<!-- todo: Resolve Care Guides vs Actions --> -<figure class="right col-6 col-lg-3"><a href="care-guides.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/contacts/care-guides.png"/> </a> -</figure> -<p>“Care Guides” are dynamic forms that you can fill out for a person or place. You can access Care Guides by clicking on the + button at the bottom of a profile. For more info, see the <a href="https://docs.communityhealthtoolkit.org/apps/concepts/care-guides/">Care Guides overview page</a>.</p> -<p>You’ll see different forms here depending on which person or place you’re viewing. For example, forms for families might include a “Family Survey.” Forms for adult women might include “New Pregnancy.” Forms for adult women who have had a pregnancy report, and no delivery yet reported, would also see “ANC visit.” Forms for children might include “Under-5 Assessment” or “Growth Monitoring.”</p> -<p>Health workers can use these Care Guides at any time. If the app has scheduled a care visit or follow up, it will be listed under “Tasks.”</p> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/reference/contact-page/#care-guides">Defining Care Guides</a></p> -<br clear="all"> -<!-- TODO: -## Defining Contact Forms --->Apps: Messaginghttps://docs.communityhealthtoolkit.org/apps/features/messaging/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/messaging/ -<p>Messaging is a quick way to coordinate with other health workers. The Messages tab allows users to send a SMS message to any person or group of people in the app. Common uses of messages include asking questions, coordinating care logistics, providing encouragement or confirming training times.</p> -<p>The main list is a combination of both outgoing and incoming messages. Messages exchanged with the same person or group are organized into a thread, similar to messages in a phone’s messaging app.</p> -<p>The features on the Messages tab are best supported on desktop and most often used by someone in a supervisor role.</p> -<figure class="left col-3 col-lg-3"><a href="messages-mobile.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/messaging/messages-mobile.png"/> </a> -</figure> -<figure class="left col-9 col-lg-9"><a href="messages-desktop.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/messaging/messages-desktop.png"/> </a> -</figure> -<h2 id="main-list">Main List</h2> -<figure class="right col-6 col-lg-3"><a href="messages-mobile.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/messaging/messages-mobile.png"/> </a> -</figure> -<p>On the main list of messages, the first line of bold text is the name and / or phone number of the sender. The second line is an excerpt from the most recent message, and the third line, if applicable, is the place(s) that the sender belongs to.</p> -<p>In the upper right corner, a timestamp displays when the most recent message was sent. An unread message is indicated by a blue line and bold blue timestamp. Messages are sorted by date with the most recent at the top of the list.</p> -<p>To send a new message that starts a brand new conversation thread, select the “Send Message +” button at the bottom of the screen. On the new message screen, enter the phone number you would like to send the message to or select a person in the app from the drop-down list. Then, type out your message text. -<br clear="all"></p> -<h2 id="detail-page">Detail Page</h2> -<figure class="right col-6 col-lg-3"><a href="messages-detail.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/messaging/messages-detail.png"/> </a> -</figure> -<p>Clicking on a message in the main list will take you to a detail tab where you can see the full text of the conversation. Underneath each individual message in the conversation, you will see the message status which tells you whether or not the message was successfully delivered or received and at what time.</p> -<p>To reply to a message, tap or click in the box at the bottom of the conversation that says “Reply to…” and start typing. Each message is limited to 160 characters but you may send more than one message if necessary.</p> -<p>It is also possible to configure auto-responses and for schedules of personalized, automated messages to be triggered upon submission of a form (e.g., a pregnancy registration triggers a schedule of personalized Antenatal care messages). -<br clear="all"></p>Apps: Reportshttps://docs.communityhealthtoolkit.org/apps/features/reports/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/reports/ -<p>The Reports tab is where you can access submitted data. Depending on how often you anticipate a user needing to access this tab, you can configure it to show in the main tabs list (preferable for admin users) or in the secondary hamburger menu (preferable for CHW users).</p> -<p>The permissions set for your role and your placement in the hierarchy will determine which reports you’re able to see on this tab. As a rule, you can only view reports submitted by yourself or those below you in the <a href="https://docs.communityhealthtoolkit.org/apps/concepts/hierarchy/">hierarchy</a>. Therefore, CHWs will only see reports that they submitted on this tab, while supervisors will see reports that they submitted as well as those submitted by their CHWs. Advanced configuration options are available for a specific off-line user role to manage what <a href="https://docs.communityhealthtoolkit.org/apps/guides/performance/replication/#report-depth">level of report data</a> is copied to their device.</p> -<figure class="left col-3 col-lg-3"><a href="reports-mobile.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/reports/reports-mobile.png"/> </a> -</figure> -<figure class="left col-9 col-lg-9"><a href="reports-desktop.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/reports/reports-desktop.png"/> </a> -</figure> -<h2 id="main-list">Main List</h2> -<figure class="right col-6 col-lg-3"><a href="reports-mobile.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/reports/reports-mobile.png"/> </a> -</figure> -<p>The first line of bold text is the name of the person whom the report is about. The second line of text is the name of the report, and the third line of text is the hierarchy of place to which that person belongs. In the upper right corner, a timestamp displays when the report was submitted. Reports are sorted by submission date, with the most recent reports at the top. If a report is unread, the timestamp will be bold blue and there will be a horizontal blue line above it.</p> -<p>Apps built with the Core Framework have a “review” feature that allows managers to indicate whether a report has been reviewed and if it contains errors. If a manager has marked a report as “correct,” a green checkmark will show below the timestamp. If a report is marked as “has errors,” a red ‘X’ will show. This same icon is used for invalid SMS messages.</p> -<br clear="all"> -<h2 id="filters--search">Filters &amp; Search</h2> -<p>The toolbar at the top of the page includes filters and search to help users narrow down the list or search for and find a specific report. These filters are configurable and could include:</p> -<ul> -<li><strong>Report Types</strong> (e.g. pregnancy registration, visits, delivery report)</li> -<li><strong>Places</strong> (e.g. districts, health centers or CHW areas)</li> -<li><strong>Date of Submission</strong></li> -<li><strong>Status</strong> (e.g. not reviewed, has errors, correct, valid SMS, invalid SMS)</li> -</ul> -<p>Using the search box, you can search for reports by patient name, phone number, ID number and more. To reset the filters and view the full list of forms, click on &ldquo;Reset&rdquo; located in the filter sidebar.</p> -<figure class="left col-3 col-lg-3"><a href="reports-search.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/reports/reports-search.png"/> </a> -</figure> -<figure class="left col-9 col-lg-9"><a href="reports-search-reset.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/reports/reports-search-reset.png"/> </a> -</figure> -<br clear="all"> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -A new user experience for Filter and Search was introduced in v3.17. The previous version can be re-enabled for users by adding the <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-permissions/">permission</a> <code>can_view_old_filter_and_search</code> to the user’s role; however, the old version should be considered deprecated and will be completely removed in a future release. See <a href="https://docs.communityhealthtoolkit.org/apps/guides/updates/feature-flags/">Feature Flags</a> documentation for more info -</div> -<br clear="all"> -<h2 id="action-buttons">Action Buttons</h2> -<p>The action buttons at the bottom of the screen are configurable using <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-permissions/">permissions</a>. Options include submit, edit, delete, review and export reports.</p> -<p>Clicking on the “Export” button will download a CSV file with all the data from the reports. And clicking the “+ Submit report” button opens a menu of forms a user can choose to complete.</p> -<figure class="left col-9 col-lg-9"><a href="reports-desktop.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/reports/reports-desktop.png"/> </a> -</figure> -<br clear="all"> -<h2 id="bulk-delete-reports">Bulk Delete Reports</h2> -<p>Allows the user to select multiple reports and delete them. <strong>Please Note</strong>: This action cannot be undone. If in doubt, do not delete! You can restrict a user’s access to this feature by disabling the <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-permissions/">permission</a> <code>can_bulk_delete_reports</code>.</p> -<figure class="left col-3 col-lg-3"><a href="reports-bulk-mobile.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/reports/reports-bulk-mobile.png"/> </a> -</figure> -<figure class="left col-9 col-lg-9"><a href="reports-bulk-desktop.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/reports/reports-bulk-desktop.png"/> </a> -</figure> -<br clear="all"> -<h2 id="detail-pages">Detail Pages</h2> -<figure class="right col-6 col-lg-3"><a href="reports-detail.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/reports/reports-detail.png"/> </a> -</figure> -<p>You can click on any report to view a report detail page. Here you&rsquo;ll find the name and phone number of the user who submitted the report as well as responses to the questions within it. If the report initiated a schedule of SMS messages, you will see the messages queued to send.</p> -<p>The buttons at the bottom are configurable. The ones you see will depend on your user role, permissions, and hierarchy.</p> -<ul> -<li><strong>Send a Message</strong>​: Opens the Messages page to send a message to the person who submitted the report</li> -<li><strong>Review</strong>: Mark as “correct” or “has errors”</li> -<li><strong>Edit</strong>: Opens the form to edit it</li> -<li><strong>Delete</strong>: Deletes a report ( cannot be undone)</li> -</ul> -<br clear="all"> -<h2 id="defining-forms">Defining Forms</h2> -<p>The reports shown in your app are the completed and submitted <em>forms</em>. These forms must be defined and included with the application. There are two types of form definitions for reports:</p> -<ul> -<li><strong>App forms</strong>: actions within the app, such as a completed task, or an action on a contact&rsquo;s profile or reports tab. App forms are defined as <a href="https://docs.communityhealthtoolkit.org/apps/reference/forms/app/">XForms</a>.</li> -<li><strong>JSON forms</strong>: data coming from external channels such as SMS, or via interoperability with other tools. JSON forms are defined using a <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/forms/">JavaScript Object Notation schema</a>.</li> -</ul>Apps: Supervisionhttps://docs.communityhealthtoolkit.org/apps/features/supervision/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/supervision/ -<p>Supervision and workforce management are important aspects to building and maintaining high-performing community health systems. Supervisors help Community Health Workers deliver quality healthcare services to their patients through building CHW care delivery knowledge and skills, fostering a supportive work environment, and supporting continuity of care between home-based care and community health centers or facilities.</p> -<p>The importance of regular and systematic CHW supervision is emphasized by the WHO’s <a href="https://www.who.int/publications/i/item/9789241550369">guidelines</a> on health policy and system support, which is formulated to optimize community-based health worker programs. Following these guidelines, the CHT is designed to enable Supervisors to provide personalized performance feedback during CHW supervision as well as track aggregate statistics. Data collected from completed <a href="https://docs.communityhealthtoolkit.org/apps/concepts/care-guides/">Care Guides</a> produce granular information which can be used to provide coaching that promotes compliance with health program standards of practice and closer monitoring of outcomes.</p> -<h2 id="chw-aggregate-targets">CHW Aggregate Targets</h2> -<figure class="right col-7 col-lg-6"><a href="aggregate-supervisor.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/supervision/aggregate-supervisor.png"/> </a> -</figure> -<p>For CHW Supervisors, the <a href="https://docs.communityhealthtoolkit.org/apps/features/targets/">Targets</a> tab provides important insights into their community unit. It presents Supervisors with actionable information about their CHWs, by aggregating data for each of the CHWs that a Supervisor manages and presenting it in an easily digestible format. This enables Supervisors to gain insight into how well their team of CHWs is working together to meet common goals.</p> -<p>Selecting an aggregate widget opens the detailed view with the data for each individual CHW. If a CHW is performing below the target goal, their value will be highlighted in red, making it easier for Supervisors to know with which CHWs to follow up for coaching and performance management.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Aggregate targets were introduced in v3.9, and can be configured for both online and offline users. Aggregate targets are based on the widgets seen by CHWs, and dependent on the data that has been synced. If a CHW or the supervisor has not synced, then the aggregate target will not be up to date. -</div> -<h2 id="supervisor-tasks">Supervisor Tasks</h2> -<p>The CHT can be configured to create <a href="https://docs.communityhealthtoolkit.org/apps/features/tasks/">Tasks</a> for Supervisors to help plan their performance management reviews. Tasks can be generated based on routine CHW supervision interactions, or data-driven based on specific events (e.g. to follow up with health workers whom haven’t submitted any forms in x period of time). Using Supervisor tasks to ensure that the right actions are taken for the right CHWs at the right time strengthens supervisory program design through routine assessments and timely feedback.</p> -<div class="pageinfo pageinfo-primary"> -<h3 id="deployment-case-study">Deployment Case Study</h3> -<p><a href="https://www.musohealth.org">Muso</a>, a leading community health organization and major contributor to the CHT, has implemented ”360º supervision”, achieving some of the lowest child mortality rates in sub-Saharan Africa. This model provides dedicated mentorship and supportive supervision to CHWs tailored to each CHW&rsquo;s particular strengths and challenges. A key theme of our human-centered approach was the idea of using data to improve one-to-one supervision, rather than using analytics to replace Supervisors. Read more about findings from a recent <a href="https://medic.org/stories/new-study-precision-supervision-and-personalized-feedback-dashboards-improve-chw-performance-in-mali/">randomized controlled trial</a>.</p> -</div> -<h2 id="user-management">User Management</h2> -<p>Supervisors are able to set up users in the CHT without contacting a system administrator. They can <strong>create</strong> new CHW user accounts or <strong>replace</strong> CHWs on an existing device.</p> -<p>When <em>creating</em> a new user account, Supervisors fill out the necessary details, including the CHW&rsquo;s phone number, from their own device. They can do this while offline, but must sync before the actual user account is created. Once the Supervisor syncs, the CHT will send an SMS to the new CHW with a <a href="https://docs.communityhealthtoolkit.org/apps/concepts/access/#magic-links-for-logging-in-token-login">magic link</a> that enables them to login and start using the app.</p> -<p>When <em>replacing</em> a CHW, Supervisors access the existing device and provide details about the new CHW. The new CHW can start using the app immediately, even while offline, and will see all of the existing household data. Once the new CHW syncs, the records on the server will be updated to reflect the new CHWs details.</p> -<p>This can be used to manage both CHW and CHW supervisor roles.</p> -<h2 id="managing-multiple-areas">Managing Multiple Areas</h2> -<p>CHT hierarchies tend to mimic geographical areas but Supervisors often manage CHWs across multiple geographical areas. (Offline) Supervisors who manage multiple areas can see data for all the different areas they manage from one app.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -The ability for one user to replicate data from multiple areas was introduced in v4.9.0. A video demonstration of setting up a multi-facility user and what this looks like from a user’s perspective can be found <a href="https://forum.communityhealthtoolkit.org/t/support-for-supervisors-who-need-to-manage-multiple-areas/3497/2?u=michael">on the forum</a> and in the <a href="https://youtu.be/hrhdrzP41gE?si=_7wglk7Nm7CCSFbY&amp;t=606">June 2024 CHT Round-up</a>. <em>NOTE: Users assigned multiple areas will not see Aggregate Targets</em>. -</div> -<h2 id="supervisor-dashboards">Supervisor Dashboards</h2> -<figure class="right col-7 col-lg-6"><a href="supervisor-dashboards.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/supervision/supervisor-dashboards.png"/> </a> -</figure> -<p>Program dashboards track, visualize, and share health progress with stakeholders more broadly. Supervisors can use program dashboards to help articulate their CHW cohorts activities and how they align with program impact standards and indicators. Summary statistics of CHW service area performance (e.g. number of home visits, number of protocol errors, etc) help to identify areas for continued improvement and deeper audits of care data.</p> -<p>The data that can be visualized is highly configurable, and depends on what data fields are configured in a particular CHT app’s forms. Dashboards can be built using any software that supports visualizing data with the widely used PostgreSQL database. Examples include the open source tool <a href="https://superset.incubator.apache.org">Superset</a>, as well as proprietary technologies like <a href="https://www.klipfolio.com">Klipfolio</a> and <a href="https://www.tableau.com">Tableau</a>.</p> -<h2 id="dhis2-data-verification">DHIS2 Data Verification</h2> -<p>Supervisors often provide a critical bridge between CHWs and broader health system reporting. Using the CHT’s <a href="https://docs.communityhealthtoolkit.org/apps/features/integrations/dhis2/">DHIS2 integration</a>, Supervisors can see the aggregate of each DHIS2 Data Value across all CHWs in their area. By tapping on a target, they can also see each CHW’s contribution towards that total. Once the Supervisor has verified data accuracy with CHWs, they can communicate with Health Records Information Officers to feed data into the national health information system.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -DHIS2 integration was introduced in v3.9. -</div>Apps: Taskshttps://docs.communityhealthtoolkit.org/apps/features/tasks/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/tasks/ -<p>Tasks help CHWs plan their day by prompting them to complete follow-up visits and other activities. The list might include upcoming scheduled ANC or Immunization visits, treatment or referral follow-ups, or other required activities such as a household survey.</p> -<figure class="left col-3 col-lg-3"><a href="tasks-mobile.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/tasks/tasks-mobile.png"/> </a> -</figure> -<figure class="left col-9 col-lg-9"><a href="tasks-desktop.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/tasks/tasks-desktop.png"/> </a> -</figure> -<h2 id="main-list">Main List</h2> -<figure class="right col-6 col-lg-3"><a href="tasks-mobile.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/tasks/tasks-mobile.png"/> </a> -</figure> -<p>On the Tasks tab is a consolidated list of tasks for all people and families that the user looks after. The task definition determines how long the task will show on this list before and after it is due.</p> -<p>Each task has an icon on the left side which indicates which type of task it is. The first bold line of text is the person or family that the task is about. The second line of text is the name of the task. The due date for the task is located in the upper right-hand corner. If a task is overdue, the due date will be red.</p> -<p>Tasks are listed in order of due date. Tasks that are past due will appear at the top of the list. CHWs should strive to complete tasks before they are overdue. Many programs add targets to track task completion and timeliness.</p> -<br clear="all"> -<h2 id="care-guides">Care Guides</h2> -<p>When a CHW clicks on a task, the care guide configured for that task displays. CHWs are then guided through questions for that specific workflow.</p> -<div class="container"> -<div class="row"> -<figure class="col-6 col-lg-3"><a href="tasks-care1.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/tasks/tasks-care1.png"/> </a> -</figure> -<figure class="col-6 col-lg-3"><a href="tasks-care2.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/tasks/tasks-care2.png"/> </a> -</figure> -<figure class="col-6 col-lg-3"><a href="tasks-care3.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/tasks/tasks-care3.png"/> </a> -</figure> -<figure class="col-6 col-lg-3"><a href="tasks-care4.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/tasks/tasks-care4.png"/> </a> -</figure> -</div> -</div> -<p>For more information on Care Guides, see the “<a href="">Decision Support for Care Guides</a>” section of this overview.</p> -<p>When the user completes the care guide, the task will be cleared from the Tasks tab, and the report will be accessible from the Reports page or on the profile of the person or place whom the report is about.</p> -<h2 id="household-tasks">Household Tasks</h2> -<figure class="right col-6 col-lg-3"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/tasks/tasks-household.png"/> -</figure> -<p>Alternatively, there is an option to configure Household Tasks. When this <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-permissions/">permission</a> is enabled, once a CHW has completed a task, they are taken to the Other Household Tasks page. This page shows the CHW all the additional outstanding tasks within the same household in which the initial task was completed.</p> -<p>CHWs are able to complete tasks directly from this page, or exit by tapping on the “X”. If the household has no additional tasks, they will return directly to the main task list.</p> -<h2 id="profile-page">Profile Page</h2> -<figure class="right col-6 col-lg-3"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/tasks/tasks-profile.png"/> -</figure> -<p>Tasks are also accessed from a the People tab in the app.</p> -<p>Tasks for a particular person or place can be viewed on the contact’s profile in the “Tasks” section. Filters allow you to choose how many tasks you’d like to view for each due date.</p> -<br clear="all">Apps: Targetshttps://docs.communityhealthtoolkit.org/apps/features/targets/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/targets/ -<!-- ## Targets: Performance Dashboards --> -<p><em>Targets</em> is the user dashboard or analytics tab. The widgets on this tab provide a summary or analysis of the data in submitted reports. These widgets can be configured to track metrics for an individual CHW, for a Supervisor overseeing a group of CHWs, or for an entire health facility.</p> -<p>For CHWs, the <strong>Targets</strong> tab provides a quick summary of their progress towards their individual goals. For Supervisors, Nurses, and facility-based users, the <strong>Targets</strong> tab provides important insights into how their community unit is performing.</p> -<figure class="left col-3 col-lg-3"><a href="targets-mobile.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/targets/targets-mobile.png"/> </a> -</figure> -<figure class="left col-9 col-lg-9"><a href="targets-desktop.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/targets/targets-desktop.png"/> </a> -</figure> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Targets or goals can be configured for any user that has offline capabilities. A user must have access to the report in order to generate the widget with its data. -</div> -<h2 id="types-of-widgets">Types of Widgets</h2> -<p>There are two basic types of widgets: count and percent. <em>Count widgets</em> display a numeric sum while <em>percent widgets</em> display progress towards achieving a target.</p> -<p>The text, icon, goal, and time frame of each widget is easily configured. The time frame is set per widget, and set to show values for &ldquo;this month&rdquo; (resets back to zero at the beginning of each month) or &ldquo;all time&rdquo; (a cumulative total).</p> -<br clear="all"> -<h3 id="count-widgets">Count Widgets</h3> -<figure class="right col-7 col-lg-4"><a href="targets-count.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/targets/targets-count.png"/> </a> -</figure> -<p>Count widgets show a tally of a particular report that has been submitted or data within a report that matches a set of criteria. For example, a count can be done for the number of new pregnancies, the number of facility-based deliveries, or the number of households registered that month.</p> -<p>A count without a goal displays a simple green number count. A count with a goal displays the value of the goal on the right side and a colored count in the center indicating progress towards achieving the goal. Progress is displayed in green if the count is equal to or above the goal, or in black if the count is below the goal.</p> -<h3 id="percent-widgets">Percent Widgets</h3> -<figure class="right col-7 col-lg-4"><a href="targets-percentage.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/targets/targets-percentage.png"/> </a> -</figure> -<p>Percent widgets display a ratio, which helps to provide insight into the proportion that matches a defined criteria. For example, the proportion of newborns delivered in a facility can be presented as a percent with respect to all registered deliveries.</p> -<p>An optional goal can be set, such as “100% of patients with a fever should be given a malaria Rapid Diagnostic Test (mRDT),” to visualize progress towards achieving a target. Widget styling is configured to show green if the goal has been met and black if the goal has not been met. Next to the percent with a goal, the count of reports used in the calculation are shown (e.g. “16 of 20 with mRDT”). CHWs have found this helpful in interpreting target information.</p>Apps: Traininghttps://docs.communityhealthtoolkit.org/apps/features/training/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/training/ -<p><em>Training Cards</em> help health workers learn about changes to CHT apps remotely, directly in their app. Training content might include information about a newly deployed feature, changes to a <a href="https://docs.communityhealthtoolkit.org/apps/concepts/care-guides/">care guide</a>, or simply a reminder about an underused feature or workflow. They are perfect for training on a very specific topic and are not meant to replace a comprehensive onboarding program.</p> -<p><a href="https://docs.communityhealthtoolkit.org/apps/guides/training/training-cards-resources/">Template training content</a> for new CHT features is made available from time to time. These templates can be customized to the local context.</p> -<figure class="col-12 col-lg-10"><a href="training-deck.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/training/training-deck.png"/> </a> -</figure> -<h2 id="accessing">Accessing</h2> -<p>When health workers open or reload their app, configured training cards will automatically show on top of all other content in the app. If it is not a convenient time to complete the training, they can cancel out at any time and will be prompted again later whenever they open or reload their app (training will start from the beginning).</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -If there are multiple training sets configured to start on the same day, the CHT will determine the order alphabetically based on the form ID. Subsequent training sets will only be displayed once the previous ones are either completed or no longer valid AND the app is opened or reloaded again. -</div> -<h2 id="completing">Completing</h2> -<p>Health workers read through each card one by one in a predefined sequence, tapping “Next” on each card. When they are finished reading all cards, they tap “Submit” on the last card. The training set is now considered complete and they can continue using their app. Completed training sets show up on the <a href="https://docs.communityhealthtoolkit.org/apps/features/reports/#main-list">main list</a> of the Reports tab and they won’t be asked to complete this set again. If there are additional training sets to complete, they will be shown the next time the app is opened or reloaded.</p> -<h2 id="monitoring">Monitoring</h2> -<p>As mentioned above, completed training sets will show up on the <a href="https://docs.communityhealthtoolkit.org/apps/features/reports/#main-list">main list</a> of the Reports tab. These reports are available in <a href="https://docs.communityhealthtoolkit.org/apps/features/supervision/#supervisor-dashboards">analytics</a>, <a href="https://docs.communityhealthtoolkit.org/apps/features/supervision/#chw-aggregate-targets">aggregate targets</a> , and can trigger supervision workflows and <a href="https://docs.communityhealthtoolkit.org/apps/features/supervision/#supervisor-tasks">tasks</a>.</p> -<h2 id="configurability">Configurability</h2> -<p>Training cards can be shown to any user associated to a contact. A “set” of training cards represents a collection of individual training cards, generally covering a single training topic. The list below highlights some of the key areas of customization:</p> -<ul> -<li>Number of sets</li> -<li>Number of cards in a set</li> -<li>Content of each individual card (text, images, etc…)</li> -<li>When health workers will start seeing a set (specific start date)</li> -<li>How long the set will be available (# of days, relative to the start date)</li> -<li>Which users will see the set (based on user roles)</li> -</ul>Apps: Mutinghttps://docs.communityhealthtoolkit.org/apps/features/muting/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/muting/ -<p>Muting is a way for CHWs to temporarily silence notifications about Contacts (people and places) and is commonly used when a person or family has temporarily relocated or refused services. When a Contact is muted, they will appear differently on the People tab and CHWs will no longer receive tasks or SMS about them.</p> -<p>To start receiving notifications about a Contact again, CHWs can <em>unmute</em> them. When a Contact is unmuted, tasks and SMS schedules will resume, but notifications that would have been sent while they were muted will not.</p> -<h3 id="mute-a-contact">Mute a Contact</h3> -<p>To mute a Contact, CHWs typically submit a &ldquo;Mute&rdquo; form about them. This can be done either from the People tab or the Reports tab. When a person is muted, in addition to their notifications being silenced, their icon turns grey and a &ldquo;Muted&rdquo; status is displayed beneath their name. While a person or place is muted, CHWs and Nurses can still submit other forms about them.</p> -<figure class=" right col-9 col-lg-9"><a href="muted_person.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/muting/muted_person.png"/> </a> -</figure> -<p>Some changes are observed in the UI when a family or a person is muted to make the muted status clear and easily recognizable. The person&rsquo;s or family&rsquo;s icon turns grey and a status of “Muted” displays on the second line below the person&rsquo;s or family&rsquo;s name. Any actions that were previously available on the family or person remain available on the profile.</p> -<p>When a place is muted, all places and people beneath it will automatically be muted as well. For example, if a family is muted, all individuals in that family will automatically be muted. It is not possible to have a muted family with some family members that are not muted. This means that if a new person is added to a muted family, that person will automatically be muted when they are created.</p> -<h3 id="unmute-a-contact">Unmute a Contact</h3> -<p>To unmute a Contact, CHWs typically submit an &ldquo;Unmute&rdquo; form. This will remove the &ldquo;Mute&rdquo; styling, resume notifications, and also unmute all places above them in the hierarchy. For example, if a family member is unmuted, the entire family will automatically be unmuted since it is not possible to have an unmuted person in a muted family.</p> -<h3 id="configurability">Configurability</h3> -<p>While it&rsquo;s most common to have dedicated mute and unmute forms, any form can be set up to mute or unmute a Contact. It&rsquo;s also possible to set up verification flows for muting whereby mute/unmute requires a Supervisor to verify before the mute/unmute happens. And like the rest of the CHT, all text is customizable.</p> -<h3 id="muting-vs-death-reporting">Muting vs Death Reporting</h3> -<p>A project may support both death reporting and muting - they are not mutually exclusive. Death reporting moves the deceased person to a different part of the family members list and does not allow actions. Muting keeps the person in the family members list, allows actions and disables schedules.</p> -<table> -<thead> -<tr> -<th>Death reporting</th> -<th>Muting</th> -</tr> -</thead> -<tbody> -<tr> -<td><p></p><ul><li>Permanent state</li></ul></td> -<td><p></p><ul><li>Temporary state</li></ul></td> -</tr> -<tr> -<td><p></p><ul><li>Only allowed at the individual level</li></ul></td> -<td><p></p><ul><li>Place, family, or individual level</li></ul></td> -</tr> -<tr> -<td><p></p><ul><li>Removes schedules</li></ul></td> -<td><p></p><ul><li>“Quiets” notifications for schedules</li></ul></td> -</tr> -<tr> -<td><p></p><ul><li>A deceased individual is removed from the family list</li></ul></td> -<td><p></p><ul><li>A muted individual is not removed from the family list<br></li></ul></td> -</tr> -<tr> -<td><p></p><ul><li><p>No new actions can be performed</p><p>except for one - reverse the death</p></li></ul></td> -<td><p></p><ul><li>New actions may be performed, but no tasks or notifications will be sent<br></li></ul></td> -</tr> -<tr> -<td><p></p><ul><li>Manager confirmation configurable</li></ul></td> -<td><p></p><ul><li>Manager confirmation configurable</li></ul></td> -</tr> -</tbody> -</table>Apps: Universal Health Coverage Modehttps://docs.communityhealthtoolkit.org/apps/features/uhc-mode/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/uhc-mode/ -<div class="pageinfo pageinfo-primary"> -<p>CHT apps using the <em>UHC Mode</em> empower CHWs to provide equitable and timely care to families in their catchment area. This feature was <a href="https://www.musohealth.org/post/new-study-demonstrates-how-digital-health-tools-can-enable-progress-towards-universal-health-care">codeveloped with Muso</a>, and showed an increase in household coverage for both rural and peri-urban settings, thereby increasing the effectiveness of CHWs and improving access to care for the populations they serve.</p> -</div> -<h2 id="uhc-with-the-cht">UHC with the CHT</h2> -<p>Apps built using the Community Health Toolkit can be used in support of community-based services, and increase Universal Health Coverage (UHC) by helping health workers regularly reach all the families that they care for. A Muso study in collaboration with Medic, <a href="https://drive.google.com/file/d/1fXruezV7sCo-CtJfi8WDivzgMcKZNdXM/view">showed an increase in household coverage</a>: <em>UHC Mode should be considered an effective tool that can improve minimum expected home visit coverage and promote progress towards UHC when implemented in the proactive community case management context.</em></p> -<h2 id="prioritizing-households">Prioritizing Households</h2> -<p> -<figure class="right col-6 col-lg-3"><a href="UHC.gif"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/uhc-mode/UHC.gif" -alt="UHC Mode screenshot"/> </a> -</figure> -<figure class="right col-6 col-lg-3"><a href="sort-dropdown.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/uhc-mode/sort-dropdown.png" -alt="Contact sorting screenshot"/> </a> -</figure> -</p> -<p>The <em>UHC Mode</em> in the CHT allows health workers to see when a household within their care area was last visited, and prioritize visits accordingly.</p> -<p>When using the <em>UHC Mode</em>, the households in the contact list can be sorted by when they were last visited. The days since the last visit is also shown in the app, along with the number of visits made to a household in a month period.</p> -<h2 id="configurability">Configurability</h2> -<p>The last visited date is calculated based on the number of days since an action was taken for that household, and the number of visits reflects the actions taken for that household in the current month. What constitutes as an action for a household, along with the start date for the reporting period, <a href="https://docs.communityhealthtoolkit.org/apps/guides/forms/uhc-mode/">are configurable</a> to CHT app developers.</p>Apps: App Managementhttps://docs.communityhealthtoolkit.org/apps/features/admin/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/admin/ -<p><em>App Management</em> is an interface for non-technical administrative users. With it you can manage users and make minor changes to the app, such as setting the SMS gateway phone number, and changing the default language for the app.</p> -<p>The <strong>App Management</strong> pages are a desktop-only interface meant for users with a reliable internet connection.</p> -<p> -<figure class="col-12 col-lg-10"><a href="admin-roles.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/features/admin/admin-roles.png"/> </a> -</figure> -<br clear="all"></p> -<h2 id="page-tabs">Page Tabs</h2> -<p>These sections of the App can be configured from within the Admin Console:</p> -<ul> -<li><strong>Settings</strong>: Change basic settings like gateway phone number &amp; country code</li> -<li><strong>Languages</strong>: Set default app language, update translations</li> -<li><strong>Forms</strong>: Upload XML and JSON forms</li> -<li><strong>Import &amp; Export</strong>: Import and export settings</li> -<li><strong>Upgrade Instance</strong>: Install a newer app version</li> -<li><strong>Users</strong>: View and edit users of the system</li> -<li><strong>Icons</strong>: View and edit icons used in the app</li> -<li><strong>Targets</strong>: Modify performance or activity targets</li> -<li><strong>Roles &amp; Permissions</strong>: Fine tuned control of user roles and permissions</li> -</ul> -<h2 id="app-management-vs-cht-conf">App Management vs cht-conf</h2> -<p>In general, everything that can be done in the <strong>Admin Console</strong> can also be done in command line tools, but not everything in the command line tools can be done in the <strong>Admin Console</strong>.</p> -<p>The <strong>Admin Console</strong> does not track changes. For most app development, using command line tools such as cht-conf and tracking files using a version control system is recommended</p> -<p><strong>In Admin Console But Not Command Line Tools:</strong></p> -<ul> -<li>User management</li> -<li>~Upgrades~</li> -</ul> -<p><strong>In Command Line Tools But Not Admin Console:</strong></p> -<ul> -<li>Most of the JSON settings</li> -<li>XLS → Xform conversion</li> -</ul>Apps: Integrationshttps://docs.communityhealthtoolkit.org/apps/features/integrations/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/integrations/ \ No newline at end of file +Features of CHT Applications on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/features/Recent content in Features of CHT Applications on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file diff --git a/apps/features/integrations/android/index.html b/apps/features/integrations/android/index.html index 926bf9afc3..9595480698 100644 --- a/apps/features/integrations/android/index.html +++ b/apps/features/integrations/android/index.html @@ -1,9 +1,9 @@ -Android | Community Health Toolkit +Android | Community Health Toolkit

Android

Launch the Android App with a link or from another app

The CHT Android application can be launched by clicking on a link or invoking an intent in another Android app. This is useful for enabling login by SMS, directing a user to a specific page, and integrating between Android applications.

Sending a URL

When the user clicks on a link to a CHT instance from an SMS, email, WhatsApp, or any other app, Android will prompt the user to choose whether to open the URL in the Android app or the browser. If a CHT app is not installed then the URL will be opened in the browser.

The prompt may look different depending on the version of Android being used. Up to Android 11, users can choose “Always” to skip this prompt in the future. Starting with Android 12, users will be promped to associate the CHT instance’s domain with the Android app. Alternatively, a CHT instance can be configured to have its links automatically verified for the associated Android App (with no user action required). See the docs on Android App Links verification for more information.

Using an intent

To have another Android application launch the CHT Android application, use an ACTION_VIEW intent, for example:

String url = "https://myapplication.app.medicmobile.org";
+ Create project issue

Android

Launch the Android App with a link or from another app

The CHT Android application can be launched by clicking on a link or invoking an intent in another Android app. This is useful for enabling login by SMS, directing a user to a specific page, and integrating between Android applications.

Sending a URL

When the user clicks on a link to a CHT instance from an SMS, email, WhatsApp, or any other app, Android will prompt the user to choose whether to open the URL in the Android app or the browser. If a CHT app is not installed then the URL will be opened in the browser.

The prompt may look different depending on the version of Android being used. Up to Android 11, users can choose “Always” to skip this prompt in the future. Starting with Android 12, users will be promped to associate the CHT instance’s domain with the Android app. Alternatively, a CHT instance can be configured to have its links automatically verified for the associated Android App (with no user action required). See the docs on Android App Links verification for more information.

Using an intent

To have another Android application launch the CHT Android application, use an ACTION_VIEW intent, for example:

String url = "https://myapplication.app.medicmobile.org";
 Intent i = new Intent(Intent.ACTION_VIEW);
 i.setData(Uri.parse(url));
 startActivity(i);
@@ -311,7 +311,8 @@
 Reference >
 app_settings.json >
 .assetlinks

Assetlinks: Defining the Digital Asset Links JSON file associating your domain with your Android app.

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/features/integrations/custom/index.html b/apps/features/integrations/custom/index.html index 967f3ec764..559f2920fd 100644 --- a/apps/features/integrations/custom/index.html +++ b/apps/features/integrations/custom/index.html @@ -1,9 +1,9 @@ -Custom | Community Health Toolkit +Custom | Community Health Toolkit

Custom

Integrate with any system using RESTful APIs

The CHT Core Framework includes functionality that allows sharing data with any API-based system. Developers have configured CHT integrations with OpenMRS, KenyaEMR, Bahmni, DHIS2, RapidPro, Apache NiFi, OpenHIM, custom electronic medical records (EMR), and several other systems.

Overview

Integrating a CHT App into your digital health ecosystem starts with identifying an integration use case. It’s important to first understand all the components present in the ecosystem (EMR, laboratory system, community health information system, etc) and then plan out what the workflow will look like operationally. It is important to consider what information is needed at each point, will it be available to them, what happens if it is not, is this workflow even useful for them.

One of the biggest challenges in developing integrations between systems is patient matching and/or deduplication. Sometimes this can be controlled operationally, other times it requires complicated algorithms or human intervention.

Below are a few example integration use cases:

  1. Lost to Follow-up: EMR generates a list of patients that require follow-up in the community, that list is sent to the CHT and healthworkers receive a task in the CHT to find those patients and refer them to the health facility.
  2. Referrals from the community: When a CHW does an assessment and determines the patient should be referred to a health facility, send the referral information to the EMR.
  3. Contact Tracing: Similar to Lost to Follow-Up, the EMR generates a list of contacts to be followed up with and this is sent to the CHT so that a tracer can call those contacts to see if they have symptoms.
  4. Interactive Messaging: Integrate with a messaging platform (such as RapidPro) to allow community members to initiate self-screening assessments, which can then be sent to the CHT for follow-up by a healthworker.

As you design your use cases, creating a sequence diagram will be helpful in illustrating what the flow will look like. Here is an example sequence diagram for an integration use case with RapidPro.

Integration Design Patterns

There are a number of different interactions that may occur between digital health systems. Below are some common use cases:

  1. Creating a patient in the CHT creates that patient in another system
  2. Creating a patient in another system creates that patient in the CHT
  3. Submitting a form in the CHT triggers an event in another system
  4. Submitting a form in the CHT sends data to another system
  5. Activity in another system triggers an event in the CHT
  6. Activity in another system stores the results in the CHT
  7. Another system needs to look up data in the CHT

Sending data to other systems

Using the outbound push feature, you can configure the CHT to send data to another system. Before starting, you’ll want to make sure you understand the APIs of the destination system and have login credentials with adequate privileges.

To send data to other systems from the CHT, you will need to do the following:

  1. Enable outbound in app_settings
  2. Specify when data is sent
  3. Specify where data is sent
  4. Specify what data is sent
  5. Set up credentials for the destination system

Enable outbound

Enable the mark_for_outbound transition in app_settings.

When data is sent

Whenever a document is changed (such as submitting a form, creating a new contact, or editing an existing one) you can configure outbound to send data to another system. The relevant_to property in the outbound configuration is used to identify which activities will trigger the sending of data.

Where data is sent

The destination property in the outbound configuration is used to specify where to send data. This will normally be the API endpoint of the destination system or interoperabiliy layer.

What data is sent

You configure what data is sent using the mapping property. You will map data from the CHT to the format required by the destination API endpoint.

Authentication

Credentials for the destination system are stored in CouchdDB. You will need to set this up before you can test your configuration.

Requests from other systems

The CHT has a complete RESTful API that other systems can utilize to interact with data in the CHT.

The most common uses are:

  1. Looking up data in the CHT from another system
  2. POSTing data to the CHT from another system

Look up data in the CHT

The CHT has a number of different API endpoints that can be used to look up data.

You can use the contacts_by_phone endpoint will return the fully hydrated contact information for those patients.

You can use the hydrate endpoint to obtain this information. to look up the complete information for that contact.

POST data to the CHT

The CHT API also allows you to POST data. Using these endpoints, you can create new records in your CHT API. You can store activities that took place in another system on that contact’s profile in the CHT, and even create tasks for CHWs in the CHT based on activities that took place in the other system.

You can do this by submitting a JSON Form to the records endpoint.

You would do this by simply configuring a task to be generated based on criteria available in the report that was created in Example 1.


CHT Applications > + Create project issue

Custom

Integrate with any system using RESTful APIs

The CHT Core Framework includes functionality that allows sharing data with any API-based system. Developers have configured CHT integrations with OpenMRS, KenyaEMR, Bahmni, DHIS2, RapidPro, Apache NiFi, OpenHIM, custom electronic medical records (EMR), and several other systems.

Overview

Integrating a CHT App into your digital health ecosystem starts with identifying an integration use case. It’s important to first understand all the components present in the ecosystem (EMR, laboratory system, community health information system, etc) and then plan out what the workflow will look like operationally. It is important to consider what information is needed at each point, will it be available to them, what happens if it is not, is this workflow even useful for them.

One of the biggest challenges in developing integrations between systems is patient matching and/or deduplication. Sometimes this can be controlled operationally, other times it requires complicated algorithms or human intervention.

Below are a few example integration use cases:

  1. Lost to Follow-up: EMR generates a list of patients that require follow-up in the community, that list is sent to the CHT and healthworkers receive a task in the CHT to find those patients and refer them to the health facility.
  2. Referrals from the community: When a CHW does an assessment and determines the patient should be referred to a health facility, send the referral information to the EMR.
  3. Contact Tracing: Similar to Lost to Follow-Up, the EMR generates a list of contacts to be followed up with and this is sent to the CHT so that a tracer can call those contacts to see if they have symptoms.
  4. Interactive Messaging: Integrate with a messaging platform (such as RapidPro) to allow community members to initiate self-screening assessments, which can then be sent to the CHT for follow-up by a healthworker.

As you design your use cases, creating a sequence diagram will be helpful in illustrating what the flow will look like. Here is an example sequence diagram for an integration use case with RapidPro.

Integration Design Patterns

There are a number of different interactions that may occur between digital health systems. Below are some common use cases:

  1. Creating a patient in the CHT creates that patient in another system
  2. Creating a patient in another system creates that patient in the CHT
  3. Submitting a form in the CHT triggers an event in another system
  4. Submitting a form in the CHT sends data to another system
  5. Activity in another system triggers an event in the CHT
  6. Activity in another system stores the results in the CHT
  7. Another system needs to look up data in the CHT

Sending data to other systems

Using the outbound push feature, you can configure the CHT to send data to another system. Before starting, you’ll want to make sure you understand the APIs of the destination system and have login credentials with adequate privileges.

To send data to other systems from the CHT, you will need to do the following:

  1. Enable outbound in app_settings
  2. Specify when data is sent
  3. Specify where data is sent
  4. Specify what data is sent
  5. Set up credentials for the destination system

Enable outbound

Enable the mark_for_outbound transition in app_settings.

When data is sent

Whenever a document is changed (such as submitting a form, creating a new contact, or editing an existing one) you can configure outbound to send data to another system. The relevant_to property in the outbound configuration is used to identify which activities will trigger the sending of data.

Where data is sent

The destination property in the outbound configuration is used to specify where to send data. This will normally be the API endpoint of the destination system or interoperabiliy layer.

What data is sent

You configure what data is sent using the mapping property. You will map data from the CHT to the format required by the destination API endpoint.

Authentication

Credentials for the destination system are stored in CouchdDB. You will need to set this up before you can test your configuration.

Requests from other systems

The CHT has a complete RESTful API that other systems can utilize to interact with data in the CHT.

The most common uses are:

  1. Looking up data in the CHT from another system
  2. POSTing data to the CHT from another system

Look up data in the CHT

The CHT has a number of different API endpoints that can be used to look up data.

You can use the contacts_by_phone endpoint will return the fully hydrated contact information for those patients.

You can use the hydrate endpoint to obtain this information. to look up the complete information for that contact.

POST data to the CHT

The CHT API also allows you to POST data. Using these endpoints, you can create new records in your CHT API. You can store activities that took place in another system on that contact’s profile in the CHT, and even create tasks for CHWs in the CHT based on activities that took place in the other system.

You can do this by submitting a JSON Form to the records endpoint.

You would do this by simply configuring a task to be generated based on criteria available in the report that was created in Example 1.


CHT Applications > Features > Integrations > DHIS2

Send aggregate, patient, and event data to DHIS2

CHT Applications > @@ -310,7 +310,8 @@ Features > Integrations > OpenMRS

Exchange patient-level data with systems based on the OpenMRS platform

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/features/integrations/dhis2/index.html b/apps/features/integrations/dhis2/index.html index 9d8316537d..295be3c6bc 100644 --- a/apps/features/integrations/dhis2/index.html +++ b/apps/features/integrations/dhis2/index.html @@ -1,9 +1,9 @@ -DHIS2 | Community Health Toolkit +DHIS2 | Community Health Toolkit

DHIS2

Send aggregate, patient, and event data to DHIS2

Most health systems have regular reporting requirements for community-level activities. Health workers often carry around heavy logbooks to manually record all relevant activities. When it is time to submit their data, community health workers (CHWs) summarize what was recorded in their logbooks and share this information with their supervisors, who in turn create paper records of these totals across entire community units or health facilities. This paper record is often passed to yet another individual whose responsibility is to manually key in the data into a health information management system, such as DHIS2.

In communities using digital health apps that do not integrate with DHIS2, it is highly likely that health workers are duplicating efforts by recording the same information both in their app and in their logbook(s). For example, they are not only registering new pregnancies in their app, but they are also manually recording this in their logbooks, manually adding them up at the end of the month, and then someone else is manually keying this into DHIS2.

In communities using digital health apps built with the CHT, health systems can reduce or eliminate the need to complete paper based forms for DHIS2 reporting needs. This gives health workers more time to focus on caring for the families in their community while also increasing accuracy and timeliness of their DHIS2 reporting requirements.

Overview

The CHT Core Framework supports integrations with DHIS2 in a variety of ways:

  1. Sending patient data
  2. Sending event data
  3. Sending pre-aggregated “Service Reports”
  4. Aggregating data across multiple health workers into DHIS2 Data Values
  5. Exporting a file that can be imported into DHIS2 as a Data Set
  6. Exposing an API for DHIS2 app developers to pull aggregate data from CHT Core
  7. Receiving data from DHIS2

Sending patient, event, and pre-aggregated data can be achieved using the Outbound push feature. Receiving data from DHIS2 can be achieved using the CHT Core Web API.

Aggregating data across multiple health workers requires a somewhat specific workflow and was designed with three key user personas in mind. The aggregate workflow is described in more detail below.

Aggregate Workflow

The aggregate workflow was designed specifically for CHWs, Supervisors, and Health Records Information Officers (HRIO) but may be adapted to other contexts.

CHW (offline user): Conducts home visits and records information in the app. Reviews aggregate data throughout the month and makes sure to sync at the end of the month.

Supervisor (offline user): Provides supervision to the CHWs, reviews and verifies aggregate data for an entire community unit.

HRIO (online user): Exports a file from the CHT and imports into DHIS2. Reviews data in DHIS2.

Community Health Workers

CHWs support patients in their community by following care guides and recording responses in the CHT. The CHT calculates aggregate DHIS2 Data Values for each CHW based on rules configured in the CHT. CHWs can view these indicators on the Targets tab and should review them and sync at the end of the month.

CHWs

See Also: Targets

Supervisors

In addition to their own targets, Supervisors can see the aggregate of each DHIS2 Data Value across all CHWs in their area from the CHW Aggregate view on the Targets tab. By tapping on a target, they can also see each CHW’s contribution towards that total. Supervisors can review and verify with CHWs that everyone has synced and that their data is correct. The Supervisor can communicate with the HRIO when everything has been validated.

Supervisors

Health Records Information Officers

HRIOs access the CHT App Management tab and select the appropriate DHIS2 Data Set, Organisation Unit, and Period. They then Export a file that is formatted for DHIS2. HRIOs will need access to the Import/Export feature in DHIS2 so that they can Import the file. Once it has been imported into DHIS2, they review the data from the Data Entry screen in DHIS2.

Data Entry

DHIS2 apps

The CHT also includes an API that can be called from other applications that returns DHIS2 Data Sets. This means that you can build a DHIS2 app that pulls data from the CHT and imports it electronically into DHIS2. This would allow Data Entry to control the process directly from DHIS2 without having to access the CHT.

Data Entry

Version Notes

FeatureCHT Core version
Calculate DHIS2 Data Values by aggregating data from CHT Core reports3.9.0
Export file from CHT Core that can be imported into DHIS2 as a Data Set3.9.0

CHT Applications > + Create project issue

DHIS2

Send aggregate, patient, and event data to DHIS2

Most health systems have regular reporting requirements for community-level activities. Health workers often carry around heavy logbooks to manually record all relevant activities. When it is time to submit their data, community health workers (CHWs) summarize what was recorded in their logbooks and share this information with their supervisors, who in turn create paper records of these totals across entire community units or health facilities. This paper record is often passed to yet another individual whose responsibility is to manually key in the data into a health information management system, such as DHIS2.

In communities using digital health apps that do not integrate with DHIS2, it is highly likely that health workers are duplicating efforts by recording the same information both in their app and in their logbook(s). For example, they are not only registering new pregnancies in their app, but they are also manually recording this in their logbooks, manually adding them up at the end of the month, and then someone else is manually keying this into DHIS2.

In communities using digital health apps built with the CHT, health systems can reduce or eliminate the need to complete paper based forms for DHIS2 reporting needs. This gives health workers more time to focus on caring for the families in their community while also increasing accuracy and timeliness of their DHIS2 reporting requirements.

Overview

The CHT Core Framework supports integrations with DHIS2 in a variety of ways:

  1. Sending patient data
  2. Sending event data
  3. Sending pre-aggregated “Service Reports”
  4. Aggregating data across multiple health workers into DHIS2 Data Values
  5. Exporting a file that can be imported into DHIS2 as a Data Set
  6. Exposing an API for DHIS2 app developers to pull aggregate data from CHT Core
  7. Receiving data from DHIS2

Sending patient, event, and pre-aggregated data can be achieved using the Outbound push feature. Receiving data from DHIS2 can be achieved using the CHT Core Web API.

Aggregating data across multiple health workers requires a somewhat specific workflow and was designed with three key user personas in mind. The aggregate workflow is described in more detail below.

Aggregate Workflow

The aggregate workflow was designed specifically for CHWs, Supervisors, and Health Records Information Officers (HRIO) but may be adapted to other contexts.

CHW (offline user): Conducts home visits and records information in the app. Reviews aggregate data throughout the month and makes sure to sync at the end of the month.

Supervisor (offline user): Provides supervision to the CHWs, reviews and verifies aggregate data for an entire community unit.

HRIO (online user): Exports a file from the CHT and imports into DHIS2. Reviews data in DHIS2.

Community Health Workers

CHWs support patients in their community by following care guides and recording responses in the CHT. The CHT calculates aggregate DHIS2 Data Values for each CHW based on rules configured in the CHT. CHWs can view these indicators on the Targets tab and should review them and sync at the end of the month.

CHWs

See Also: Targets

Supervisors

In addition to their own targets, Supervisors can see the aggregate of each DHIS2 Data Value across all CHWs in their area from the CHW Aggregate view on the Targets tab. By tapping on a target, they can also see each CHW’s contribution towards that total. Supervisors can review and verify with CHWs that everyone has synced and that their data is correct. The Supervisor can communicate with the HRIO when everything has been validated.

Supervisors

Health Records Information Officers

HRIOs access the CHT App Management tab and select the appropriate DHIS2 Data Set, Organisation Unit, and Period. They then Export a file that is formatted for DHIS2. HRIOs will need access to the Import/Export feature in DHIS2 so that they can Import the file. Once it has been imported into DHIS2, they review the data from the Data Entry screen in DHIS2.

Data Entry

DHIS2 apps

The CHT also includes an API that can be called from other applications that returns DHIS2 Data Sets. This means that you can build a DHIS2 app that pulls data from the CHT and imports it electronically into DHIS2. This would allow Data Entry to control the process directly from DHIS2 without having to access the CHT.

Data Entry

Version Notes

FeatureCHT Core version
Calculate DHIS2 Data Values by aggregating data from CHT Core reports3.9.0
Export file from CHT Core that can be imported into DHIS2 as a Data Set3.9.0

CHT Applications > Quick Guides > Integrations > DHIS2 Aggregate

Design considerations and how to configure

CHT Applications > Reference > app_settings.json > .dhis_data_sets

DHIS2 Integration: Instructions and schema for defining DHIS2 integrations

-

Last modified 19.04.2021: Updates headers (#467) (b33dc854)
\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/features/integrations/index.html b/apps/features/integrations/index.html index 0842113905..11be329c19 100644 --- a/apps/features/integrations/index.html +++ b/apps/features/integrations/index.html @@ -1,9 +1,9 @@ -Integrations | Community Health Toolkit +Integrations | Community Health Toolkit

Integrations

Exchange data with other systems

Android

Launch the Android App with a link or from another app

Custom

Integrate with any system using RESTful APIs

DHIS2

Send aggregate, patient, and event data to DHIS2

OpenMRS

Exchange patient-level data with systems based on the OpenMRS platform

OppiaMobile

Integrate CHT core with OppiaMobile’s learning management platform

RapidPro

Integrate interactive messaging conversations into your workflows


CHT Applications > + Create project issue

Integrations

Exchange data with other systems

Android

Launch the Android App with a link or from another app

Custom

Integrate with any system using RESTful APIs

DHIS2

Send aggregate, patient, and event data to DHIS2

OpenMRS

Exchange patient-level data with systems based on the OpenMRS platform

OppiaMobile

Integrate CHT core with OppiaMobile’s learning management platform

RapidPro

Integrate interactive messaging conversations into your workflows


CHT Applications > Quick Guides > Integrations

How to build integrations with external software and data sources

-

Last modified 09.03.2023: Training card guide (#970) (fb07ed89)
\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/features/integrations/index.xml b/apps/features/integrations/index.xml index 7104e5f887..938557bd7b 100644 --- a/apps/features/integrations/index.xml +++ b/apps/features/integrations/index.xml @@ -1,190 +1,4 @@ -Community Health Toolkit – Integrationshttps://docs.communityhealthtoolkit.org/apps/features/integrations/Recent content in Integrations on Community Health ToolkitHugo -- gohugo.ioenApps: Androidhttps://docs.communityhealthtoolkit.org/apps/features/integrations/android/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/integrations/android/ -<p>The <a href="https://github.com/medic/cht-android">CHT Android application</a> can be launched by clicking on a link or invoking an intent in another Android app. This is useful for enabling login by SMS, directing a user to a specific page, and integrating between Android applications.</p> -<h2 id="sending-a-url">Sending a URL</h2> -<p> -<figure class="right col-6 col-md-4 col-lg-2"><a href="../../../guides/android/branding/android-12-prompt.png"> -<img src="../../../guides/android/branding/android-12-prompt.png"/> </a> -</figure> -<figure class="right col-4 col-lg-3"><a href="android-prompt.png"> -<img src="android-prompt.png"/> </a> -</figure> -</p> -<p>When the user clicks on a link to a CHT instance from an SMS, email, WhatsApp, or any other app, Android will prompt the user to choose whether to open the URL in the Android app or the browser. If a CHT app is not installed then the URL will be opened in the browser.</p> -<p>The prompt may look different depending on the version of Android being used. Up to Android 11, users can choose &ldquo;Always&rdquo; to skip this prompt in the future. Starting with Android 12, users will be promped to associate the CHT instance&rsquo;s domain with the Android app. Alternatively, a CHT instance can be configured to have its links automatically verified for the associated Android App (with no user action required). See the docs on <a href="https://docs.communityhealthtoolkit.org/apps/guides/android/branding/#android-app-links-verification">Android App Links verification</a> for more information.</p> -<h2 id="using-an-intent">Using an intent</h2> -<p>To have another Android application launch the CHT Android application, use an <a href="https://developer.android.com/reference/android/content/Intent.html#ACTION_VIEW">ACTION_VIEW</a> intent, for example:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#000">String</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">url</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#34;https://myapplication.app.medicmobile.org&#34;</span><span style="color:#000;font-weight:bold">;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#000">Intent</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">i</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">new</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">Intent</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">Intent</span><span style="color:#000;font-weight:bold">.</span><span style="color:#c4a000">ACTION_VIEW</span><span style="color:#000;font-weight:bold">);</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#000">i</span><span style="color:#000;font-weight:bold">.</span><span style="color:#c4a000">setData</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">Uri</span><span style="color:#000;font-weight:bold">.</span><span style="color:#c4a000">parse</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">url</span><span style="color:#000;font-weight:bold">));</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#000">startActivity</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">i</span><span style="color:#000;font-weight:bold">);</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span></code></pre></div>Apps: Customhttps://docs.communityhealthtoolkit.org/apps/features/integrations/custom/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/integrations/custom/ -<p>The CHT Core Framework includes functionality that allows sharing data with any API-based system. Developers have configured CHT integrations with OpenMRS, KenyaEMR, Bahmni, DHIS2, RapidPro, Apache NiFi, OpenHIM, custom electronic medical records (EMR), and several other systems.</p> -<h2 id="overview">Overview</h2> -<p>Integrating a CHT App into your digital health ecosystem starts with identifying an integration use case. It&rsquo;s important to first understand all the components present in the ecosystem (EMR, laboratory system, community health information system, etc) and then plan out what the workflow will look like operationally. It is important to consider what information is needed at each point, will it be available to them, what happens if it is not, is this workflow even useful for them.</p> -<p>One of the biggest challenges in developing integrations between systems is patient matching and/or <a href="https://en.wikipedia.org/wiki/Data_deduplication">deduplication</a>. Sometimes this can be controlled operationally, other times it requires complicated algorithms or human intervention.</p> -<p>Below are a few example integration use cases:</p> -<ol> -<li><strong>Lost to Follow-up</strong>: EMR generates a list of patients that require follow-up in the community, that list is sent to the CHT and healthworkers receive a task in the CHT to find those patients and refer them to the health facility.</li> -<li><strong>Referrals from the community</strong>: When a CHW does an assessment and determines the patient should be referred to a health facility, send the referral information to the EMR.</li> -<li><strong>Contact Tracing</strong>: Similar to Lost to Follow-Up, the EMR generates a list of contacts to be followed up with and this is sent to the CHT so that a tracer can call those contacts to see if they have symptoms.</li> -<li><strong>Interactive Messaging</strong>: Integrate with a messaging platform (such as RapidPro) to allow community members to initiate self-screening assessments, which can then be sent to the CHT for follow-up by a healthworker.</li> -</ol> -<p>As you design your use cases, creating a <a href="https://www.websequencediagrams.com/">sequence diagram</a> will be helpful in illustrating what the flow will look like. <a href="https://docs.communityhealthtoolkit.org/apps/guides/integrations/rapidpro/#sequence-diagrams">Here</a> is an example sequence diagram for an integration use case with RapidPro.</p> -<h2 id="integration-design-patterns">Integration Design Patterns</h2> -<p>There are a number of different interactions that may occur between digital health systems. Below are some common use cases:</p> -<ol> -<li>Creating a patient in the CHT creates that patient in another system</li> -<li>Creating a patient in another system creates that patient in the CHT</li> -<li>Submitting a form in the CHT triggers an event in another system</li> -<li>Submitting a form in the CHT sends data to another system</li> -<li>Activity in another system triggers an event in the CHT</li> -<li>Activity in another system stores the results in the CHT</li> -<li>Another system needs to look up data in the CHT</li> -</ol> -<h2 id="sending-data-to-other-systems">Sending data to other systems</h2> -<p>Using the <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/outbound/">outbound push</a> feature, you can configure the CHT to send data to another system. Before starting, you&rsquo;ll want to make sure you understand the APIs of the destination system and have login credentials with adequate privileges.</p> -<p>To send data to other systems from the CHT, you will need to do the following:</p> -<ol> -<li>Enable <code>outbound</code> in <code>app_settings</code></li> -<li>Specify <em>when</em> data is sent</li> -<li>Specify <em>where</em> data is sent</li> -<li>Specify <em>what</em> data is sent</li> -<li>Set up credentials for the destination system</li> -</ol> -<h3 id="enable-outbound">Enable outbound</h3> -<p><a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/outbound/#configuration">Enable</a> the <code>mark_for_outbound</code> transition in <code>app_settings</code>.</p> -<h3 id="when-data-is-sent">When data is sent</h3> -<p>Whenever a document is changed (such as submitting a form, creating a new contact, or editing an existing one) you can configure outbound to send data to another system. The <code>relevant_to</code> <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/outbound/#relevant_to">property</a> in the outbound configuration is used to identify which activities will trigger the sending of data.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Example</h4> -Send data to the EMR whenever a CHW submits an <code>assessment</code> form where <code>referral = true</code>. -</div> -<h3 id="where-data-is-sent">Where data is sent</h3> -<p>The <code>destination</code> <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/outbound/#destination">property</a> in the outbound configuration is used to specify <em>where</em> to send data. This will normally be the API endpoint of the destination system or interoperabiliy layer.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Example</h4> -Send data to the <code>/api/v1/referral</code> endpoint in the destination system. -</div> -<h3 id="what-data-is-sent">What data is sent</h3> -<p>You configure <em>what</em> data is sent using the <code>mapping</code> <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/outbound/#mapping">property</a>. You will map data from the CHT to the format required by the destination API endpoint.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Example</h4> -Map the <code>contact.name</code> field in the CHT to the <code>patient.name</code> field in the EMR. -</div> -<h3 id="authentication">Authentication</h3> -<p>Credentials for the destination system are <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/outbound/#credentials">stored in CouchdDB</a>. You will need to set this up before you can test your configuration.</p> -<h2 id="requests-from-other-systems">Requests from other systems</h2> -<p>The CHT has a complete RESTful API that other systems can utilize to interact with data in the CHT.</p> -<p>The most common uses are:</p> -<ol> -<li>Looking up data in the CHT from another system</li> -<li>POSTing data to the CHT from another system</li> -</ol> -<h3 id="look-up-data-in-the-cht">Look up data in the CHT</h3> -<p>The CHT has a number of different API endpoints that can be used to look up data.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Example 1</h4> -You have a patient’s phone number and you want to look up more information about that patient, such as who their CHW is or what Catchment Area they live in. -</div> -<p>You can use the <code>contacts_by_phone</code> <a href="https://docs.communityhealthtoolkit.org/apps/reference/api/#get-apiv1contacts-by-phone">endpoint</a> will return the fully hydrated contact information for those patients.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Example 2</h4> -You just have the internal UUID of a particular contact but want to get the complete information available for that contact. -</div> -<p>You can use the <code>hydrate</code> <a href="https://docs.communityhealthtoolkit.org/apps/reference/api/#get-apiv1hydrate">endpoint</a> to obtain this information. to look up the complete information for that contact.</p> -<h3 id="post-data-to-the-cht">POST data to the CHT</h3> -<p>The CHT API also allows you to POST data. Using these endpoints, you can create new records in your CHT API. You can store activities that took place in another system on that contact&rsquo;s profile in the CHT, and even create tasks for CHWs in the CHT based on activities that took place in the other system.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Example 1</h4> -You use RapidPro to send daily quarantine follow-up messages to a patient. You want to store the patient’s responses to those messages on their profile in the CHT. -</div> -<p>You can do this by submitting a JSON Form to the records <a href="https://docs.communityhealthtoolkit.org/apps/reference/api/#post-apiv2records">endpoint</a>.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Example 2</h4> -Continuing from Example 1, create a task for a CHW in the CHT whenever a patient responds that they have developed symptoms. -</div> -<p>You would do this by simply configuring a <a href="https://docs.communityhealthtoolkit.org/apps/features/tasks/">task</a> to be generated based on criteria available in the report that was created in Example 1.</p>Apps: DHIS2https://docs.communityhealthtoolkit.org/apps/features/integrations/dhis2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/integrations/dhis2/ -<p>Most health systems have regular reporting requirements for community-level activities. Health workers often carry around heavy logbooks to manually record all relevant activities. When it is time to submit their data, community health workers (CHWs) summarize what was recorded in their logbooks and share this information with their supervisors, who in turn create paper records of these totals across entire community units or health facilities. This paper record is often passed to yet another individual whose responsibility is to manually key in the data into a health information management system, such as DHIS2.</p> -<p>In communities using digital health apps that do not integrate with DHIS2, it is highly likely that health workers are duplicating efforts by recording the same information both in their app and in their logbook(s). For example, they are not only registering new pregnancies in their app, but they are also manually recording this in their logbooks, manually adding them up at the end of the month, and then someone else is manually keying this into DHIS2.</p> -<p>In communities using digital health apps built with the CHT, health systems can reduce or eliminate the need to complete paper based forms for DHIS2 reporting needs. This gives health workers more time to focus on caring for the families in their community while also increasing accuracy and timeliness of their DHIS2 reporting requirements.</p> -<h2 id="overview">Overview</h2> -<p>The CHT Core Framework supports integrations with DHIS2 in a variety of ways:</p> -<ol> -<li>Sending patient data</li> -<li>Sending event data</li> -<li>Sending pre-aggregated &ldquo;Service Reports&rdquo;</li> -<li>Aggregating data across multiple health workers into DHIS2 Data Values</li> -<li>Exporting a file that can be imported into DHIS2 as a Data Set</li> -<li>Exposing an API for DHIS2 app developers to pull aggregate data from CHT Core</li> -<li>Receiving data from DHIS2</li> -</ol> -<p>Sending patient, event, and pre-aggregated data can be achieved using the <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/outbound/">Outbound push</a> feature. Receiving data from DHIS2 can be achieved using the <a href="https://github.com/medic/cht-core/tree/master/api">CHT Core Web API</a>.</p> -<p>Aggregating data across multiple health workers requires a somewhat specific workflow and was designed with three key user personas in mind. The aggregate workflow is described in more detail below.</p> -<h2 id="aggregate-workflow">Aggregate Workflow</h2> -<p>The aggregate workflow was designed specifically for CHWs, Supervisors, and Health Records Information Officers (HRIO) but may be adapted to other contexts.</p> -<p><strong>CHW (offline user)</strong>: Conducts home visits and records information in the app. Reviews aggregate data throughout the month and makes sure to sync at the end of the month.</p> -<p><strong>Supervisor (offline user)</strong>: Provides supervision to the CHWs, reviews and verifies aggregate data for an entire community unit.</p> -<p><strong>HRIO (online user)</strong>: Exports a file from the CHT and imports into DHIS2. Reviews data in DHIS2.</p> -<h3 id="community-health-workers">Community Health Workers</h3> -<p>CHWs support patients in their community by following care guides and recording responses in the CHT. The CHT calculates aggregate DHIS2 Data Values for each CHW based on rules configured in the CHT. CHWs can view these indicators on the Targets tab and should review them and sync at the end of the month.</p> -<p><img src="chw.png" alt="CHWs" title="Feature Overview CHWs"></p> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/features/targets/">Targets</a></p> -<h3 id="supervisors">Supervisors</h3> -<p>In addition to their own targets, Supervisors can see the aggregate of each DHIS2 Data Value across <em>all</em> CHWs in their area from the <em>CHW Aggregate</em> view on the <strong>Targets tab</strong>. By tapping on a target, they can also see each CHW’s contribution towards that total. Supervisors can review and verify with CHWs that everyone has synced and that their data is correct. The Supervisor can communicate with the HRIO when everything has been validated.</p> -<p><img src="supervisor.png" alt="Supervisors" title="Feature Overview Supervisors"></p> -<h3 id="health-records-information-officers">Health Records Information Officers</h3> -<p>HRIOs access the <strong>CHT App Management</strong> tab and select the appropriate <em>DHIS2 Data Set, Organisation Unit,</em> and <em>Period</em>. They then <em>Export</em> a file that is formatted for DHIS2. HRIOs will need access to the <strong>Import/Export</strong> feature in DHIS2 so that they can <strong>Import</strong> the file. Once it has been imported into DHIS2, they review the data from the <strong>Data Entry</strong> screen in DHIS2.</p> -<p><img src="data-entry-1.png" alt="Data Entry" title="Feature Overview Data Entry 1"></p> -<h2 id="dhis2-apps">DHIS2 apps</h2> -<p>The CHT also includes an <a href="https://github.com/medic/cht-core/tree/master/api">API</a> that can be called from other applications that returns DHIS2 Data Sets. This means that you can build a <a href="https://docs.dhis2.org/master/en/developer/html/apps_creating_apps.html">DHIS2 app</a> that pulls data from the CHT and imports it electronically into DHIS2. This would allow <strong>Data Entry</strong> to control the process directly from DHIS2 without having to access the CHT.</p> -<p><img src="data-entry-2.png" alt="Data Entry" title="Feature Overview Data Entry 2"></p> -<h2 id="version-notes">Version Notes</h2> -<table> -<thead> -<tr> -<th>Feature</th> -<th>CHT Core version</th> -</tr> -</thead> -<tbody> -<tr> -<td>Calculate DHIS2 Data Values by aggregating data from CHT Core reports</td> -<td>3.9.0</td> -</tr> -<tr> -<td>Export file from CHT Core that can be imported into DHIS2 as a Data Set</td> -<td>3.9.0</td> -</tr> -</tbody> -</table>Apps: OpenMRShttps://docs.communityhealthtoolkit.org/apps/features/integrations/openmrs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/integrations/openmrs/ -<p><a href="https://openmrs.org">OpenMRS</a> is an open source electronic medical record system used in dozens of countries. Integrating CHT apps with OpenMRS can open up opportunities to improve health outcomes for patients by promoting better coordination of care. For example, referrals by CHWs in the community can be sent electronically to health facilities using OpenMRS so that nurses and clinicians can prepare for their visit and have full access to the assessment done by a CHW.</p> -<p>Integrating CHT apps with OpenMRS can be achieved by following the CHT&rsquo;s <a href="https://docs.communityhealthtoolkit.org/apps/guides/integrations/openmrs/">OpenMRS integration guide</a>, which leverages the <a href="https://rest.openmrs.org">OpenMRS REST API</a>.</p>Apps: OppiaMobilehttps://docs.communityhealthtoolkit.org/apps/features/integrations/oppia/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/integrations/oppia/ -<p><a href="https://digital-campus.org/oppiamobile">OppiaMobile</a> is an open source mobile learning platform built by Digital Campus specially designed for delivering learning content, multimedia, and quizzes in low connectivity settings. All the content and activities can be accessed and used when no internet connection is available, and users can earn points and badges for completing activities and watching videos. To learn more about the platform, check out the <a href="https://digital-campus.org/oppiamobile/developers">overview</a>, OppiaMobile on <a href="https://github.com/DigitalCampus">Github</a>, and their <a href="http://oppiamobile.readthedocs.io/en/latest">documentation site</a>. You can also join the OppiaMobile <a href="https://community.oppia-mobile.org">Community Discussion Board</a>.</p> -<h2 id="overview">Overview</h2> -<p>This documentation describes how the CHT and OppiaMobile can integrate to provide a learning and care experience for community health workers and other health care providers. It demonstrates how both apps link to one another to provide a seamless user experience, describes features of both applications, and the required configuration adjustments.</p> -<p>We provide a detailed example of the CHT&lt;&gt;OppiaMobile integration, including how to access the learning material, an overview of the functionalities within educational modules, and post-course assessment and supervisor support.</p> -<h2 id="features">Features</h2> -<p>This integration leverages the <strong>remote onboarding, task &amp; scheduling, and target features</strong> of the CHT core framework with the <strong>curated, multimedia educational content</strong> available via OppiaMobile’s learning platform.</p> -<p>The CHT Core Framework &amp; OppiaMobile integration currently supports the following capabilities and features:</p> -<ul> -<li><a href="https://docs.communityhealthtoolkit.org/apps/features/tasks/">Task management</a> for notifications on new educational modules and software updates</li> -<li><a href="https://docs.communityhealthtoolkit.org/apps/examples/training/">Remote onboarding</a> to new apps, software features, and workflows when they are updated, without relying on face-to-face training</li> -<li>Optimized multimedia content with links to educational modules powered by OppiaMobile</li> -<li>Message and feedback options, to contact supervisors with questions and seek support</li> -<li><a href="https://docs.communityhealthtoolkit.org/apps/features/targets/#supervisor-view">Supervisor visibility</a> into CHW progress for onboarding, learning, and care</li> -<li>Hosting options for government-led, government-owned platforms</li> -</ul>Apps: RapidProhttps://docs.communityhealthtoolkit.org/apps/features/integrations/rapidpro/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/integrations/rapidpro/ -<p><a href="https://rapidpro.io/">RapidPro</a> is a software product that allows you to visually build logic to support interactive messaging flows. The flows support a variety of technologies such as: SMS, USSD, IVR, Telegram, Facebook Messenger, and WhatsApp. RapidPro is open source and provides an <a href="https://community.rapidpro.io/rapidpros-api/">API</a> to integrate with other applications. To learn more about the platform, check out RapidPro on <a href="https://rapidpro.github.io/rapidpro/">GitHub</a>, their <a href="https://community.rapidpro.io/">Community</a> website, or join their Google <a href="https://groups.google.com/g/rapidpro">Group</a>.</p> -<h2 id="overview">Overview</h2> -<p>CHT-based <a href="https://docs.communityhealthtoolkit.org/apps/concepts/workflows/#sms-messaging">SMS workflows</a> can be configured to support registering of new patients or pregnancies, recording outcomes of visits, confirmation via auto-responses, and scheduling reminders. Some projects are designed entirely around SMS workflows. The CHT also supports person to person SMS <a href="https://docs.communityhealthtoolkit.org/apps/features/messaging/">messaging</a> from the Messages tab.</p> -<p>For more complex messaging workflows or to utilize other messaging platforms, you can design workflows that leverage the functionality of RapidPro and the CHT together. This enables semi-automated, direct to patient approaches to health assessments and care coordination at the community level.</p> -<h2 id="workflows">Workflows</h2> -<p>Integrated RapidPro/CHT workflows are very flexible and leverage the full functionality of each application; You configure RapidPro directly in RapidPro, and configure the CHT in the CHT and the two systems communicate with each other through APIs and <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/outbound/">Outbound push</a>. With this architecture, you are not limited to a subset of functionality within either application.</p> -<p>A simple RapidPro/CHT integration might include triggering an interactive SMS messaging flow in RapidPro whenever a new patient is registered in the CHT and then storing the responses of that messaging flow in the CHT. You could then conditionally trigger a <a href="https://docs.communityhealthtoolkit.org/apps/features/tasks/">Task</a> for a health worker in the CHT based on the patient responses from the RapidPro flow.</p> -<p>App builders have built and deployed a number of interactive messaging workflows that integrate RapidPro and the CHT already, see below for a few examples.</p> -<h3 id="contact-tracing">Contact Tracing</h3> -<p>The <a href="https://docs.communityhealthtoolkit.org/apps/examples/contact-tracing/#workflow-example">COVID-19 Contact Tracing app</a> uses RapidPro to send messages to quarantined COVID-19 patients. Messages are sent to the patients daily asking whether or not they developed new symptoms. If so, a health worker will be notified by SMS and receive a CHT task. All responses to the RapidPro workflow are recorded in the CHT and can be queried in analytics.</p> -<h3 id="remote-training">Remote Training</h3> -<p>The <a href="https://docs.communityhealthtoolkit.org/apps/examples/training/#remote-training-by-sms">Remote Training by SMS app</a> uses RapidPro to train health workers on Antenatal Care in the language of their choice. If the health worker answers a training question incorrectly, a task can be created for their supervisor to follow up with them.</p> -<h3 id="chw-symptom-and-mental-health-checks">CHW Symptom and Mental Health Checks</h3> -<p>The <a href="https://docs.google.com/document/d/19F6vOCNFKQnSyREiaBnryUmre20s5QZzYe0hWuWn-0k/edit">CHW Symptom and Mental Health Checks app</a> is used to proactively check in with health workers to screen for COVID-19 symptoms and/or the need for psychosocial counseling.</p> \ No newline at end of file +Integrations on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/features/integrations/Recent content in Integrations on Community Health ToolkitHugo -- gohugo.ioenAndroidhttps://docs.communityhealthtoolkit.org/apps/features/integrations/android/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/integrations/android/The CHT Android application can be launched by clicking on a link or invoking an intent in another Android app. This is useful for enabling login by SMS, directing a user to a specific page, and integrating between Android applications. +Sending a URL When the user clicks on a link to a CHT instance from an SMS, email, WhatsApp, or any other app, Android will prompt the user to choose whether to open the URL in the Android app or the browser.Customhttps://docs.communityhealthtoolkit.org/apps/features/integrations/custom/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/integrations/custom/The CHT Core Framework includes functionality that allows sharing data with any API-based system. Developers have configured CHT integrations with OpenMRS, KenyaEMR, Bahmni, DHIS2, RapidPro, Apache NiFi, OpenHIM, custom electronic medical records (EMR), and several other systems. +Overview Integrating a CHT App into your digital health ecosystem starts with identifying an integration use case. It&rsquo;s important to first understand all the components present in the ecosystem (EMR, laboratory system, community health information system, etc) and then plan out what the workflow will look like operationally.DHIS2https://docs.communityhealthtoolkit.org/apps/features/integrations/dhis2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/integrations/dhis2/Most health systems have regular reporting requirements for community-level activities. Health workers often carry around heavy logbooks to manually record all relevant activities. When it is time to submit their data, community health workers (CHWs) summarize what was recorded in their logbooks and share this information with their supervisors, who in turn create paper records of these totals across entire community units or health facilities. This paper record is often passed to yet another individual whose responsibility is to manually key in the data into a health information management system, such as DHIS2.OpenMRShttps://docs.communityhealthtoolkit.org/apps/features/integrations/openmrs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/integrations/openmrs/OpenMRS is an open source electronic medical record system used in dozens of countries. Integrating CHT apps with OpenMRS can open up opportunities to improve health outcomes for patients by promoting better coordination of care. For example, referrals by CHWs in the community can be sent electronically to health facilities using OpenMRS so that nurses and clinicians can prepare for their visit and have full access to the assessment done by a CHW.OppiaMobilehttps://docs.communityhealthtoolkit.org/apps/features/integrations/oppia/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/integrations/oppia/OppiaMobile is an open source mobile learning platform built by Digital Campus specially designed for delivering learning content, multimedia, and quizzes in low connectivity settings. All the content and activities can be accessed and used when no internet connection is available, and users can earn points and badges for completing activities and watching videos. To learn more about the platform, check out the overview, OppiaMobile on Github, and their documentation site.RapidProhttps://docs.communityhealthtoolkit.org/apps/features/integrations/rapidpro/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/integrations/rapidpro/RapidPro is a software product that allows you to visually build logic to support interactive messaging flows. The flows support a variety of technologies such as: SMS, USSD, IVR, Telegram, Facebook Messenger, and WhatsApp. RapidPro is open source and provides an API to integrate with other applications. To learn more about the platform, check out RapidPro on GitHub, their Community website, or join their Google Group. +Overview CHT-based SMS workflows can be configured to support registering of new patients or pregnancies, recording outcomes of visits, confirmation via auto-responses, and scheduling reminders. \ No newline at end of file diff --git a/apps/features/integrations/openmrs/index.html b/apps/features/integrations/openmrs/index.html index df02248b0b..0b07310d7d 100644 --- a/apps/features/integrations/openmrs/index.html +++ b/apps/features/integrations/openmrs/index.html @@ -1,9 +1,9 @@ -OpenMRS | Community Health Toolkit +OpenMRS | Community Health Toolkit

OpenMRS

Exchange patient-level data with systems based on the OpenMRS platform

OpenMRS is an open source electronic medical record system used in dozens of countries. Integrating CHT apps with OpenMRS can open up opportunities to improve health outcomes for patients by promoting better coordination of care. For example, referrals by CHWs in the community can be sent electronically to health facilities using OpenMRS so that nurses and clinicians can prepare for their visit and have full access to the assessment done by a CHW.

Integrating CHT apps with OpenMRS can be achieved by following the CHT’s OpenMRS integration guide, which leverages the OpenMRS REST API.


CHT Applications > + Create project issue

OpenMRS

Exchange patient-level data with systems based on the OpenMRS platform

OpenMRS is an open source electronic medical record system used in dozens of countries. Integrating CHT apps with OpenMRS can open up opportunities to improve health outcomes for patients by promoting better coordination of care. For example, referrals by CHWs in the community can be sent electronically to health facilities using OpenMRS so that nurses and clinicians can prepare for their visit and have full access to the assessment done by a CHW.

Integrating CHT apps with OpenMRS can be achieved by following the CHT’s OpenMRS integration guide, which leverages the OpenMRS REST API.


CHT Applications > Quick Guides > Integrations > OpenMRS

Exchange patient-level data with systems based on the OpenMRS platform

CHT Applications > @@ -313,7 +313,8 @@ Features > Integrations > DHIS2

Send aggregate, patient, and event data to DHIS2

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/features/integrations/oppia/index.html b/apps/features/integrations/oppia/index.html index 47e0ba171a..8ede8ce7d1 100644 --- a/apps/features/integrations/oppia/index.html +++ b/apps/features/integrations/oppia/index.html @@ -1,9 +1,9 @@ -OppiaMobile | Community Health Toolkit +OppiaMobile | Community Health Toolkit

OppiaMobile

Integrate CHT core with OppiaMobile’s learning management platform

OppiaMobile is an open source mobile learning platform built by Digital Campus specially designed for delivering learning content, multimedia, and quizzes in low connectivity settings. All the content and activities can be accessed and used when no internet connection is available, and users can earn points and badges for completing activities and watching videos. To learn more about the platform, check out the overview, OppiaMobile on Github, and their documentation site. You can also join the OppiaMobile Community Discussion Board.

Overview

This documentation describes how the CHT and OppiaMobile can integrate to provide a learning and care experience for community health workers and other health care providers. It demonstrates how both apps link to one another to provide a seamless user experience, describes features of both applications, and the required configuration adjustments.

We provide a detailed example of the CHT<>OppiaMobile integration, including how to access the learning material, an overview of the functionalities within educational modules, and post-course assessment and supervisor support.

Features

This integration leverages the remote onboarding, task & scheduling, and target features of the CHT core framework with the curated, multimedia educational content available via OppiaMobile’s learning platform.

The CHT Core Framework & OppiaMobile integration currently supports the following capabilities and features:

  • Task management for notifications on new educational modules and software updates
  • Remote onboarding to new apps, software features, and workflows when they are updated, without relying on face-to-face training
  • Optimized multimedia content with links to educational modules powered by OppiaMobile
  • Message and feedback options, to contact supervisors with questions and seek support
  • Supervisor visibility into CHW progress for onboarding, learning, and care
  • Hosting options for government-led, government-owned platforms

CHT Applications > + Create project issue

OppiaMobile

Integrate CHT core with OppiaMobile’s learning management platform

OppiaMobile is an open source mobile learning platform built by Digital Campus specially designed for delivering learning content, multimedia, and quizzes in low connectivity settings. All the content and activities can be accessed and used when no internet connection is available, and users can earn points and badges for completing activities and watching videos. To learn more about the platform, check out the overview, OppiaMobile on Github, and their documentation site. You can also join the OppiaMobile Community Discussion Board.

Overview

This documentation describes how the CHT and OppiaMobile can integrate to provide a learning and care experience for community health workers and other health care providers. It demonstrates how both apps link to one another to provide a seamless user experience, describes features of both applications, and the required configuration adjustments.

We provide a detailed example of the CHT<>OppiaMobile integration, including how to access the learning material, an overview of the functionalities within educational modules, and post-course assessment and supervisor support.

Features

This integration leverages the remote onboarding, task & scheduling, and target features of the CHT core framework with the curated, multimedia educational content available via OppiaMobile’s learning platform.

The CHT Core Framework & OppiaMobile integration currently supports the following capabilities and features:

  • Task management for notifications on new educational modules and software updates
  • Remote onboarding to new apps, software features, and workflows when they are updated, without relying on face-to-face training
  • Optimized multimedia content with links to educational modules powered by OppiaMobile
  • Message and feedback options, to contact supervisors with questions and seek support
  • Supervisor visibility into CHW progress for onboarding, learning, and care
  • Hosting options for government-led, government-owned platforms

CHT Applications > Examples > Learning & Care

An integration built to pilot the integrated workflows focused on CHW remote learning and care support for COVID-19.

CHT Applications > Quick Guides > Integrations > OppiaMobile

Components & configuration

-

Last modified 17.12.2020: Oppia x CHT (#392) (a994da3d)
\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/features/integrations/rapidpro/index.html b/apps/features/integrations/rapidpro/index.html index e584532696..af7cde2254 100644 --- a/apps/features/integrations/rapidpro/index.html +++ b/apps/features/integrations/rapidpro/index.html @@ -1,9 +1,9 @@ -RapidPro | Community Health Toolkit +RapidPro | Community Health Toolkit

RapidPro

Integrate interactive messaging conversations into your workflows

RapidPro is a software product that allows you to visually build logic to support interactive messaging flows. The flows support a variety of technologies such as: SMS, USSD, IVR, Telegram, Facebook Messenger, and WhatsApp. RapidPro is open source and provides an API to integrate with other applications. To learn more about the platform, check out RapidPro on GitHub, their Community website, or join their Google Group.

Overview

CHT-based SMS workflows can be configured to support registering of new patients or pregnancies, recording outcomes of visits, confirmation via auto-responses, and scheduling reminders. Some projects are designed entirely around SMS workflows. The CHT also supports person to person SMS messaging from the Messages tab.

For more complex messaging workflows or to utilize other messaging platforms, you can design workflows that leverage the functionality of RapidPro and the CHT together. This enables semi-automated, direct to patient approaches to health assessments and care coordination at the community level.

Workflows

Integrated RapidPro/CHT workflows are very flexible and leverage the full functionality of each application; You configure RapidPro directly in RapidPro, and configure the CHT in the CHT and the two systems communicate with each other through APIs and Outbound push. With this architecture, you are not limited to a subset of functionality within either application.

A simple RapidPro/CHT integration might include triggering an interactive SMS messaging flow in RapidPro whenever a new patient is registered in the CHT and then storing the responses of that messaging flow in the CHT. You could then conditionally trigger a Task for a health worker in the CHT based on the patient responses from the RapidPro flow.

App builders have built and deployed a number of interactive messaging workflows that integrate RapidPro and the CHT already, see below for a few examples.

Contact Tracing

The COVID-19 Contact Tracing app uses RapidPro to send messages to quarantined COVID-19 patients. Messages are sent to the patients daily asking whether or not they developed new symptoms. If so, a health worker will be notified by SMS and receive a CHT task. All responses to the RapidPro workflow are recorded in the CHT and can be queried in analytics.

Remote Training

The Remote Training by SMS app uses RapidPro to train health workers on Antenatal Care in the language of their choice. If the health worker answers a training question incorrectly, a task can be created for their supervisor to follow up with them.

CHW Symptom and Mental Health Checks

The CHW Symptom and Mental Health Checks app is used to proactively check in with health workers to screen for COVID-19 symptoms and/or the need for psychosocial counseling.


CHT Applications > + Create project issue

RapidPro

Integrate interactive messaging conversations into your workflows

RapidPro is a software product that allows you to visually build logic to support interactive messaging flows. The flows support a variety of technologies such as: SMS, USSD, IVR, Telegram, Facebook Messenger, and WhatsApp. RapidPro is open source and provides an API to integrate with other applications. To learn more about the platform, check out RapidPro on GitHub, their Community website, or join their Google Group.

Overview

CHT-based SMS workflows can be configured to support registering of new patients or pregnancies, recording outcomes of visits, confirmation via auto-responses, and scheduling reminders. Some projects are designed entirely around SMS workflows. The CHT also supports person to person SMS messaging from the Messages tab.

For more complex messaging workflows or to utilize other messaging platforms, you can design workflows that leverage the functionality of RapidPro and the CHT together. This enables semi-automated, direct to patient approaches to health assessments and care coordination at the community level.

Workflows

Integrated RapidPro/CHT workflows are very flexible and leverage the full functionality of each application; You configure RapidPro directly in RapidPro, and configure the CHT in the CHT and the two systems communicate with each other through APIs and Outbound push. With this architecture, you are not limited to a subset of functionality within either application.

A simple RapidPro/CHT integration might include triggering an interactive SMS messaging flow in RapidPro whenever a new patient is registered in the CHT and then storing the responses of that messaging flow in the CHT. You could then conditionally trigger a Task for a health worker in the CHT based on the patient responses from the RapidPro flow.

App builders have built and deployed a number of interactive messaging workflows that integrate RapidPro and the CHT already, see below for a few examples.

Contact Tracing

The COVID-19 Contact Tracing app uses RapidPro to send messages to quarantined COVID-19 patients. Messages are sent to the patients daily asking whether or not they developed new symptoms. If so, a health worker will be notified by SMS and receive a CHT task. All responses to the RapidPro workflow are recorded in the CHT and can be queried in analytics.

Remote Training

The Remote Training by SMS app uses RapidPro to train health workers on Antenatal Care in the language of their choice. If the health worker answers a training question incorrectly, a task can be created for their supervisor to follow up with them.

CHW Symptom and Mental Health Checks

The CHW Symptom and Mental Health Checks app is used to proactively check in with health workers to screen for COVID-19 symptoms and/or the need for psychosocial counseling.


CHT Applications > Quick Guides > Integrations > RapidPro

Key concepts, design considerations, how to configure, and best practices

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/features/messaging/index.html b/apps/features/messaging/index.html index f20b290c40..04bb9e8f6a 100644 --- a/apps/features/messaging/index.html +++ b/apps/features/messaging/index.html @@ -1,9 +1,9 @@ -Messaging | Community Health Toolkit +Messaging | Community Health Toolkit

Messaging

Messaging for Care Coordination, Alerts, and Notifications

Messaging is a quick way to coordinate with other health workers. The Messages tab allows users to send a SMS message to any person or group of people in the app. Common uses of messages include asking questions, coordinating care logistics, providing encouragement or confirming training times.

The main list is a combination of both outgoing and incoming messages. Messages exchanged with the same person or group are organized into a thread, similar to messages in a phone’s messaging app.

The features on the Messages tab are best supported on desktop and most often used by someone in a supervisor role.

Main List

On the main list of messages, the first line of bold text is the name and / or phone number of the sender. The second line is an excerpt from the most recent message, and the third line, if applicable, is the place(s) that the sender belongs to.

In the upper right corner, a timestamp displays when the most recent message was sent. An unread message is indicated by a blue line and bold blue timestamp. Messages are sorted by date with the most recent at the top of the list.

To send a new message that starts a brand new conversation thread, select the “Send Message +” button at the bottom of the screen. On the new message screen, enter the phone number you would like to send the message to or select a person in the app from the drop-down list. Then, type out your message text.

Detail Page

Clicking on a message in the main list will take you to a detail tab where you can see the full text of the conversation. Underneath each individual message in the conversation, you will see the message status which tells you whether or not the message was successfully delivered or received and at what time.

To reply to a message, tap or click in the box at the bottom of the conversation that says “Reply to…” and start typing. Each message is limited to 160 characters but you may send more than one message if necessary.

It is also possible to configure auto-responses and for schedules of personalized, automated messages to be triggered upon submission of a form (e.g., a pregnancy registration triggers a schedule of personalized Antenatal care messages).


CHT Applications > + Create project issue

Messaging

Messaging for Care Coordination, Alerts, and Notifications

Messaging is a quick way to coordinate with other health workers. The Messages tab allows users to send a SMS message to any person or group of people in the app. Common uses of messages include asking questions, coordinating care logistics, providing encouragement or confirming training times.

The main list is a combination of both outgoing and incoming messages. Messages exchanged with the same person or group are organized into a thread, similar to messages in a phone’s messaging app.

The features on the Messages tab are best supported on desktop and most often used by someone in a supervisor role.

Main List

On the main list of messages, the first line of bold text is the name and / or phone number of the sender. The second line is an excerpt from the most recent message, and the third line, if applicable, is the place(s) that the sender belongs to.

In the upper right corner, a timestamp displays when the most recent message was sent. An unread message is indicated by a blue line and bold blue timestamp. Messages are sorted by date with the most recent at the top of the list.

To send a new message that starts a brand new conversation thread, select the “Send Message +” button at the bottom of the screen. On the new message screen, enter the phone number you would like to send the message to or select a person in the app from the drop-down list. Then, type out your message text.

Detail Page

Clicking on a message in the main list will take you to a detail tab where you can see the full text of the conversation. Underneath each individual message in the conversation, you will see the message status which tells you whether or not the message was successfully delivered or received and at what time.

To reply to a message, tap or click in the box at the bottom of the conversation that says “Reply to…” and start typing. Each message is limited to 160 characters but you may send more than one message if necessary.

It is also possible to configure auto-responses and for schedules of personalized, automated messages to be triggered upon submission of a form (e.g., a pregnancy registration triggers a schedule of personalized Antenatal care messages).


CHT Applications > Reference > app_settings.json > .schedules

SMS Schedules: Defining SMS workflows with schedules, registration, and patient reports.

CHT Applications > @@ -309,7 +309,8 @@ Quick Guides > Forms > App Form SMS

Trigger calls and SMS from within the form, or send an SMS once submitted.

-

Last modified 21.09.2020: updated images (a17c9e45)
\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/features/messaging/index.xml b/apps/features/messaging/index.xml index 2e9b2a44b9..1b9e5d0af2 100644 --- a/apps/features/messaging/index.xml +++ b/apps/features/messaging/index.xml @@ -1 +1 @@ -Community Health Toolkit – Messaginghttps://docs.communityhealthtoolkit.org/apps/features/messaging/Recent content in Messaging on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file +Messaging on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/features/messaging/Recent content in Messaging on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file diff --git a/apps/features/muting/index.html b/apps/features/muting/index.html index 87ad451bfa..81b680269f 100644 --- a/apps/features/muting/index.html +++ b/apps/features/muting/index.html @@ -1,9 +1,9 @@ -Muting | Community Health Toolkit +Muting | Community Health Toolkit

Muting

Temporarily silence tasks and SMS schedules

Muting is a way for CHWs to temporarily silence notifications about Contacts (people and places) and is commonly used when a person or family has temporarily relocated or refused services. When a Contact is muted, they will appear differently on the People tab and CHWs will no longer receive tasks or SMS about them.

To start receiving notifications about a Contact again, CHWs can unmute them. When a Contact is unmuted, tasks and SMS schedules will resume, but notifications that would have been sent while they were muted will not.

Mute a Contact

To mute a Contact, CHWs typically submit a “Mute” form about them. This can be done either from the People tab or the Reports tab. When a person is muted, in addition to their notifications being silenced, their icon turns grey and a “Muted” status is displayed beneath their name. While a person or place is muted, CHWs and Nurses can still submit other forms about them.

Some changes are observed in the UI when a family or a person is muted to make the muted status clear and easily recognizable. The person’s or family’s icon turns grey and a status of “Muted” displays on the second line below the person’s or family’s name. Any actions that were previously available on the family or person remain available on the profile.

When a place is muted, all places and people beneath it will automatically be muted as well. For example, if a family is muted, all individuals in that family will automatically be muted. It is not possible to have a muted family with some family members that are not muted. This means that if a new person is added to a muted family, that person will automatically be muted when they are created.

Unmute a Contact

To unmute a Contact, CHWs typically submit an “Unmute” form. This will remove the “Mute” styling, resume notifications, and also unmute all places above them in the hierarchy. For example, if a family member is unmuted, the entire family will automatically be unmuted since it is not possible to have an unmuted person in a muted family.

Configurability

While it’s most common to have dedicated mute and unmute forms, any form can be set up to mute or unmute a Contact. It’s also possible to set up verification flows for muting whereby mute/unmute requires a Supervisor to verify before the mute/unmute happens. And like the rest of the CHT, all text is customizable.

Muting vs Death Reporting

A project may support both death reporting and muting - they are not mutually exclusive. Death reporting moves the deceased person to a different part of the family members list and does not allow actions. Muting keeps the person in the family members list, allows actions and disables schedules.

Death reportingMuting

  • Permanent state

  • Temporary state

  • Only allowed at the individual level

  • Place, family, or individual level

  • Removes schedules

  • “Quiets” notifications for schedules

  • A deceased individual is removed from the family list

  • A muted individual is not removed from the family list

  • No new actions can be performed

    except for one - reverse the death

  • New actions may be performed, but no tasks or notifications will be sent

  • Manager confirmation configurable

  • Manager confirmation configurable

CHT Applications > + Create project issue

Muting

Temporarily silence tasks and SMS schedules

Muting is a way for CHWs to temporarily silence notifications about Contacts (people and places) and is commonly used when a person or family has temporarily relocated or refused services. When a Contact is muted, they will appear differently on the People tab and CHWs will no longer receive tasks or SMS about them.

To start receiving notifications about a Contact again, CHWs can unmute them. When a Contact is unmuted, tasks and SMS schedules will resume, but notifications that would have been sent while they were muted will not.

Mute a Contact

To mute a Contact, CHWs typically submit a “Mute” form about them. This can be done either from the People tab or the Reports tab. When a person is muted, in addition to their notifications being silenced, their icon turns grey and a “Muted” status is displayed beneath their name. While a person or place is muted, CHWs and Nurses can still submit other forms about them.

Some changes are observed in the UI when a family or a person is muted to make the muted status clear and easily recognizable. The person’s or family’s icon turns grey and a status of “Muted” displays on the second line below the person’s or family’s name. Any actions that were previously available on the family or person remain available on the profile.

When a place is muted, all places and people beneath it will automatically be muted as well. For example, if a family is muted, all individuals in that family will automatically be muted. It is not possible to have a muted family with some family members that are not muted. This means that if a new person is added to a muted family, that person will automatically be muted when they are created.

Unmute a Contact

To unmute a Contact, CHWs typically submit an “Unmute” form. This will remove the “Mute” styling, resume notifications, and also unmute all places above them in the hierarchy. For example, if a family member is unmuted, the entire family will automatically be unmuted since it is not possible to have an unmuted person in a muted family.

Configurability

While it’s most common to have dedicated mute and unmute forms, any form can be set up to mute or unmute a Contact. It’s also possible to set up verification flows for muting whereby mute/unmute requires a Supervisor to verify before the mute/unmute happens. And like the rest of the CHT, all text is customizable.

Muting vs Death Reporting

A project may support both death reporting and muting - they are not mutually exclusive. Death reporting moves the deceased person to a different part of the family members list and does not allow actions. Muting keeps the person in the family members list, allows actions and disables schedules.

Death reportingMuting

  • Permanent state

  • Temporary state

  • Only allowed at the individual level

  • Place, family, or individual level

  • Removes schedules

  • “Quiets” notifications for schedules

  • A deceased individual is removed from the family list

  • A muted individual is not removed from the family list

  • No new actions can be performed

    except for one - reverse the death

  • New actions may be performed, but no tasks or notifications will be sent

  • Manager confirmation configurable

  • Manager confirmation configurable

CHT Applications > Reference > app_settings.json > .transitions : Muting

Sentinel Transitions: functions executed when database documents change

-

Last modified 09.03.2023: Training card guide (#970) (fb07ed89)
\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/features/muting/index.xml b/apps/features/muting/index.xml index c8c2acd277..b69844ea65 100644 --- a/apps/features/muting/index.xml +++ b/apps/features/muting/index.xml @@ -1 +1 @@ -Community Health Toolkit – Mutinghttps://docs.communityhealthtoolkit.org/apps/features/muting/Recent content in Muting on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file +Muting on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/features/muting/Recent content in Muting on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file diff --git a/apps/features/reports/index.html b/apps/features/reports/index.html index c4ba452bb5..b9dcee3e63 100644 --- a/apps/features/reports/index.html +++ b/apps/features/reports/index.html @@ -1,9 +1,9 @@ -Reports | Community Health Toolkit +Reports | Community Health Toolkit

Reports

Reports for Data & Report Management

The Reports tab is where you can access submitted data. Depending on how often you anticipate a user needing to access this tab, you can configure it to show in the main tabs list (preferable for admin users) or in the secondary hamburger menu (preferable for CHW users).

The permissions set for your role and your placement in the hierarchy will determine which reports you’re able to see on this tab. As a rule, you can only view reports submitted by yourself or those below you in the hierarchy. Therefore, CHWs will only see reports that they submitted on this tab, while supervisors will see reports that they submitted as well as those submitted by their CHWs. Advanced configuration options are available for a specific off-line user role to manage what level of report data is copied to their device.

Main List

The first line of bold text is the name of the person whom the report is about. The second line of text is the name of the report, and the third line of text is the hierarchy of place to which that person belongs. In the upper right corner, a timestamp displays when the report was submitted. Reports are sorted by submission date, with the most recent reports at the top. If a report is unread, the timestamp will be bold blue and there will be a horizontal blue line above it.

Apps built with the Core Framework have a “review” feature that allows managers to indicate whether a report has been reviewed and if it contains errors. If a manager has marked a report as “correct,” a green checkmark will show below the timestamp. If a report is marked as “has errors,” a red ‘X’ will show. This same icon is used for invalid SMS messages.


The toolbar at the top of the page includes filters and search to help users narrow down the list or search for and find a specific report. These filters are configurable and could include:

  • Report Types (e.g. pregnancy registration, visits, delivery report)
  • Places (e.g. districts, health centers or CHW areas)
  • Date of Submission
  • Status (e.g. not reviewed, has errors, correct, valid SMS, invalid SMS)

Using the search box, you can search for reports by patient name, phone number, ID number and more. To reset the filters and view the full list of forms, click on “Reset” located in the filter sidebar.



Action Buttons

The action buttons at the bottom of the screen are configurable using permissions. Options include submit, edit, delete, review and export reports.

Clicking on the “Export” button will download a CSV file with all the data from the reports. And clicking the “+ Submit report” button opens a menu of forms a user can choose to complete.


Bulk Delete Reports

Allows the user to select multiple reports and delete them. Please Note: This action cannot be undone. If in doubt, do not delete! You can restrict a user’s access to this feature by disabling the permission can_bulk_delete_reports.


Detail Pages

You can click on any report to view a report detail page. Here you’ll find the name and phone number of the user who submitted the report as well as responses to the questions within it. If the report initiated a schedule of SMS messages, you will see the messages queued to send.

The buttons at the bottom are configurable. The ones you see will depend on your user role, permissions, and hierarchy.

  • Send a Message​: Opens the Messages page to send a message to the person who submitted the report
  • Review: Mark as “correct” or “has errors”
  • Edit: Opens the form to edit it
  • Delete: Deletes a report ( cannot be undone)

Defining Forms

The reports shown in your app are the completed and submitted forms. These forms must be defined and included with the application. There are two types of form definitions for reports:

  • App forms: actions within the app, such as a completed task, or an action on a contact’s profile or reports tab. App forms are defined as XForms.
  • JSON forms: data coming from external channels such as SMS, or via interoperability with other tools. JSON forms are defined using a JavaScript Object Notation schema.

CHT Applications > + Create project issue

Reports

Reports for Data & Report Management

The Reports tab is where you can access submitted data. Depending on how often you anticipate a user needing to access this tab, you can configure it to show in the main tabs list (preferable for admin users) or in the secondary hamburger menu (preferable for CHW users).

The permissions set for your role and your placement in the hierarchy will determine which reports you’re able to see on this tab. As a rule, you can only view reports submitted by yourself or those below you in the hierarchy. Therefore, CHWs will only see reports that they submitted on this tab, while supervisors will see reports that they submitted as well as those submitted by their CHWs. Advanced configuration options are available for a specific off-line user role to manage what level of report data is copied to their device.

Main List

The first line of bold text is the name of the person whom the report is about. The second line of text is the name of the report, and the third line of text is the hierarchy of place to which that person belongs. In the upper right corner, a timestamp displays when the report was submitted. Reports are sorted by submission date, with the most recent reports at the top. If a report is unread, the timestamp will be bold blue and there will be a horizontal blue line above it.

Apps built with the Core Framework have a “review” feature that allows managers to indicate whether a report has been reviewed and if it contains errors. If a manager has marked a report as “correct,” a green checkmark will show below the timestamp. If a report is marked as “has errors,” a red ‘X’ will show. This same icon is used for invalid SMS messages.


The toolbar at the top of the page includes filters and search to help users narrow down the list or search for and find a specific report. These filters are configurable and could include:

  • Report Types (e.g. pregnancy registration, visits, delivery report)
  • Places (e.g. districts, health centers or CHW areas)
  • Date of Submission
  • Status (e.g. not reviewed, has errors, correct, valid SMS, invalid SMS)

Using the search box, you can search for reports by patient name, phone number, ID number and more. To reset the filters and view the full list of forms, click on “Reset” located in the filter sidebar.



Action Buttons

The action buttons at the bottom of the screen are configurable using permissions. Options include submit, edit, delete, review and export reports.

Clicking on the “Export” button will download a CSV file with all the data from the reports. And clicking the “+ Submit report” button opens a menu of forms a user can choose to complete.


Bulk Delete Reports

Allows the user to select multiple reports and delete them. Please Note: This action cannot be undone. If in doubt, do not delete! You can restrict a user’s access to this feature by disabling the permission can_bulk_delete_reports.


Detail Pages

You can click on any report to view a report detail page. Here you’ll find the name and phone number of the user who submitted the report as well as responses to the questions within it. If the report initiated a schedule of SMS messages, you will see the messages queued to send.

The buttons at the bottom are configurable. The ones you see will depend on your user role, permissions, and hierarchy.

  • Send a Message​: Opens the Messages page to send a message to the person who submitted the report
  • Review: Mark as “correct” or “has errors”
  • Edit: Opens the form to edit it
  • Delete: Deletes a report ( cannot be undone)

Defining Forms

The reports shown in your app are the completed and submitted forms. These forms must be defined and included with the application. There are two types of form definitions for reports:

  • App forms: actions within the app, such as a completed task, or an action on a contact’s profile or reports tab. App forms are defined as XForms.
  • JSON forms: data coming from external channels such as SMS, or via interoperability with other tools. JSON forms are defined using a JavaScript Object Notation schema.

CHT Applications > Reference > app_settings.json > .patient_reports

Patient Reports: Defining SMS workflows with schedules, registration, and patient reports.

CHT Applications > @@ -310,7 +310,8 @@ Quick Guides > Forms > Report Titles

Customizing the title shown in the Reports list

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/features/reports/index.xml b/apps/features/reports/index.xml index 522df54248..c3f5c492af 100644 --- a/apps/features/reports/index.xml +++ b/apps/features/reports/index.xml @@ -1 +1 @@ -Community Health Toolkit – Reportshttps://docs.communityhealthtoolkit.org/apps/features/reports/Recent content in Reports on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file +Reports on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/features/reports/Recent content in Reports on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file diff --git a/apps/features/supervision/index.html b/apps/features/supervision/index.html index cb7b60ee60..04eda4d5d0 100644 --- a/apps/features/supervision/index.html +++ b/apps/features/supervision/index.html @@ -1,9 +1,9 @@ -Supervision | Community Health Toolkit +Supervision | Community Health Toolkit

Supervision

Supervision and workforce management to strengthen health systems

Supervision and workforce management are important aspects to building and maintaining high-performing community health systems. Supervisors help Community Health Workers deliver quality healthcare services to their patients through building CHW care delivery knowledge and skills, fostering a supportive work environment, and supporting continuity of care between home-based care and community health centers or facilities.

The importance of regular and systematic CHW supervision is emphasized by the WHO’s guidelines on health policy and system support, which is formulated to optimize community-based health worker programs. Following these guidelines, the CHT is designed to enable Supervisors to provide personalized performance feedback during CHW supervision as well as track aggregate statistics. Data collected from completed Care Guides produce granular information which can be used to provide coaching that promotes compliance with health program standards of practice and closer monitoring of outcomes.

CHW Aggregate Targets

For CHW Supervisors, the Targets tab provides important insights into their community unit. It presents Supervisors with actionable information about their CHWs, by aggregating data for each of the CHWs that a Supervisor manages and presenting it in an easily digestible format. This enables Supervisors to gain insight into how well their team of CHWs is working together to meet common goals.

Selecting an aggregate widget opens the detailed view with the data for each individual CHW. If a CHW is performing below the target goal, their value will be highlighted in red, making it easier for Supervisors to know with which CHWs to follow up for coaching and performance management.

Supervisor Tasks

The CHT can be configured to create Tasks for Supervisors to help plan their performance management reviews. Tasks can be generated based on routine CHW supervision interactions, or data-driven based on specific events (e.g. to follow up with health workers whom haven’t submitted any forms in x period of time). Using Supervisor tasks to ensure that the right actions are taken for the right CHWs at the right time strengthens supervisory program design through routine assessments and timely feedback.

Deployment Case Study

Muso, a leading community health organization and major contributor to the CHT, has implemented ”360º supervision”, achieving some of the lowest child mortality rates in sub-Saharan Africa. This model provides dedicated mentorship and supportive supervision to CHWs tailored to each CHW’s particular strengths and challenges. A key theme of our human-centered approach was the idea of using data to improve one-to-one supervision, rather than using analytics to replace Supervisors. Read more about findings from a recent randomized controlled trial.

User Management

Supervisors are able to set up users in the CHT without contacting a system administrator. They can create new CHW user accounts or replace CHWs on an existing device.

When creating a new user account, Supervisors fill out the necessary details, including the CHW’s phone number, from their own device. They can do this while offline, but must sync before the actual user account is created. Once the Supervisor syncs, the CHT will send an SMS to the new CHW with a magic link that enables them to login and start using the app.

When replacing a CHW, Supervisors access the existing device and provide details about the new CHW. The new CHW can start using the app immediately, even while offline, and will see all of the existing household data. Once the new CHW syncs, the records on the server will be updated to reflect the new CHWs details.

This can be used to manage both CHW and CHW supervisor roles.

Managing Multiple Areas

CHT hierarchies tend to mimic geographical areas but Supervisors often manage CHWs across multiple geographical areas. (Offline) Supervisors who manage multiple areas can see data for all the different areas they manage from one app.

Supervisor Dashboards

Program dashboards track, visualize, and share health progress with stakeholders more broadly. Supervisors can use program dashboards to help articulate their CHW cohorts activities and how they align with program impact standards and indicators. Summary statistics of CHW service area performance (e.g. number of home visits, number of protocol errors, etc) help to identify areas for continued improvement and deeper audits of care data.

The data that can be visualized is highly configurable, and depends on what data fields are configured in a particular CHT app’s forms. Dashboards can be built using any software that supports visualizing data with the widely used PostgreSQL database. Examples include the open source tool Superset, as well as proprietary technologies like Klipfolio and Tableau.

DHIS2 Data Verification

Supervisors often provide a critical bridge between CHWs and broader health system reporting. Using the CHT’s DHIS2 integration, Supervisors can see the aggregate of each DHIS2 Data Value across all CHWs in their area. By tapping on a target, they can also see each CHW’s contribution towards that total. Once the Supervisor has verified data accuracy with CHWs, they can communicate with Health Records Information Officers to feed data into the national health information system.


CHT Applications > + Create project issue

Supervision

Supervision and workforce management to strengthen health systems

Supervision and workforce management are important aspects to building and maintaining high-performing community health systems. Supervisors help Community Health Workers deliver quality healthcare services to their patients through building CHW care delivery knowledge and skills, fostering a supportive work environment, and supporting continuity of care between home-based care and community health centers or facilities.

The importance of regular and systematic CHW supervision is emphasized by the WHO’s guidelines on health policy and system support, which is formulated to optimize community-based health worker programs. Following these guidelines, the CHT is designed to enable Supervisors to provide personalized performance feedback during CHW supervision as well as track aggregate statistics. Data collected from completed Care Guides produce granular information which can be used to provide coaching that promotes compliance with health program standards of practice and closer monitoring of outcomes.

CHW Aggregate Targets

For CHW Supervisors, the Targets tab provides important insights into their community unit. It presents Supervisors with actionable information about their CHWs, by aggregating data for each of the CHWs that a Supervisor manages and presenting it in an easily digestible format. This enables Supervisors to gain insight into how well their team of CHWs is working together to meet common goals.

Selecting an aggregate widget opens the detailed view with the data for each individual CHW. If a CHW is performing below the target goal, their value will be highlighted in red, making it easier for Supervisors to know with which CHWs to follow up for coaching and performance management.

Supervisor Tasks

The CHT can be configured to create Tasks for Supervisors to help plan their performance management reviews. Tasks can be generated based on routine CHW supervision interactions, or data-driven based on specific events (e.g. to follow up with health workers whom haven’t submitted any forms in x period of time). Using Supervisor tasks to ensure that the right actions are taken for the right CHWs at the right time strengthens supervisory program design through routine assessments and timely feedback.

Deployment Case Study

Muso, a leading community health organization and major contributor to the CHT, has implemented ”360º supervision”, achieving some of the lowest child mortality rates in sub-Saharan Africa. This model provides dedicated mentorship and supportive supervision to CHWs tailored to each CHW’s particular strengths and challenges. A key theme of our human-centered approach was the idea of using data to improve one-to-one supervision, rather than using analytics to replace Supervisors. Read more about findings from a recent randomized controlled trial.

User Management

Supervisors are able to set up users in the CHT without contacting a system administrator. They can create new CHW user accounts or replace CHWs on an existing device.

When creating a new user account, Supervisors fill out the necessary details, including the CHW’s phone number, from their own device. They can do this while offline, but must sync before the actual user account is created. Once the Supervisor syncs, the CHT will send an SMS to the new CHW with a magic link that enables them to login and start using the app.

When replacing a CHW, Supervisors access the existing device and provide details about the new CHW. The new CHW can start using the app immediately, even while offline, and will see all of the existing household data. Once the new CHW syncs, the records on the server will be updated to reflect the new CHWs details.

This can be used to manage both CHW and CHW supervisor roles.

Managing Multiple Areas

CHT hierarchies tend to mimic geographical areas but Supervisors often manage CHWs across multiple geographical areas. (Offline) Supervisors who manage multiple areas can see data for all the different areas they manage from one app.

Supervisor Dashboards

Program dashboards track, visualize, and share health progress with stakeholders more broadly. Supervisors can use program dashboards to help articulate their CHW cohorts activities and how they align with program impact standards and indicators. Summary statistics of CHW service area performance (e.g. number of home visits, number of protocol errors, etc) help to identify areas for continued improvement and deeper audits of care data.

The data that can be visualized is highly configurable, and depends on what data fields are configured in a particular CHT app’s forms. Dashboards can be built using any software that supports visualizing data with the widely used PostgreSQL database. Examples include the open source tool Superset, as well as proprietary technologies like Klipfolio and Tableau.

DHIS2 Data Verification

Supervisors often provide a critical bridge between CHWs and broader health system reporting. Using the CHT’s DHIS2 integration, Supervisors can see the aggregate of each DHIS2 Data Value across all CHWs in their area. By tapping on a target, they can also see each CHW’s contribution towards that total. Once the Supervisor has verified data accuracy with CHWs, they can communicate with Health Records Information Officers to feed data into the national health information system.


CHT Applications > Features > Targets

Dashboards to track metrics for an individual CHW or for an entire health facility

Design System > Best Practices @@ -314,7 +314,8 @@ : Create User for Contacts

Sentinel Transitions: functions executed when database documents change

CHT Applications > Examples > Supervision & Performance Management

A reference app for CHW supervisors to support performance management of CHWs using a mobile app.

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/features/supervision/index.xml b/apps/features/supervision/index.xml index 551bbf95d1..69b85463e7 100644 --- a/apps/features/supervision/index.xml +++ b/apps/features/supervision/index.xml @@ -1 +1 @@ -Community Health Toolkit – Supervisionhttps://docs.communityhealthtoolkit.org/apps/features/supervision/Recent content in Supervision on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file +Supervision on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/features/supervision/Recent content in Supervision on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file diff --git a/apps/features/targets/index.html b/apps/features/targets/index.html index 4e5b0f69db..b16a9db3c5 100644 --- a/apps/features/targets/index.html +++ b/apps/features/targets/index.html @@ -1,9 +1,9 @@ -Targets | Community Health Toolkit +Targets | Community Health Toolkit

Targets

Dashboards to track metrics for an individual CHW or for an entire health facility

Targets is the user dashboard or analytics tab. The widgets on this tab provide a summary or analysis of the data in submitted reports. These widgets can be configured to track metrics for an individual CHW, for a Supervisor overseeing a group of CHWs, or for an entire health facility.

For CHWs, the Targets tab provides a quick summary of their progress towards their individual goals. For Supervisors, Nurses, and facility-based users, the Targets tab provides important insights into how their community unit is performing.

Types of Widgets

There are two basic types of widgets: count and percent. Count widgets display a numeric sum while percent widgets display progress towards achieving a target.

The text, icon, goal, and time frame of each widget is easily configured. The time frame is set per widget, and set to show values for “this month” (resets back to zero at the beginning of each month) or “all time” (a cumulative total).


Count Widgets

Count widgets show a tally of a particular report that has been submitted or data within a report that matches a set of criteria. For example, a count can be done for the number of new pregnancies, the number of facility-based deliveries, or the number of households registered that month.

A count without a goal displays a simple green number count. A count with a goal displays the value of the goal on the right side and a colored count in the center indicating progress towards achieving the goal. Progress is displayed in green if the count is equal to or above the goal, or in black if the count is below the goal.

Percent Widgets

Percent widgets display a ratio, which helps to provide insight into the proportion that matches a defined criteria. For example, the proportion of newborns delivered in a facility can be presented as a percent with respect to all registered deliveries.

An optional goal can be set, such as “100% of patients with a fever should be given a malaria Rapid Diagnostic Test (mRDT),” to visualize progress towards achieving a target. Widget styling is configured to show green if the goal has been met and black if the goal has not been met. Next to the percent with a goal, the count of reports used in the calculation are shown (e.g. “16 of 20 with mRDT”). CHWs have found this helpful in interpreting target information.


CHT Applications > + Create project issue

Targets

Dashboards to track metrics for an individual CHW or for an entire health facility

Targets is the user dashboard or analytics tab. The widgets on this tab provide a summary or analysis of the data in submitted reports. These widgets can be configured to track metrics for an individual CHW, for a Supervisor overseeing a group of CHWs, or for an entire health facility.

For CHWs, the Targets tab provides a quick summary of their progress towards their individual goals. For Supervisors, Nurses, and facility-based users, the Targets tab provides important insights into how their community unit is performing.

Types of Widgets

There are two basic types of widgets: count and percent. Count widgets display a numeric sum while percent widgets display progress towards achieving a target.

The text, icon, goal, and time frame of each widget is easily configured. The time frame is set per widget, and set to show values for “this month” (resets back to zero at the beginning of each month) or “all time” (a cumulative total).


Count Widgets

Count widgets show a tally of a particular report that has been submitted or data within a report that matches a set of criteria. For example, a count can be done for the number of new pregnancies, the number of facility-based deliveries, or the number of households registered that month.

A count without a goal displays a simple green number count. A count with a goal displays the value of the goal on the right side and a colored count in the center indicating progress towards achieving the goal. Progress is displayed in green if the count is equal to or above the goal, or in black if the count is below the goal.

Percent Widgets

Percent widgets display a ratio, which helps to provide insight into the proportion that matches a defined criteria. For example, the proportion of newborns delivered in a facility can be presented as a percent with respect to all registered deliveries.

An optional goal can be set, such as “100% of patients with a fever should be given a malaria Rapid Diagnostic Test (mRDT),” to visualize progress towards achieving a target. Widget styling is configured to show green if the goal has been met and black if the goal has not been met. Next to the percent with a goal, the count of reports used in the calculation are shown (e.g. “16 of 20 with mRDT”). CHWs have found this helpful in interpreting target information.


CHT Applications > Reference > targets.js

Targets: Definition of target widgets calculated and seen in the app

CHT Core Framework > Overview > @@ -312,7 +312,8 @@ Features > Supervision : Chw Aggregate Targets

Supervision and workforce management to strengthen health systems

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/features/targets/index.xml b/apps/features/targets/index.xml index 429ab8ab94..a09f5c5369 100644 --- a/apps/features/targets/index.xml +++ b/apps/features/targets/index.xml @@ -1 +1 @@ -Community Health Toolkit – Targetshttps://docs.communityhealthtoolkit.org/apps/features/targets/Recent content in Targets on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file +Targets on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/features/targets/Recent content in Targets on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file diff --git a/apps/features/tasks/index.html b/apps/features/tasks/index.html index a998f60aaa..97e08ca06b 100644 --- a/apps/features/tasks/index.html +++ b/apps/features/tasks/index.html @@ -1,9 +1,9 @@ -Tasks | Community Health Toolkit +Tasks | Community Health Toolkit

Tasks

Ensuring that the right actions are taken for the right people at the right time

Tasks help CHWs plan their day by prompting them to complete follow-up visits and other activities. The list might include upcoming scheduled ANC or Immunization visits, treatment or referral follow-ups, or other required activities such as a household survey.

Main List

On the Tasks tab is a consolidated list of tasks for all people and families that the user looks after. The task definition determines how long the task will show on this list before and after it is due.

Each task has an icon on the left side which indicates which type of task it is. The first bold line of text is the person or family that the task is about. The second line of text is the name of the task. The due date for the task is located in the upper right-hand corner. If a task is overdue, the due date will be red.

Tasks are listed in order of due date. Tasks that are past due will appear at the top of the list. CHWs should strive to complete tasks before they are overdue. Many programs add targets to track task completion and timeliness.


Care Guides

When a CHW clicks on a task, the care guide configured for that task displays. CHWs are then guided through questions for that specific workflow.

For more information on Care Guides, see the “Decision Support for Care Guides” section of this overview.

When the user completes the care guide, the task will be cleared from the Tasks tab, and the report will be accessible from the Reports page or on the profile of the person or place whom the report is about.

Household Tasks

Alternatively, there is an option to configure Household Tasks. When this permission is enabled, once a CHW has completed a task, they are taken to the Other Household Tasks page. This page shows the CHW all the additional outstanding tasks within the same household in which the initial task was completed.

CHWs are able to complete tasks directly from this page, or exit by tapping on the “X”. If the household has no additional tasks, they will return directly to the main task list.

Profile Page

Tasks are also accessed from a the People tab in the app.

Tasks for a particular person or place can be viewed on the contact’s profile in the “Tasks” section. Filters allow you to choose how many tasks you’d like to view for each due date.



CHT Applications > + Create project issue

Tasks

Ensuring that the right actions are taken for the right people at the right time

Tasks help CHWs plan their day by prompting them to complete follow-up visits and other activities. The list might include upcoming scheduled ANC or Immunization visits, treatment or referral follow-ups, or other required activities such as a household survey.

Main List

On the Tasks tab is a consolidated list of tasks for all people and families that the user looks after. The task definition determines how long the task will show on this list before and after it is due.

Each task has an icon on the left side which indicates which type of task it is. The first bold line of text is the person or family that the task is about. The second line of text is the name of the task. The due date for the task is located in the upper right-hand corner. If a task is overdue, the due date will be red.

Tasks are listed in order of due date. Tasks that are past due will appear at the top of the list. CHWs should strive to complete tasks before they are overdue. Many programs add targets to track task completion and timeliness.


Care Guides

When a CHW clicks on a task, the care guide configured for that task displays. CHWs are then guided through questions for that specific workflow.

For more information on Care Guides, see the “Decision Support for Care Guides” section of this overview.

When the user completes the care guide, the task will be cleared from the Tasks tab, and the report will be accessible from the Reports page or on the profile of the person or place whom the report is about.

Household Tasks

Alternatively, there is an option to configure Household Tasks. When this permission is enabled, once a CHW has completed a task, they are taken to the Other Household Tasks page. This page shows the CHW all the additional outstanding tasks within the same household in which the initial task was completed.

CHWs are able to complete tasks directly from this page, or exit by tapping on the “X”. If the household has no additional tasks, they will return directly to the main task list.

Profile Page

Tasks are also accessed from a the People tab in the app.

Tasks for a particular person or place can be viewed on the contact’s profile in the “Tasks” section. Filters allow you to choose how many tasks you’d like to view for each due date.



CHT Applications > Reference > tasks.js

Tasks: Definition of tasks shown to app users

CHT Applications > Concepts > Workflows

Building connections between people, actions, and data systems

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/features/tasks/index.xml b/apps/features/tasks/index.xml index 95b106887f..1fd6db4ee8 100644 --- a/apps/features/tasks/index.xml +++ b/apps/features/tasks/index.xml @@ -1 +1 @@ -Community Health Toolkit – Taskshttps://docs.communityhealthtoolkit.org/apps/features/tasks/Recent content in Tasks on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file +Tasks on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/features/tasks/Recent content in Tasks on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file diff --git a/apps/features/training/index.html b/apps/features/training/index.html index 82cb0634ff..4ac4656689 100644 --- a/apps/features/training/index.html +++ b/apps/features/training/index.html @@ -1,9 +1,9 @@ -Training | Community Health Toolkit +Training | Community Health Toolkit

Training

Remotely train health workers

Training Cards help health workers learn about changes to CHT apps remotely, directly in their app. Training content might include information about a newly deployed feature, changes to a care guide, or simply a reminder about an underused feature or workflow. They are perfect for training on a very specific topic and are not meant to replace a comprehensive onboarding program.

Template training content for new CHT features is made available from time to time. These templates can be customized to the local context.

Accessing

When health workers open or reload their app, configured training cards will automatically show on top of all other content in the app. If it is not a convenient time to complete the training, they can cancel out at any time and will be prompted again later whenever they open or reload their app (training will start from the beginning).

Completing

Health workers read through each card one by one in a predefined sequence, tapping “Next” on each card. When they are finished reading all cards, they tap “Submit” on the last card. The training set is now considered complete and they can continue using their app. Completed training sets show up on the main list of the Reports tab and they won’t be asked to complete this set again. If there are additional training sets to complete, they will be shown the next time the app is opened or reloaded.

Monitoring

As mentioned above, completed training sets will show up on the main list of the Reports tab. These reports are available in analytics, aggregate targets , and can trigger supervision workflows and tasks.

Configurability

Training cards can be shown to any user associated to a contact. A “set” of training cards represents a collection of individual training cards, generally covering a single training topic. The list below highlights some of the key areas of customization:

  • Number of sets
  • Number of cards in a set
  • Content of each individual card (text, images, etc…)
  • When health workers will start seeing a set (specific start date)
  • How long the set will be available (# of days, relative to the start date)
  • Which users will see the set (based on user roles)

CHT Applications > + Create project issue

Training

Remotely train health workers

Training Cards help health workers learn about changes to CHT apps remotely, directly in their app. Training content might include information about a newly deployed feature, changes to a care guide, or simply a reminder about an underused feature or workflow. They are perfect for training on a very specific topic and are not meant to replace a comprehensive onboarding program.

Template training content for new CHT features is made available from time to time. These templates can be customized to the local context.

Accessing

When health workers open or reload their app, configured training cards will automatically show on top of all other content in the app. If it is not a convenient time to complete the training, they can cancel out at any time and will be prompted again later whenever they open or reload their app (training will start from the beginning).

Completing

Health workers read through each card one by one in a predefined sequence, tapping “Next” on each card. When they are finished reading all cards, they tap “Submit” on the last card. The training set is now considered complete and they can continue using their app. Completed training sets show up on the main list of the Reports tab and they won’t be asked to complete this set again. If there are additional training sets to complete, they will be shown the next time the app is opened or reloaded.

Monitoring

As mentioned above, completed training sets will show up on the main list of the Reports tab. These reports are available in analytics, aggregate targets , and can trigger supervision workflows and tasks.

Configurability

Training cards can be shown to any user associated to a contact. A “set” of training cards represents a collection of individual training cards, generally covering a single training topic. The list below highlights some of the key areas of customization:

  • Number of sets
  • Number of cards in a set
  • Content of each individual card (text, images, etc…)
  • When health workers will start seeing a set (specific start date)
  • How long the set will be available (# of days, relative to the start date)
  • Which users will see the set (based on user roles)

CHT Applications > Quick Guides > Training > Training Cards

Deploy in-app training cards for remote training.

CHT Applications > @@ -311,7 +311,8 @@ Remote Training

App and care workflow training using remote capabilities.

CHT Applications > Examples > Learning & Care

An integration built to pilot the integrated workflows focused on CHW remote learning and care support for COVID-19.

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/features/training/index.xml b/apps/features/training/index.xml index d1f3bd2e98..6c22bb0562 100644 --- a/apps/features/training/index.xml +++ b/apps/features/training/index.xml @@ -1 +1 @@ -Community Health Toolkit – Traininghttps://docs.communityhealthtoolkit.org/apps/features/training/Recent content in Training on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file +Training on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/features/training/Recent content in Training on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file diff --git a/apps/features/uhc-mode/index.html b/apps/features/uhc-mode/index.html index 63fbfd5e86..d850c72de9 100644 --- a/apps/features/uhc-mode/index.html +++ b/apps/features/uhc-mode/index.html @@ -1,9 +1,9 @@ -Universal Health Coverage Mode | Community Health Toolkit +Universal Health Coverage Mode | Community Health Toolkit

Universal Health Coverage Mode

Supporting equitable and timely care to families to increase Universal Health Coverage (UHC)

CHT apps using the UHC Mode empower CHWs to provide equitable and timely care to families in their catchment area. This feature was codeveloped with Muso, and showed an increase in household coverage for both rural and peri-urban settings, thereby increasing the effectiveness of CHWs and improving access to care for the populations they serve.

UHC with the CHT

Apps built using the Community Health Toolkit can be used in support of community-based services, and increase Universal Health Coverage (UHC) by helping health workers regularly reach all the families that they care for. A Muso study in collaboration with Medic, showed an increase in household coverage: UHC Mode should be considered an effective tool that can improve minimum expected home visit coverage and promote progress towards UHC when implemented in the proactive community case management context.

Prioritizing Households

UHC Mode screenshot
Contact sorting screenshot

The UHC Mode in the CHT allows health workers to see when a household within their care area was last visited, and prioritize visits accordingly.

When using the UHC Mode, the households in the contact list can be sorted by when they were last visited. The days since the last visit is also shown in the app, along with the number of visits made to a household in a month period.

Configurability

The last visited date is calculated based on the number of days since an action was taken for that household, and the number of visits reflects the actions taken for that household in the current month. What constitutes as an action for a household, along with the start date for the reporting period, are configurable to CHT app developers.


CHT Applications > + Create project issue

Universal Health Coverage Mode

Supporting equitable and timely care to families to increase Universal Health Coverage (UHC)

CHT apps using the UHC Mode empower CHWs to provide equitable and timely care to families in their catchment area. This feature was codeveloped with Muso, and showed an increase in household coverage for both rural and peri-urban settings, thereby increasing the effectiveness of CHWs and improving access to care for the populations they serve.

UHC with the CHT

Apps built using the Community Health Toolkit can be used in support of community-based services, and increase Universal Health Coverage (UHC) by helping health workers regularly reach all the families that they care for. A Muso study in collaboration with Medic, showed an increase in household coverage: UHC Mode should be considered an effective tool that can improve minimum expected home visit coverage and promote progress towards UHC when implemented in the proactive community case management context.

Prioritizing Households

UHC Mode screenshot
Contact sorting screenshot

The UHC Mode in the CHT allows health workers to see when a household within their care area was last visited, and prioritize visits accordingly.

When using the UHC Mode, the households in the contact list can be sorted by when they were last visited. The days since the last visit is also shown in the app, along with the number of visits made to a household in a month period.

Configurability

The last visited date is calculated based on the number of days since an action was taken for that household, and the number of visits reflects the actions taken for that household in the current month. What constitutes as an action for a household, along with the start date for the reporting period, are configurable to CHT app developers.


CHT Applications > Quick Guides > Forms > UHC Mode

How to enable Universal Health Coverage monitoring with UHC Mode

CHT Applications > @@ -310,7 +310,8 @@ Reference > app_settings.json > .permissions

User Permissions: Assigning fine grained settings for user roles

-

Last modified 09.03.2023: Training card guide (#970) (fb07ed89)
\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/features/uhc-mode/index.xml b/apps/features/uhc-mode/index.xml index c80f6fbf17..0ef59240a5 100644 --- a/apps/features/uhc-mode/index.xml +++ b/apps/features/uhc-mode/index.xml @@ -1 +1 @@ -Community Health Toolkit – Universal Health Coverage Modehttps://docs.communityhealthtoolkit.org/apps/features/uhc-mode/Recent content in Universal Health Coverage Mode on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file +Universal Health Coverage Mode on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/features/uhc-mode/Recent content in Universal Health Coverage Mode on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file diff --git a/apps/guides/android/branding/index.html b/apps/guides/android/branding/index.html index 8db5389157..97d97a7f96 100644 --- a/apps/guides/android/branding/index.html +++ b/apps/guides/android/branding/index.html @@ -1,9 +1,9 @@ -Building CHT Android Flavors | Community Health Toolkit +Building CHT Android Flavors | Community Health Toolkit

Building CHT Android Flavors

Branding the CHT Android applications

This tutorial will take you through building a CHT Android Application off the existing wrapper.

The CHT Android application is a thin wrapper to load the CHT Core Framework web application in a WebView.

You will be adding a new android flavor based off the CHT Android.

Brief Overview of Key Concepts

The CHT Android is a native Android container for the Community Health Toolkit (CHT). The repository contains “flavored” configurations, where each “flavor” or “brand” is an app. All apps share the same code and features, but can be customized, hard-coding a specific CHT deployment and have a partner specific logo and display name.

Add a new Brand

Adding a new “brand” or “flavor” requires the following steps:

  1. Check you meet the Required Resources.

  2. Add the New Brand in the source code.

  3. Generate a new keystore if there is no one.

  4. Test locally and create a pull request with the changes.

  5. Release the new flavor.

  6. Publish in the Play Store or whatever channel.

Below are more instructions for each step.

1. Required Resources

To proceed you need to have ready the following:

  • The URL of your CHT server so users don’t have to type it in post install.
  • The app logo and title.
  • Translations for your supported languages (most flavors don’t need to customize translations though).

Also be sure to have a working Development Environment.

While you should use your own branding, the CHT logo is available to use if so desired.

Play Store assets

If you are going to publish the app in the Play Store, Google will require to provide the following to list the app:

  • A description of the app.
  • A shorter description (80 characters).
  • Logo 512x512px, typically a version of the partner logo e.g. square design icons.
  • A background image.
  • Screenshots.

Google is constantly changing the requirements to publish in the Play Store, it’s a good practice to check in advance whether all the requirements are met (checkout Add preview assets…).

Test data

When publishing for the first time in the Play Store, a reviewer from Google will try to check whether the permission requested by the app follows the Play Store rules. The CHT Android app has enabled by default location request permissions, and the workflow to request the permission follows the strict rules imposed by Google, but they won’t be aware that your flavored app is based on the CHT Android, so you have to provide Google with instructions of how to test the app, specifically how to test the location request.

To do so, give them instructions of how to login with the app (with a real username and password), and the basic steps to reach the location request, like open up a form.

Once approved you can delete the “test” user, Google conduct the tests only the first time, or when a new permission request is added to the app.

2. New Brand

Each branded app has an identifier (id) that is used to identify and configure it in different parts of the source code and when invoking some commands. In the instructions below we will use as example the id new_brand.

  1. Check out the tag from the last stable release in CHT Android repository and create a branch, for example, if the latest stable release is v0.11.0 and the branch name is v0.11.0-new-brand, then the command is:

    git checkout v0.11.0 -b v0.11.0-new-brand
    + Create project issue

Building CHT Android Flavors

Branding the CHT Android applications

This tutorial will take you through building a CHT Android Application off the existing wrapper.

The CHT Android application is a thin wrapper to load the CHT Core Framework web application in a WebView.

You will be adding a new android flavor based off the CHT Android.

Brief Overview of Key Concepts

The CHT Android is a native Android container for the Community Health Toolkit (CHT). The repository contains “flavored” configurations, where each “flavor” or “brand” is an app. All apps share the same code and features, but can be customized, hard-coding a specific CHT deployment and have a partner specific logo and display name.

Add a new Brand

Adding a new “brand” or “flavor” requires the following steps:

  1. Check you meet the Required Resources.

  2. Add the New Brand in the source code.

  3. Generate a new keystore if there is no one.

  4. Test locally and create a pull request with the changes.

  5. Release the new flavor.

  6. Publish in the Play Store or whatever channel.

Below are more instructions for each step.

1. Required Resources

To proceed you need to have ready the following:

  • The URL of your CHT server so users don’t have to type it in post install.
  • The app logo and title.
  • Translations for your supported languages (most flavors don’t need to customize translations though).

Also be sure to have a working Development Environment.

While you should use your own branding, the CHT logo is available to use if so desired.

Play Store assets

If you are going to publish the app in the Play Store, Google will require to provide the following to list the app:

  • A description of the app.
  • A shorter description (80 characters).
  • Logo 512x512px, typically a version of the partner logo e.g. square design icons.
  • A background image.
  • Screenshots.

Google is constantly changing the requirements to publish in the Play Store, it’s a good practice to check in advance whether all the requirements are met (checkout Add preview assets…).

Test data

When publishing for the first time in the Play Store, a reviewer from Google will try to check whether the permission requested by the app follows the Play Store rules. The CHT Android app has enabled by default location request permissions, and the workflow to request the permission follows the strict rules imposed by Google, but they won’t be aware that your flavored app is based on the CHT Android, so you have to provide Google with instructions of how to test the app, specifically how to test the location request.

To do so, give them instructions of how to login with the app (with a real username and password), and the basic steps to reach the location request, like open up a form.

Once approved you can delete the “test” user, Google conduct the tests only the first time, or when a new permission request is added to the app.

2. New Brand

Each branded app has an identifier (id) that is used to identify and configure it in different parts of the source code and when invoking some commands. In the instructions below we will use as example the id new_brand.

  1. Check out the tag from the last stable release in CHT Android repository and create a branch, for example, if the latest stable release is v0.11.0 and the branch name is v0.11.0-new-brand, then the command is:

    git checkout v0.11.0 -b v0.11.0-new-brand
     
  2. Add productFlavors { <new_brand> { ... } } in build.gradle, e.g.:

    new_brand {
       dimension = 'brand'
       applicationId = 'org.medicmobile.webapp.mobile.new_brand'
    @@ -414,7 +414,8 @@
     Reference >
     app_settings.json >
     .assetlinks

    Assetlinks: Defining the Digital Asset Links JSON file associating your domain with your Android app.

-

\ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/guides/android/index.html b/apps/guides/android/index.html index 8c65767d1a..0409b259ef 100644 --- a/apps/guides/android/index.html +++ b/apps/guides/android/index.html @@ -1,9 +1,9 @@ -Android app development | Community Health Toolkit +Android app development | Community Health Toolkit

Android app development

Guides for developing a CHT Android application

The Community Health Toolkit includes 2 apps that are currently supported: the CHT Android and the CHT Gateway. The following guides will help you setup a development environment in your local computer to improve, release, and brand the apps (branding only applies to CHT Android).

Medic Collect is currently in maintenance mode, although some of the guides here may apply if you need to work with it.


Publishing

Instructions for Publishing Android Apps

Building CHT Android Flavors

Branding the CHT Android applications


CHT Applications > + Create project issue

Android app development

Guides for developing a CHT Android application

The Community Health Toolkit includes 2 apps that are currently supported: the CHT Android and the CHT Gateway. The following guides will help you setup a development environment in your local computer to improve, release, and brand the apps (branding only applies to CHT Android).

Medic Collect is currently in maintenance mode, although some of the guides here may apply if you need to work with it.


Publishing

Instructions for Publishing Android Apps

Building CHT Android Flavors

Branding the CHT Android applications

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/guides/android/index.xml b/apps/guides/android/index.xml index 964e86efd0..f0f6513b3d 100644 --- a/apps/guides/android/index.xml +++ b/apps/guides/android/index.xml @@ -1,298 +1,6 @@ -Community Health Toolkit – Android app developmenthttps://docs.communityhealthtoolkit.org/apps/guides/android/Recent content in Android app development on Community Health ToolkitHugo -- gohugo.ioenApps: Publishinghttps://docs.communityhealthtoolkit.org/apps/guides/android/publishing/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/android/publishing/ -<p>Once the flavor is <a href="https://docs.communityhealthtoolkit.org/apps/guides/android/branding/">built</a> there are many different ways to publish the binaries for installation.</p> -<h3 id="google-play-store">Google Play Store</h3> -<p>The Play Store has the advantage of being installed on all Android phones by default. This makes it very easy for users to install your app, which makes it the approach we recommend for most applications.</p> -<p>One of the downsides is it can be more difficult to get your app published and it may be removed in future if it&rsquo;s found to not comply with future requirements.</p> -<p>For this method you will need access to the organization&rsquo;s play store console with permission to publish the app.</p> -<p>In the <a href="https://play.google.com/console">Google Play Console</a>, for each flavor to publish:</p> -<ul> -<li>Create a new <code>Production</code> release</li> -<li>Upload the <code>arm64</code> app bundles (from the GitHub Release) for the flavor. If you plan on uploading multiple APKs, the APKs should have different version codes. Read more: <a href="https://developer.android.com/google/play/publishing/multiple-apks#Rules">here</a>.</li> -<li>Use the new cht-android version as the Release name</li> -<li>Add a one sentence summary of the CHANGELOG entry as the Release notes.</li> -</ul> -<p>For a more detailed explanation, follow this <a href="https://support.google.com/googleplay/android-developer/answer/9859751?hl=en">doc</a>.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Published apps are not immediately available to users on the Play Store. Confirm, via the Google Play Console, that the release is available before officially announcing it (this could take hours or days). -</div> -<h4 id="new-app-in-the-play-store">New App in the Play Store</h4> -<p>Remember that when the app is created in the Play Store, it&rsquo;s required to choose the way the app will be signed by Google: we upload the signed AAB files, but then Google creates optimized versions of the app in .apk format. The app has to be configured to use the same signing and upload signatures by Google. Choose to upload a &ldquo;Java keystore&rdquo;, the Play Console will require a file encrypted with a tool named PEPK, that file is <code>&lt;brand&gt;_private_key.pepk</code> generated when following the instructions of <a href="https://docs.communityhealthtoolkit.org/apps/guides/android/branding/">New brand</a> (the button to upload the <code>.pepk</code> in the Play Console may say &ldquo;Upload generated ZIP&rdquo; although the PEPK file doesn&rsquo;t look like a .zip file).</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -New apps cannot longer be uploaded with the APK format in the Play Store. Apps created before Aug 1, 2021 can still be updated with <code>.apk</code> files, but new ones needs to be uploaded with the Android App Bundle format (<code>.aab</code>). Checkout the <a href="https://docs.communityhealthtoolkit.org/contribute/code/android/development-setup/#artifact-formats">Artifact formats</a> section. -</div> -<h3 id="side-loading">Side loading</h3> -<p>This method gives an app developer full control over installation. It is also possible to do the installation without an internet connection which makes it ideal for remote installation, or to save bandwidth when performing multiple installs.</p> -<ol> -<li>In the phone settings <a href="https://developer.android.com/distribute/marketing-tools/alternative-distribution#unknown-sources">select the option</a> to &ldquo;opt in for installing unknown apps&rdquo;.</li> -<li>Download the correct APK on to the phone. It&rsquo;s important to select the right APK for the instruction set and Android version, as documented <a href="https://docs.communityhealthtoolkit.org/contribute/code/android/development-setup/#apks">in this table</a>. This is likely easiest done by using the phone&rsquo;s browser to navigate to the download page.</li> -<li>After downloading, you should be prompted to install the APK.</li> -</ol> -<h3 id="f-droid">F-Droid</h3> -<p>F-Droid is a free open source application store which gives the app developer more control over the listing. As it isn&rsquo;t installed on Android devices by default it takes a little more effort to set up originally, but is easier than manually sideloading.</p> -<p>Read more about <a href="https://medic.org/stories/using-f-droid-for-app-distribution-a-product-experiment/">Using F-Droid for app distribution</a>.</p> -<h3 id="mobile-device-management">Mobile Device Management</h3> -<p>Using mobile device management (MDM) software allows administrators to remotely manage mobile devices. This gives the IT administrator full control over which applications are installed on the devices, as well as having the option to delete apps and data from lost or stolen mobile devices. For this reason, using MDM software is highly recommended for deployments.</p> -<p>There are many commercially available MDM tools to evaluate, with a wide range of features and prices. <a href="https://workspace.google.com/intl/en_us/products/admin/endpoint/">Google Endpoint</a> is available for organizations using Google Workspace (formerly G Suite), and has free plans for non-profit organizations. Check out the <a href="https://support.google.com/a/answer/1734200">Endpoint documentation overview</a> for more information, including <a href="https://support.google.com/a/answer/7400753">how to enable mobile device management</a>, and <a href="https://support.google.com/a/answer/173390">how to remotely wipe a device</a>.</p> -<p>Other MDM providers include <a href="https://h-mdm.com/">Headwind MDM</a> and <a href="https://docs.microsoft.com/en-us/mem/intune/">Microsoft Intune</a>. It is recommended that you research MDM options and pick the one that&rsquo;s right for you.</p> -<h3 id="progressive-web-app">Progressive Web App</h3> -<p>Another alternative is to install the CHT Core webapp as a Progressive Web App. This avoids building an Android application altogether. Read more on the <a href="https://docs.communityhealthtoolkit.org/core/overview/pwa/">PWA page</a>.</p>Apps: Building CHT Android Flavorshttps://docs.communityhealthtoolkit.org/apps/guides/android/branding/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/android/branding/ -<div class="pageinfo pageinfo-primary"> -<p>This tutorial will take you through building a CHT Android Application off the existing wrapper.</p> -<p>The CHT Android application is a thin wrapper to load the CHT Core Framework web application in a WebView.</p> -<p>You will be adding a new android flavor based off the <a href="https://github.com/medic/cht-android">CHT Android</a>.</p> -</div> -<h2 id="brief-overview-of-key-concepts">Brief Overview of Key Concepts</h2> -<p>The CHT Android is a native Android container for the Community Health Toolkit (CHT). The repository contains &ldquo;flavored&rdquo; configurations, where each &ldquo;flavor&rdquo; or &ldquo;brand&rdquo; is an app. All apps share the same code and features, but can be customized, hard-coding a specific CHT deployment and have a partner specific logo and display name.</p> -<h2 id="add-a-new-brand">Add a new Brand</h2> -<p>Adding a new <em>&ldquo;brand&rdquo;</em> or <em>&ldquo;flavor&rdquo;</em> requires the following steps:</p> -<ol> -<li> -<p>Check you meet the <strong><a href="#1-required-resources">Required Resources</a></strong>.</p> -</li> -<li> -<p>Add the <strong><a href="#2-new-brand">New Brand</a></strong> in the source code.</p> -</li> -<li> -<p><strong><a href="#3-generate-a-new-keystore">Generate a new keystore</a></strong> if there is no one.</p> -</li> -<li> -<p><strong><a href="#4-test-locally-the-keystore">Test locally</a></strong> and create a pull request with the changes.</p> -</li> -<li> -<p><strong><a href="#5-release-the-new-flavor">Release</a></strong> the new flavor.</p> -</li> -<li> -<p><strong><a href="#6-publish-the-app">Publish</a></strong> in the Play Store or whatever channel.</p> -</li> -</ol> -<p>Below are more instructions for each step.</p> -<h3 id="1-required-resources">1. Required Resources</h3> -<p>To proceed you need to have ready the following:</p> -<ul> -<li>The URL of your CHT server so users don&rsquo;t have to type it in post install.</li> -<li>The app logo and title.</li> -<li>Translations for your supported languages (most flavors don&rsquo;t need to customize translations though).</li> -</ul> -<p>Also be sure to have a working <strong><a href="https://docs.communityhealthtoolkit.org/contribute/code/android/development-setup/">Development Environment</a></strong>.</p> -<p>While you should use your own branding, the <a href="./CHT.logo.512.png">CHT logo</a> is available to use if so desired.</p> -<h4 id="play-store-assets">Play Store assets</h4> -<p>If you are going to publish the app in the <strong>Play Store</strong>, Google will require to provide the following to list the app:</p> -<ul> -<li>A description of the app.</li> -<li>A shorter description (80 characters).</li> -<li>Logo 512x512px, typically a version of the partner logo e.g. square design icons.</li> -<li>A background image.</li> -<li>Screenshots.</li> -</ul> -<p>Google is constantly changing the requirements to publish in the Play Store, it&rsquo;s a good practice to check in advance whether all the requirements are met (checkout <em><a href="https://support.google.com/googleplay/android-developer/answer/9866151">Add preview assets&hellip;</a></em>).</p> -<h5 id="test-data">Test data</h5> -<p>When publishing for the first time in the Play Store, a reviewer from Google will try to check whether the permission requested by the app follows the Play Store rules. The CHT Android app has enabled by default location request permissions, and the workflow to request the permission follows the strict rules imposed by Google, but they won&rsquo;t be aware that your <em>flavored</em> app is based on the CHT Android, so you have to provide Google with instructions of how to test the app, specifically how to test the location request.</p> -<p>To do so, give them instructions of how to login with the app (with a real username and password), and the basic steps to reach the location request, like open up a form.</p> -<p>Once approved you can delete the &ldquo;test&rdquo; user, Google conduct the tests only the first time, or when a new permission request is added to the app.</p> -<h3 id="2-new-brand">2. New Brand</h3> -<p>Each branded app has an identifier (<em>id</em>) that is used to identify and configure it in different parts of the source code and when invoking some commands. In the instructions below we will use as example the id <strong><code>new_brand</code></strong>.</p> -<ol> -<li> -<p>Check out the tag from the <a href="https://github.com/medic/cht-android/releases">last stable release</a> in CHT Android repository and create a branch, for example, if the latest stable release is <code>v0.11.0</code> and the branch name is <code>v0.11.0-new-brand</code>, then the command is:</p> -<pre tabindex="0"><code>git checkout v0.11.0 -b v0.11.0-new-brand -</code></pre></li> -<li> -<p>Add <code>productFlavors { &lt;new_brand&gt; { ... } }</code> in <a href="https://github.com/medic/cht-android/blob/master/build.gradle">build.gradle</a>, e.g.:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#000">new_brand</span> <span style="color:#ce5c00;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">dimension</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#4e9a06">&#39;brand&#39;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">applicationId</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#4e9a06">&#39;org.medicmobile.webapp.mobile.new_brand&#39;</span> -</span></span><span style="display:flex;"><span><span style="color:#ce5c00;font-weight:bold">}</span> -</span></span></code></pre></div></li> -<li> -<p>Add icons, strings etc. in the <code>src/&lt;new_brand&gt;</code> folder. It&rsquo;s required to place there at least the <code>src/new_brand/res/values/strings.xml</code> file with the name of the app and the URL of the CHT instance:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">&lt;?xml version=&#34;1.0&#34; encoding=&#34;utf-8&#34;?&gt;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">&lt;resources&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;string</span> <span style="color:#c4a000">name=</span><span style="color:#4e9a06">&#34;app_name&#34;</span><span style="color:#204a87;font-weight:bold">&gt;</span>New Brand<span style="color:#204a87;font-weight:bold">&lt;/string&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;string</span> <span style="color:#c4a000">name=</span><span style="color:#4e9a06">&#34;app_host&#34;</span><span style="color:#204a87;font-weight:bold">&gt;</span>new_brand.app.medicmobile.org<span style="color:#204a87;font-weight:bold">&lt;/string&gt;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">&lt;/resources&gt;</span> -</span></span></code></pre></div></li> -<li> -<p>Enable automated builds of the APKs and AABs: add the <code>new_brand</code> flavor in <a href="https://github.com/medic/cht-android/blob/master/.github/workflows/publish.yml">.github/workflows/publish.yml</a>. The <em>Unpack secrets &hellip;</em> task unpacks and decrypts the secret file with the keystore (next section), The <em>Assemble &hellip;</em> task takes care of generating the <code>.apk</code> files for sideloading, and the <em>Bundle &hellip;</em> task is responsible of generating the <code>.aab</code> files for publishing in the Play Store (you can skip the last if you are not going to publish in the Play Store):</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yml" data-lang="yml"><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#204a87;font-weight:bold">name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">Unpack secrets new_brand</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">env</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">ANDROID_SECRETS_KEY</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">${{ secrets.ANDROID_SECRETS_KEY_NEW_BRAND }}</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">ANDROID_SECRETS_IV</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">${{ secrets.ANDROID_SECRETS_IV_NEW_BRAND }}</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">run</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">make org=new_brand keydec</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#204a87;font-weight:bold">name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">Assemble new_brand</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">uses</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">maierj/fastlane-action@v1.4.0</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">with</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">lane</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">build</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">options</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#39;{ &#34;flavor&#34;: &#34;new_brand&#34; }&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">env</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">ANDROID_KEYSTORE_PATH</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">new_brand.keystore</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">ANDROID_KEYSTORE_PASSWORD</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">${{ secrets.ANDROID_KEYSTORE_PASSWORD_NEW_BRAND }}</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">ANDROID_KEY_PASSWORD</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">${{ secrets.ANDROID_KEY_PASSWORD_NEW_BRAND }}</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#204a87;font-weight:bold">name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">Bundle new_brand</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">uses</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">maierj/fastlane-action@v1.4.0</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">with</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">lane</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">bundle</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">options</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#39;{ &#34;flavor&#34;: &#34;new_brand&#34; }&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">env</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">ANDROID_KEYSTORE_PATH</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">new_brand.keystore</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">ANDROID_KEYSTORE_PASSWORD</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">${{ secrets.ANDROID_KEYSTORE_PASSWORD_NEW_BRAND }}</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">ANDROID_KEY_PASSWORD</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">${{ secrets.ANDROID_KEY_PASSWORD_NEW_BRAND }}</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span></code></pre></div><p>The variables in the <code>env</code> sections point to a keystore and the passwords to unlock the keystore that will be generated in the following steps, but it&rsquo;s important to follow the name convention, in the example all the variables that are configured in Github Actions end with the suffix <code>_NEW_BRAND</code>, these variables need to be added in the CHT Android repository settings by a manager of Medic.</p> -</li> -</ol> -<h3 id="3-generate-a-new-keystore">3. Generate a new keystore</h3> -<p>Each branded app created needs its own keystore to sign the binaries for releasing.</p> -<p>Since the <code>.aab</code> files generated here are signed with the same key you generated, the files and key can be uploaded to the Play Store later and any file generated locally following the steps above will be compatible with any installation made from the Play Store.</p> -<p>The keystore files are placed into a compressed and encrypted file in the <a href="https://github.com/medic/cht-android/tree/master/secrets">secrets/</a> folder. In our case the file will be <code>secrets/secrets-new_brand.tar.gz.enc</code>, and the content inside when the file is decrypted is:</p> -<ul> -<li><code>new_brand.keystore</code>: the Java keystore with a signature key inside that is always called <code>medicmobile</code>. It&rsquo;s used to sign the APKs and the bundles, and the one that Google will use to sign the optimized APKs that generates in the Play Store.</li> -<li><code>new_brand_private_key.pepk</code>: a PEPK file is an encrypted file that contains inside the <code>medicmobile</code> key from the keystore above, ready to be uploaded to the Play Store the first time the app is registered in the Play Console. The file is only used there, but kept in the compressed file as a backup.</li> -</ul> -<p>Don&rsquo;t worry to follow all the name conventions and how to generate these files, you can create all them in one step: the new keystore, the passwords and the PEPK file with <code>make org=new_brand keygen</code>.</p> -<p>Executing the command will check that you have the necessary tooling installed, and ask you the information about the certificate like the organization name, organization unit, etc. The command also takes care of picking random passwords that meet the security requirements, and then compresses the key files and finally encrypt the <code>.tar.gz</code> file into the <code>.enc</code> file. At the end of the execution, the script will also show the list of environment variables that you have to setup in CI (Github Actions) and locally in order to sign the apps with the new keystore. Below is an example of executing it to create the keystore for our &ldquo;new_brand&rdquo;:</p> -<pre tabindex="0"><code>make org=new_brand keygen -Verifying the following executables are in the $PATH: java keytool openssl ... -keytool -genkey -storepass dd8668... -v -keystore new_brand.keystore -alias medicmobile -keyalg RSA -keysize 2048 -validity 9125 -What is your first and last name? -[Unknown]: -What is the name of your organizational unit? -[Unknown]: New Brand -What is the name of your organization? -[Unknown]: Medic -What is the name of your City or Locality? -[Unknown]: San Fran... ... -Is CN=Unknown, OU=New Brand, O=Medic, L=San Francisco, ST=CA, C=US correct? -[no]: y -Generating 2,048 bit RSA key pair and self-signed certificate (SHA256withRSA) with a validity of 9,125 days -for: CN=Unknown, OU=New Brand, O=Medic, L=San Francisco, ST=CA, C=US -[Storing new_brand.keystore] -... ... -####################################### Secrets! ####################################### -# # -# The following environment variables needs to be added to the CI environment # -# (Github Actions), and to your local environment if you also want # -# to sign APK or AAB files locally: # -# # -export ANDROID_KEYSTORE_PASSWORD_NEW_BRAND=dd8668... -export ANDROID_KEY_PASSWORD_NEW_BRAND=dd8668... -export ANDROID_SECRETS_IV_NEW_BRAND=88d9c2dea7a9... -export ANDROID_SECRETS_KEY_NEW_BRAND=2824d02d2bc221f5844b8fe1d928211dcbbc... -# -# The file secrets/secrets-new_brand.tar.gz.enc was created and has to be added to the git -# repository (don&#39;t worry, it&#39;s encrypted with some of the keys above). # -# NOTE: *keep the environment variables secret !!* # -# # -########################################### End of Secrets ################################### -</code></pre><p>The <em>Secrets!</em> section at the end is as important as the <code>secrets/secrets-new_brand.tar.gz.enc</code> file generated, because as it says above, it needs to be configured in CI.</p> -<p>Use a safe channel to send the environment variables to the manager in charge, like a password manager, and keep them locally at least for testing, storing in a script file that is safe in your computer.</p> -<p>About the file <code>secrets/secrets-new_brand.tar.gz.enc</code>, as the last paragraph in the console says: <em>has to be added to the git repository (don&rsquo;t worry, it&rsquo;s encrypted with some of the keys above)</em>.</p> -<p>If you want to start over because some of the parameters were wrong, just execute <code>make org=new_brand keyrm-all</code> to clean all the files generated. Once committed the <code>.enc</code> file, you can delete the uncompressed and unencrypted version with <code>make org=new_brand keyrm</code>, it will delete the <code>new_brand.keystore</code>, <code>new_brand_private_key.pepk</code>, and the unencrypted <code>.tar.gz</code> files, that are safer kept in the <code>.tar.gz.enc</code> file.</p> -<p>If you encounter issues with the <code>make org=new_brand keygen</code> command repeatedly looping through questions, we recommend changing your OS language to English.</p> -<h3 id="4-test-the-keystore-locally">4. Test the keystore locally</h3> -<p><strong>Want to check the keystore?</strong> here are a few things you must test before upload to the repository:</p> -<ol> -<li> -<p>To decrypt the content like CI does to sign the app, execute: <code>make org=new_brand keydec</code>, it will decrypt and decompress the files removed in the step above. Remember that the environment variables printed in the console needs to be loaded in the CLI. Note that all the variables above end with the suffix <code>_NEW_BRAND</code>, as the id of the app that we pass through the <code>org</code> argument in lowercase, but if Make found the same variables defined without the prefix, they take precedence over the suffix ones.</p> -</li> -<li> -<p>Execute <code>make org=new_brand keyprint</code> to see the certificate content, like the org name, the certificate fingerprints, etc.</p> -</li> -<li> -<p>Sign your app! You can try locally to build the app with the certificate. To create the .apk files run: <code>make org=new_brand flavor=New_brand assemble</code>. The &ldquo;release&rdquo; files signed should be placed in <code>build/outputs/apk/new_brand/release/</code>. To ensure the files were signed with the right signature execute <code>make keyprint-apk</code>, it will check the certificate of the first apk file under the <code>build/</code> folder:</p> -<pre tabindex="0"><code>make keyprint-apk -apksigner verify -v --print-certs build/outputs/apk/new_brand/release/cht-android-SNAPSHOT-new_brand-arm64-v8a-release.apk -... ... -Verified using v2 scheme (APK Signature Scheme v2): true -... ... -Signer #1 certificate DN: CN=Unknown, OU=New Brand, O=Medic Mobile, L=San Francisco, ST=CA, C=US -Signer #1 certificate SHA-256 digest: 7f072b... -</code></pre></li> -</ol> -<p>Also, do the same for the bundle format: build and verify, despite the AAB are not useful for local development. In our example, execute first <code>make org=new_brand flavor=New_brand bundle</code>, and then <code>make keyprint-bundle</code> to see the signature of one of the <code>.aab</code> files generated.</p> -<p>Because the files generated here are signed with the same key that you are going to use in CI, and the files produced in CI will be uploaded to the Play Store later, any file generated locally following the steps above will be compatible with any installation made from the Play Store, means that if a user install the app from the Play Store, and then we want to replace the installation with an alpha version generated in CI or a local version generated in dev environment, it will work without requiring the user to uninstall the app and lost the data.</p> -<h3 id="5-release-the-new-flavor">5. Release the new flavor</h3> -<p>Releasing a new flavor requires the following steps:</p> -<ol> -<li>Make a pull request to the release branch in the CHT Android repository.</li> -<li>Once approved it&rsquo;s recommended to create an alpha version to do final tests.</li> -<li>Merge the pull request.</li> -<li><a href="https://docs.communityhealthtoolkit.org/contribute/code/android/releasing/">Release the flavor</a>.</li> -</ol> -<h3 id="6-publish-the-app">6. Publish the app</h3> -<p>The last step is to publish it in the Play Store, or whatever option best suit your needs. Checkout the <a href="https://docs.communityhealthtoolkit.org/apps/guides/android/publishing/">Publishing</a> page to see all the options available and instructions.</p> -<h2 id="android-app-links-verification">Android App Links verification</h2> -<p><em>Supported for CHT Core 4.7.0+ and CHT Android 1.3.0+</em></p> -<p>Starting with Android 12, Android supports associating an app with a domain and automatically verifying this association. This allows deep links to immediately open content in the app. To get this working, you need to host a Digital Asset Links JSON file at <code>https://&lt;domain.name&gt;/.well-known/assetlinks.json</code> containing some information about your app to associate it with your domain. More information is available on the <a href="https://developer.android.com/training/app-links/verify-android-applinks">official Android docs</a>.</p> -<h3 id="hosting-assetlinksjson-with-the-cht">Hosting <code>assetlinks.json</code> with the CHT</h3> -<p>Since CHT Core version 4.7.0, the CHT supports serving <code>assetlinks.json</code> by adding it to your app settings. -All you have to do to make the CHT serve your assetlinks at <code>/.well-known/assetlinks.json</code> is to:</p> -<ol> -<li>Ensure your flavor of cht-android <a href="#3-generate-a-new-keystore">has a valid keystore</a>.</li> -<li>Use the <code>keytool</code> utility (included with your Java SDK) to get your app&rsquo;s cert fingerprint: -<pre tabindex="0"><code>keytool -list -v -keystore ./path/to/release-key.keystore -# or alternatively: -keytool -printcert -jarfile ./path/to/project.apk -</code></pre></li> -<li>Set the cert fingerprint in the <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/assetlinks/"><code>assetlinks</code> configuration</a> for your CHT instance and deploy it to your server with cht-conf.</li> -</ol> -<h4 id="note-for-apps-using-google-play-signing">Note for Apps Using Google Play Signing</h4> -<p>For apps signed by Google Play, you need to use the SHA256 fingerprint provided in the Google Play Console to ensure successful domain verification.</p> -<h4 id="steps-to-retrieve-sha256-fingerprint-from-google-play">Steps to Retrieve SHA256 Fingerprint from Google Play</h4> -<ol> -<li><strong>Log in to Google Play Console</strong>.</li> -<li><strong>Navigate to your app</strong>: Select your app from the list of published applications.</li> -<li><strong>Setup &gt; App signing</strong>: In the left-hand menu, under the Setup Menu, there is App Signing.</li> -<li><strong>Find SHA256 fingerprint</strong>: Google Play will display the SHA256 fingerprint required for your app.</li> -<li><strong>Update assetlinks.json</strong>: Use this SHA256 fingerprint in your <code>assetlinks.json</code> file. (Google Play also provides the full JSON)</li> -</ol> -<p>Once added and pushed to the server, the deep link can be monitored from the console as well, under the <strong>Deep Links</strong> in the left-hand menu where the Play Console allows you to check the deep link settings and also provides hints to fix any errors (providing the right SHA256 fingerprint to add).</p> -<h3 id="verifying-it-works">Verifying it works</h3> -<p>There are different ways to verify your setup works and we&rsquo;ll go through a few of them in the next steps.</p> -<h4 id="using-android-debug-bridge-adb">Using Android Debug Bridge <code>adb</code></h4> -<ol> -<li>To install the <code>adb</code> command, follow the instructions under the <a href="https://docs.communityhealthtoolkit.org/contribute/code/android/development-setup/#debug-tool-adb">Development Environment &gt; Debug tool adb</a> section.</li> -<li>With the phone connected to your computer, open a command line session and write the following command: <code>adb shell pm get-app-links &lt;package_name&gt;</code> where <code>&lt;package_name&gt;</code> is your application ID.</li> -</ol> -<p>The output of this command should look like this:</p> -<pre tabindex="0"><code>&lt;package_name&gt;: -ID: 01234567-89ab-cdef-0123-456789abcdef -Signatures: [&#34;62:BF:C1:78:24:D8:4D:5C:B4:E1:8B:66:98:EA:14:16:57:6F:A4:E5:96:CD:93:81:B2:65:19:71:A7:80:EA:4D&#34;] -Domain verification state: -mobile.webapp.medicmobile.org: verified -</code></pre><p>The domain verification state for your CHT instance&rsquo;s domain should show <code>verified</code>.</p> -<h4 id="manually-testing-on-the-device">Manually testing on the device</h4> -<figure class="right col-6 col-md-4 col-lg-2"><a href="android-12-prompt.png"> -<img src="android-12-prompt.png"/> </a> -</figure> -<p>Another way of verifying your Android app has been properly associated to your CHT instance&rsquo;s domain is by opening -the Android app on a device. You can run this test on a real device or with the emulator in Android Studio.</p> -<p>Opening the app for the first time should take you straight to the login page <strong>without</strong> prompting you to link a domain to the app as shown in the following screenshot:</p> -<p>Additionally, clicking a link to your CHT instance should open the app immediately instead of opening the CHT instance in the default browser.</p> -<h3 id="use-case---a-single-android-app-for-many-cht-instances">Use case - a single Android app for many CHT instances</h3> -<p>For specific large deployment scenarios, you might publish a single Android app to serve multiple CHT instances. -In this case, each CHT instance&rsquo;s app settings will need to be configured with the same <code>assetlinks.json</code> because -they share the same Android app and hence the same <code>package_name</code> and <code>sha256_cert_fingerprints</code> properties.</p> -<p>When building your Android app, you will need to ensure the app&rsquo;s manifest has <code>&lt;intent-filter android:autoverify=&quot;true&quot;&gt;</code> -with each CHT instance&rsquo;s domain listed in it like so:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">&lt;?xml version=&#34;1.0&#34; encoding=&#34;utf-8&#34;?&gt;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">&lt;manifest</span> <span style="color:#c4a000">xmlns:android=</span><span style="color:#4e9a06">&#34;http://schemas.android.com/apk/res/android&#34;</span> <span style="color:#c4a000">xmlns:tools=</span><span style="color:#4e9a06">&#34;http://schemas.android.com/tools&#34;</span><span style="color:#204a87;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;uses-permission</span> <span style="color:#c4a000">android:name=</span><span style="color:#4e9a06">&#34;android.permission.READ_EXTERNAL_STORAGE&#34;</span> <span style="color:#c4a000">tools:node=</span><span style="color:#4e9a06">&#34;remove&#34;</span> <span style="color:#204a87;font-weight:bold">/&gt;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;application&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;activity</span> <span style="color:#c4a000">android:name=</span><span style="color:#4e9a06">&#34;AppUrlIntentActivity&#34;</span> <span style="color:#c4a000">android:launchMode=</span><span style="color:#4e9a06">&#34;singleInstance&#34;</span> <span style="color:#c4a000">android:exported=</span><span style="color:#4e9a06">&#34;true&#34;</span><span style="color:#204a87;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;intent-filter</span> <span style="color:#c4a000">android:autoVerify=</span><span style="color:#4e9a06">&#34;true&#34;</span><span style="color:#204a87;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;action</span> <span style="color:#c4a000">android:name=</span><span style="color:#4e9a06">&#34;android.intent.action.VIEW&#34;</span> <span style="color:#204a87;font-weight:bold">/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;category</span> <span style="color:#c4a000">android:name=</span><span style="color:#4e9a06">&#34;android.intent.category.DEFAULT&#34;</span> <span style="color:#204a87;font-weight:bold">/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;category</span> <span style="color:#c4a000">android:name=</span><span style="color:#4e9a06">&#34;android.intent.category.BROWSABLE&#34;</span> <span style="color:#204a87;font-weight:bold">/&gt;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;data</span> <span style="color:#c4a000">android:scheme=</span><span style="color:#4e9a06">&#34;https&#34;</span> <span style="color:#c4a000">android:host=</span><span style="color:#4e9a06">&#34;first-cht-app.org&#34;</span> <span style="color:#c4a000">android:pathPattern=</span><span style="color:#4e9a06">&#34;.*&#34;</span><span style="color:#204a87;font-weight:bold">/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;data</span> <span style="color:#c4a000">android:scheme=</span><span style="color:#4e9a06">&#34;https&#34;</span> <span style="color:#c4a000">android:host=</span><span style="color:#4e9a06">&#34;second-cht-app.org&#34;</span> <span style="color:#c4a000">android:pathPattern=</span><span style="color:#4e9a06">&#34;.*&#34;</span><span style="color:#204a87;font-weight:bold">/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;data</span> <span style="color:#c4a000">android:scheme=</span><span style="color:#4e9a06">&#34;https&#34;</span> <span style="color:#c4a000">android:host=</span><span style="color:#4e9a06">&#34;third-cht-app.org&#34;</span> <span style="color:#c4a000">android:pathPattern=</span><span style="color:#4e9a06">&#34;.*&#34;</span><span style="color:#204a87;font-weight:bold">/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/intent-filter&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/activity&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/application&gt;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">&lt;/manifest&gt;</span> -</span></span></code></pre></div> \ No newline at end of file +Android app development on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/guides/android/Recent content in Android app development on Community Health ToolkitHugo -- gohugo.ioenPublishinghttps://docs.communityhealthtoolkit.org/apps/guides/android/publishing/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/android/publishing/Once the flavor is built there are many different ways to publish the binaries for installation. +Google Play Store The Play Store has the advantage of being installed on all Android phones by default. This makes it very easy for users to install your app, which makes it the approach we recommend for most applications. +One of the downsides is it can be more difficult to get your app published and it may be removed in future if it&rsquo;s found to not comply with future requirements.Building CHT Android Flavorshttps://docs.communityhealthtoolkit.org/apps/guides/android/branding/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/android/branding/This tutorial will take you through building a CHT Android Application off the existing wrapper. +The CHT Android application is a thin wrapper to load the CHT Core Framework web application in a WebView. +You will be adding a new android flavor based off the CHT Android. +Brief Overview of Key Concepts The CHT Android is a native Android container for the Community Health Toolkit (CHT). The repository contains &ldquo;flavored&rdquo; configurations, where each &ldquo;flavor&rdquo; or &ldquo;brand&rdquo; is an app. \ No newline at end of file diff --git a/apps/guides/android/publishing/index.html b/apps/guides/android/publishing/index.html index 18a9329610..3a48a680a3 100644 --- a/apps/guides/android/publishing/index.html +++ b/apps/guides/android/publishing/index.html @@ -1,9 +1,9 @@ -Publishing | Community Health Toolkit +Publishing | Community Health Toolkit

Publishing

Instructions for Publishing Android Apps

Once the flavor is built there are many different ways to publish the binaries for installation.

Google Play Store

The Play Store has the advantage of being installed on all Android phones by default. This makes it very easy for users to install your app, which makes it the approach we recommend for most applications.

One of the downsides is it can be more difficult to get your app published and it may be removed in future if it’s found to not comply with future requirements.

For this method you will need access to the organization’s play store console with permission to publish the app.

In the Google Play Console, for each flavor to publish:

  • Create a new Production release
  • Upload the arm64 app bundles (from the GitHub Release) for the flavor. If you plan on uploading multiple APKs, the APKs should have different version codes. Read more: here.
  • Use the new cht-android version as the Release name
  • Add a one sentence summary of the CHANGELOG entry as the Release notes.

For a more detailed explanation, follow this doc.

New App in the Play Store

Remember that when the app is created in the Play Store, it’s required to choose the way the app will be signed by Google: we upload the signed AAB files, but then Google creates optimized versions of the app in .apk format. The app has to be configured to use the same signing and upload signatures by Google. Choose to upload a “Java keystore”, the Play Console will require a file encrypted with a tool named PEPK, that file is <brand>_private_key.pepk generated when following the instructions of New brand (the button to upload the .pepk in the Play Console may say “Upload generated ZIP” although the PEPK file doesn’t look like a .zip file).

Side loading

This method gives an app developer full control over installation. It is also possible to do the installation without an internet connection which makes it ideal for remote installation, or to save bandwidth when performing multiple installs.

  1. In the phone settings select the option to “opt in for installing unknown apps”.
  2. Download the correct APK on to the phone. It’s important to select the right APK for the instruction set and Android version, as documented in this table. This is likely easiest done by using the phone’s browser to navigate to the download page.
  3. After downloading, you should be prompted to install the APK.

F-Droid

F-Droid is a free open source application store which gives the app developer more control over the listing. As it isn’t installed on Android devices by default it takes a little more effort to set up originally, but is easier than manually sideloading.

Read more about Using F-Droid for app distribution.

Mobile Device Management

Using mobile device management (MDM) software allows administrators to remotely manage mobile devices. This gives the IT administrator full control over which applications are installed on the devices, as well as having the option to delete apps and data from lost or stolen mobile devices. For this reason, using MDM software is highly recommended for deployments.

There are many commercially available MDM tools to evaluate, with a wide range of features and prices. Google Endpoint is available for organizations using Google Workspace (formerly G Suite), and has free plans for non-profit organizations. Check out the Endpoint documentation overview for more information, including how to enable mobile device management, and how to remotely wipe a device.

Other MDM providers include Headwind MDM and Microsoft Intune. It is recommended that you research MDM options and pick the one that’s right for you.

Progressive Web App

Another alternative is to install the CHT Core webapp as a Progressive Web App. This avoids building an Android application altogether. Read more on the PWA page.


Contributor Handbook > + Create project issue

Publishing

Instructions for Publishing Android Apps

Once the flavor is built there are many different ways to publish the binaries for installation.

Google Play Store

The Play Store has the advantage of being installed on all Android phones by default. This makes it very easy for users to install your app, which makes it the approach we recommend for most applications.

One of the downsides is it can be more difficult to get your app published and it may be removed in future if it’s found to not comply with future requirements.

For this method you will need access to the organization’s play store console with permission to publish the app.

In the Google Play Console, for each flavor to publish:

  • Create a new Production release
  • Upload the arm64 app bundles (from the GitHub Release) for the flavor. If you plan on uploading multiple APKs, the APKs should have different version codes. Read more: here.
  • Use the new cht-android version as the Release name
  • Add a one sentence summary of the CHANGELOG entry as the Release notes.

For a more detailed explanation, follow this doc.

New App in the Play Store

Remember that when the app is created in the Play Store, it’s required to choose the way the app will be signed by Google: we upload the signed AAB files, but then Google creates optimized versions of the app in .apk format. The app has to be configured to use the same signing and upload signatures by Google. Choose to upload a “Java keystore”, the Play Console will require a file encrypted with a tool named PEPK, that file is <brand>_private_key.pepk generated when following the instructions of New brand (the button to upload the .pepk in the Play Console may say “Upload generated ZIP” although the PEPK file doesn’t look like a .zip file).

Side loading

This method gives an app developer full control over installation. It is also possible to do the installation without an internet connection which makes it ideal for remote installation, or to save bandwidth when performing multiple installs.

  1. In the phone settings select the option to “opt in for installing unknown apps”.
  2. Download the correct APK on to the phone. It’s important to select the right APK for the instruction set and Android version, as documented in this table. This is likely easiest done by using the phone’s browser to navigate to the download page.
  3. After downloading, you should be prompted to install the APK.

F-Droid

F-Droid is a free open source application store which gives the app developer more control over the listing. As it isn’t installed on Android devices by default it takes a little more effort to set up originally, but is easier than manually sideloading.

Read more about Using F-Droid for app distribution.

Mobile Device Management

Using mobile device management (MDM) software allows administrators to remotely manage mobile devices. This gives the IT administrator full control over which applications are installed on the devices, as well as having the option to delete apps and data from lost or stolen mobile devices. For this reason, using MDM software is highly recommended for deployments.

There are many commercially available MDM tools to evaluate, with a wide range of features and prices. Google Endpoint is available for organizations using Google Workspace (formerly G Suite), and has free plans for non-profit organizations. Check out the Endpoint documentation overview for more information, including how to enable mobile device management, and how to remotely wipe a device.

Other MDM providers include Headwind MDM and Microsoft Intune. It is recommended that you research MDM options and pick the one that’s right for you.

Progressive Web App

Another alternative is to install the CHT Core webapp as a Progressive Web App. This avoids building an Android application altogether. Read more on the PWA page.

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/guides/data/analytics/environment-variables/index.html b/apps/guides/data/analytics/environment-variables/index.html index 1e4bdcd5f7..32674ae9f6 100644 --- a/apps/guides/data/analytics/environment-variables/index.html +++ b/apps/guides/data/analytics/environment-variables/index.html @@ -1,9 +1,9 @@ -Environment Variables | Community Health Toolkit +Environment Variables | Community Health Toolkit

Environment Variables

Environment variables for running CHT Sync

There are three environment variable groups in the .env file. To successfully set up CHT Sync, it is important to understand the difference between them.

  1. POSTGRES_: Used by PostgreSQL to establish the PostgreSQL database to synchronize CouchDB data to. They also define the schema and table names to store the CouchDB data. The main objective is to define the environment where the raw CouchDB data will be copied.
  2. DBT_: Exclusive to the DBT configuration. The main objective is to define the environment where the tables and views for the models defined in CHT_PIPELINE_BRANCH_URL will be created. It is important to separate this environment from the previous group. DBT_POSTGRES_SCHEMA must be different from POSTGRES_SCHEMA. DBT_POSTGRES_HOST has to be the Postgres instance created with the environment variables set in the first group.
  3. COUCHDB_: Used by CouchDB to define the CouchDB instance to sync with. With COUCHDB_DBS, we can specify a list of databases to sync.

All the variables in the .env file:

NameDefaultDescription
COMPOSE_PROJECT_NAMEpipeline(Optional) Docker Compose name
POSTGRES_USERpostgresUsername of the PostgreSQL database to copy CouchDB data to
POSTGRES_PASSWORDpostgresPassword of the PostgreSQL database to copy CouchDB data to
POSTGRES_DBdataPostgreSQL database where the CouchDB data is copied
POSTGRES_SCHEMAv1PostgreSQL schema where the CouchDB data is copied
POSTGRES_TABLEmedicPostgreSQL table where the CouchDB data is copied. For DBT use only.
POSTGRES_HOSTpostgresPostgreSQL instance to copy CouchDB data to. To be set only if the PostgreSQL instance is different than the container provided with CHT Sync.
DBT_POSTGRES_USERpostgresUsername of the PostgreSQL database where DBT creates tables and views from the models in CHT_PIPELINE_BRANCH_URL
DBT_POSTGRES_PASSWORDpostgresPassword of the PostgreSQL database where DBT creates tables and views from the models in CHT_PIPELINE_BRANCH_URL
DBT_POSTGRES_SCHEMAdbtPostgreSQL schema where DBT creates tables and views from the models in CHT_PIPELINE_BRANCH_URL
DBT_POSTGRES_HOSTpostgresPostgreSQL instance IP or endpoint
CHT_PIPELINE_BRANCH_URL"https://github.com/medic/cht-pipeline.git#main"CHT Pipeline branch containing the DBT models
DATAEMON_INTERVAL5Interval (in minutes) for looking for new changes in the CouchDB data
COUCHDB_USERmedicUsername of the CouchDB instance to sync with
COUCHDB_PASSWORDpasswordPassword of the CouchDB instance to sync with
COUCHDB_DBS"medic"Space separated list of databases to sync e.g "medic medic_sentinel"
COUCHDB_HOSTcouchdbHost of the CouchDB instance to sync with
COUCHDB_PORT5984Port of the CouchDB instance to sync with
COUCHDB_SECUREfalseIs connection to CouchDB instance secure?
-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/guides/data/analytics/index.html b/apps/guides/data/analytics/index.html index 61c892428b..82a1337135 100644 --- a/apps/guides/data/analytics/index.html +++ b/apps/guides/data/analytics/index.html @@ -1,9 +1,9 @@ -Data Synchronization and Analytics | Community Health Toolkit +Data Synchronization and Analytics | Community Health Toolkit

Data Synchronization and Analytics

Using CHT Sync and CHT Pipeline for data synchronization and analytics

Introduction & Prerequisites to data synchronization and analytics

High level approach to data synchronization and analytics with CHT applications

Local CHT Sync Setup

Setting up a local deployment of CHT Sync with the CHT

Production CHT Sync Setup

Setting up a production deployment of CHT Sync with the CHT

Environment Variables

Environment variables for running CHT Sync

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/guides/data/analytics/index.xml b/apps/guides/data/analytics/index.xml index b7705c5bbb..b94b9a1f30 100644 --- a/apps/guides/data/analytics/index.xml +++ b/apps/guides/data/analytics/index.xml @@ -1,168 +1,5 @@ -Community Health Toolkit – Data Synchronization and Analyticshttps://docs.communityhealthtoolkit.org/apps/guides/data/analytics/Recent content in Data Synchronization and Analytics on Community Health ToolkitHugo -- gohugo.ioenApps: Introduction & Prerequisites to data synchronization and analyticshttps://docs.communityhealthtoolkit.org/apps/guides/data/analytics/introduction/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/analytics/introduction/ -<div class="pageinfo pageinfo-primary"> -<p>The pages in this section apply to both CHT 3.x (beyond 3.12) and CHT 4.x.</p> -<p><a href="https://github.com/medic/cht-sync/blob/main/postgres/init-dbt-resources.sh">CHT Sync schema</a> differs from <a href="https://github.com/medic/cht-couch2pg">CHT Couch2pg</a>.</p> -</div> -<p>Most CHT deployments require some sort of analytics so that stakeholders can make data driven decisions. CouchDB, which is the database used by the CHT, is not designed for analytics. It is a document database, which means that it is optimized for storing and retrieving documents, and not for aggregating data. For example, if you wanted to know how many patients were registered in a particular area, you would have to query the database for all the patients in that area, and then count them. This is not a very efficient process. It is much more efficient to store the number of patients in a particular area in a separate database, and update that number whenever a patient is registered or unregistered. This is what CHT Sync paired with CHT Pipeline is designed to do.</p> -<h2 id="cht-sync-introduction">CHT Sync Introduction</h2> -<p>Medic maintains CHT Sync which is an integrated solution designed to enable data synchronization between CouchDB and PostgreSQL for the purpose of analytics. It can easily be deployed using Docker. It is supported on CHT 3.12 and later, including CHT 4.x. By using CHT Sync, a CHT deployment can easily get analytics by using a data visualization tool. All tools are open-source and have no licensing fees.</p> -<p>CHT Sync has been designed to work in both local development environments for testing models or workflows, and in production environments. The setup can accommodate the needs of different environments.</p> -<h2 id="cht-sync-prerequisites">CHT Sync Prerequisites</h2> -<ul> -<li><a href="https://docs.docker.com/install/">Docker</a></li> -<li><a href="https://docs.docker.com/compose/install/">Docker Compose</a></li> -<li><a href="https://git-scm.com/book/en/v2/Getting-Started-Installing-Git">git</a></li> -<li><a href="https://docs.npmjs.com/downloading-and-installing-node-js-and-npm">Node and npm</a> (Node 18 LTS or newer)</li> -<li><a href="https://github.com/medic/cht-sync">CHT Sync</a> GitHub repository (can be cloned via <code>git clone https://github.com/medic/cht-sync</code>).</li> -</ul>Apps: Local CHT Sync Setuphttps://docs.communityhealthtoolkit.org/apps/guides/data/analytics/setup/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/analytics/setup/ -<p>Before setting up CHT Sync in production, it&rsquo;s very handy to be able to run it locally. This will allow you to experiment with the data flow and easily query development data quickly and locally.</p> -<p>These instructions assume you&rsquo;re running CHT Sync, CHT Core and PostgreSQL either locally on your workstation or on a local server. They are not meant to be used to deploy a secure, always on production instance.</p> -<h2 id="setup">Setup</h2> -<p>Copy the values in <code>env.template</code> file to the <code>.env</code> file. For more information, see the references on the <a href="https://docs.communityhealthtoolkit.org/apps/guides/data/analytics/environment-variables/">Environment variables page</a>.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -The first time you run the commands from any of the sections below it will need to download many Docker images and will take a while. You&rsquo;ll know it&rsquo;s done when you see <code>#8 DONE 0.0s</code> and you are returned to the command line. Be patient! -</div> -<h3 id="run-all-cht-sync-services-locally">Run all CHT Sync services locally</h3> -<p>This setup involves starting couch2pg, PostgreSQL, pgAdmin, DBT, and CouchDB.</p> -<p>Run the Docker containers and wait for every container to be up and running:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>docker-compose -f docker-compose.couchdb.yml -f docker-compose.postgres.yml -f docker-compose.yml up -d -</span></span></code></pre></div><p>You can verify this command worked by running <code>docker ps</code>. It should show 5 containers running including couch2pg, DBT, PostgreSQL, CouchDB and pgAdmin.</p> -<p>Now that all services are running, use pgAdmin to connect to server <code>postgres:5432</code> with user <code>postgres</code> and password <code>postgres</code>. You should be able to see data being inserted into the <code>v1.medic</code> table when inserting sample data into the CouchDB instance.</p> -<h3 id="separate-couchdb-instance">Separate CouchDB instance</h3> -<p>This setup involves starting couch2pg, PostgreSQL, pgAdmin and DBT. It assumes you have a CouchDB instance running, and you updated the <code>.env</code> CouchDB variables accordingly.</p> -<p>Run the Docker containers locally and wait for every container to be up and running:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>docker-compose -f docker-compose.postgres.yml -f docker-compose.yml up -d -</span></span></code></pre></div><p>You can verify this command worked by running <code>docker ps</code>. It should show 4 containers running including couch2pg, DBT, PostgreSQL, and pgAdmin.</p> -<h3 id="separate-couchdb-and-postgresql-instances">Separate CouchDB and PostgreSQL instances</h3> -<p>This local setup involves starting couch2pg and DBT. It assumes that CouchDB and PostgreSQL instances are run separately from the Docker Compose provided with CHT Sync, and the <code>.env</code> variables were updated to match those instances details.</p> -<p>Run the Docker containers locally and wait for every container to be up and running:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>docker-compose -f docker-compose.yml up -d -</span></span></code></pre></div><p>You can verify this command worked by running <code>docker ps</code>. It should show 2 containers running: couch2pg and DBT.</p> -<h3 id="cleanup">Cleanup</h3> -<p>When you are done using the services, you can clean everything by running <code>down</code>.</p> -<p>For example, in the scenario of <a href="#run-all-cht-sync-services-locally">running all CHT Sync services</a>, the command should look like:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>docker compose -f docker-compose.couchdb.yml -f docker-compose.postgres.yml -f docker-compose.yml down -</span></span></code></pre></div><p>To remove all the data volumes, add <code>-v</code> at the end of this command.</p> -<h2 id="setup-superset">Setup Superset</h2> -<p>To build data visualization dashboards, follow the <a href="https://superset.apache.org/docs/installation/installing-superset-using-docker-compose/">Superset instructions</a> to run Superset and connect it to the PostgreSQL database.</p>Apps: Production CHT Sync Setuphttps://docs.communityhealthtoolkit.org/apps/guides/data/analytics/production/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/analytics/production/ -<div class="pageinfo pageinfo-primary"> -<p><strong>This is under active development.</strong> The page will be updated accordingly once it&rsquo;s validated that the current approach is suited for being used in production. For the time being, you can try out and test CHT Sync <a href="https://docs.communityhealthtoolkit.org/apps/guides/data/analytics/setup/">locally</a>.</p> -</div>Apps: Environment Variableshttps://docs.communityhealthtoolkit.org/apps/guides/data/analytics/environment-variables/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/analytics/environment-variables/ -<p>There are three environment variable groups in the <code>.env</code> file. To successfully set up CHT Sync, it is important to understand the difference between them.</p> -<ol> -<li><code>POSTGRES_</code>: Used by PostgreSQL to establish the PostgreSQL database to synchronize CouchDB data to. They also define the schema and table names to store the CouchDB data. The main objective is to define the environment where the raw CouchDB data will be copied.</li> -<li><code>DBT_</code>: Exclusive to the DBT configuration. The main objective is to define the environment where the tables and views for the models defined in <code>CHT_PIPELINE_BRANCH_URL</code> will be created. It is important to separate this environment from the previous group. <code>DBT_POSTGRES_SCHEMA</code> must be different from <code>POSTGRES_SCHEMA</code>. <code>DBT_POSTGRES_HOST</code> has to be the Postgres instance created with the environment variables set in the first group.</li> -<li><code>COUCHDB_</code>: Used by CouchDB to define the CouchDB instance to sync with. With <code>COUCHDB_DBS</code>, we can specify a list of databases to sync.</li> -</ol> -<p>All the variables in the <code>.env</code> file:</p> -<table> -<thead> -<tr> -<th>Name</th> -<th>Default</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>COMPOSE_PROJECT_NAME</code></td> -<td><code>pipeline</code></td> -<td>(Optional) Docker Compose name</td> -</tr> -<tr> -<td><code>POSTGRES_USER</code></td> -<td><code>postgres</code></td> -<td>Username of the PostgreSQL database to copy CouchDB data to</td> -</tr> -<tr> -<td><code>POSTGRES_PASSWORD</code></td> -<td><code>postgres</code></td> -<td>Password of the PostgreSQL database to copy CouchDB data to</td> -</tr> -<tr> -<td><code>POSTGRES_DB</code></td> -<td><code>data</code></td> -<td>PostgreSQL database where the CouchDB data is copied</td> -</tr> -<tr> -<td><code>POSTGRES_SCHEMA</code></td> -<td><code>v1</code></td> -<td>PostgreSQL schema where the CouchDB data is copied</td> -</tr> -<tr> -<td><code>POSTGRES_TABLE</code></td> -<td><code>medic</code></td> -<td>PostgreSQL table where the CouchDB data is copied. For <code>DBT</code> use only.</td> -</tr> -<tr> -<td><code>POSTGRES_HOST</code></td> -<td><code>postgres</code></td> -<td>PostgreSQL instance to copy CouchDB data to. To be set only if the PostgreSQL instance is different than the container provided with CHT Sync.</td> -</tr> -<tr> -<td><code>DBT_POSTGRES_USER</code></td> -<td><code>postgres</code></td> -<td>Username of the PostgreSQL database where <code>DBT</code> creates tables and views from the models in <code>CHT_PIPELINE_BRANCH_URL</code></td> -</tr> -<tr> -<td><code>DBT_POSTGRES_PASSWORD</code></td> -<td><code>postgres</code></td> -<td>Password of the PostgreSQL database where <code>DBT</code> creates tables and views from the models in <code>CHT_PIPELINE_BRANCH_URL</code></td> -</tr> -<tr> -<td><code>DBT_POSTGRES_SCHEMA</code></td> -<td><code>dbt</code></td> -<td>PostgreSQL schema where <code>DBT</code> creates tables and views from the models in <code>CHT_PIPELINE_BRANCH_URL</code></td> -</tr> -<tr> -<td><code>DBT_POSTGRES_HOST</code></td> -<td><code>postgres</code></td> -<td>PostgreSQL instance IP or endpoint</td> -</tr> -<tr> -<td><code>CHT_PIPELINE_BRANCH_URL</code></td> -<td><code>&quot;https://github.com/medic/cht-pipeline.git#main&quot;</code></td> -<td>CHT Pipeline branch containing the <code>DBT</code> models</td> -</tr> -<tr> -<td><code>DATAEMON_INTERVAL</code></td> -<td><code>5</code></td> -<td>Interval (in minutes) for looking for new changes in the CouchDB data</td> -</tr> -<tr> -<td><code>COUCHDB_USER</code></td> -<td><code>medic</code></td> -<td>Username of the CouchDB instance to sync with</td> -</tr> -<tr> -<td><code>COUCHDB_PASSWORD</code></td> -<td><code>password</code></td> -<td>Password of the CouchDB instance to sync with</td> -</tr> -<tr> -<td><code>COUCHDB_DBS</code></td> -<td><code>&quot;medic&quot;</code></td> -<td>Space separated list of databases to sync e.g <code>&quot;medic medic_sentinel&quot;</code></td> -</tr> -<tr> -<td><code>COUCHDB_HOST</code></td> -<td><code>couchdb</code></td> -<td>Host of the CouchDB instance to sync with</td> -</tr> -<tr> -<td><code>COUCHDB_PORT</code></td> -<td><code>5984</code></td> -<td>Port of the CouchDB instance to sync with</td> -</tr> -<tr> -<td><code>COUCHDB_SECURE</code></td> -<td><code>false</code></td> -<td>Is connection to CouchDB instance secure?</td> -</tr> -</tbody> -</table> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -If <code>CHT_PIPELINE_BRANCH_URL</code> is pointing to a private GitHub repository, you&rsquo;ll need an access token in the URL. Assuming your repository is <code>medic/cht-pipeline</code>, you would replace <code>&lt;PAT&gt;</code> with an access token: <code>https://&lt;PAT&gt;@github.com/medic/cht-pipeline.git#main</code>. Please see <a href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens">GitHub&rsquo;s instructions</a> on how to generate a token. -</div> \ No newline at end of file +Data Synchronization and Analytics on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/guides/data/analytics/Recent content in Data Synchronization and Analytics on Community Health ToolkitHugo -- gohugo.ioenIntroduction & Prerequisites to data synchronization and analyticshttps://docs.communityhealthtoolkit.org/apps/guides/data/analytics/introduction/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/analytics/introduction/The pages in this section apply to both CHT 3.x (beyond 3.12) and CHT 4.x. +CHT Sync schema differs from CHT Couch2pg. +Most CHT deployments require some sort of analytics so that stakeholders can make data driven decisions. CouchDB, which is the database used by the CHT, is not designed for analytics. It is a document database, which means that it is optimized for storing and retrieving documents, and not for aggregating data.Local CHT Sync Setuphttps://docs.communityhealthtoolkit.org/apps/guides/data/analytics/setup/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/analytics/setup/Before setting up CHT Sync in production, it&rsquo;s very handy to be able to run it locally. This will allow you to experiment with the data flow and easily query development data quickly and locally. +These instructions assume you&rsquo;re running CHT Sync, CHT Core and PostgreSQL either locally on your workstation or on a local server. They are not meant to be used to deploy a secure, always on production instance.Production CHT Sync Setuphttps://docs.communityhealthtoolkit.org/apps/guides/data/analytics/production/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/analytics/production/This is under active development. The page will be updated accordingly once it&rsquo;s validated that the current approach is suited for being used in production. For the time being, you can try out and test CHT Sync locally.Environment Variableshttps://docs.communityhealthtoolkit.org/apps/guides/data/analytics/environment-variables/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/analytics/environment-variables/There are three environment variable groups in the .env file. To successfully set up CHT Sync, it is important to understand the difference between them. +POSTGRES_: Used by PostgreSQL to establish the PostgreSQL database to synchronize CouchDB data to. They also define the schema and table names to store the CouchDB data. The main objective is to define the environment where the raw CouchDB data will be copied. DBT_: Exclusive to the DBT configuration. \ No newline at end of file diff --git a/apps/guides/data/analytics/introduction/index.html b/apps/guides/data/analytics/introduction/index.html index 2b59270110..05f364ca12 100644 --- a/apps/guides/data/analytics/introduction/index.html +++ b/apps/guides/data/analytics/introduction/index.html @@ -1,9 +1,9 @@ -Introduction & Prerequisites to data synchronization and analytics | Community Health Toolkit +Introduction & Prerequisites to data synchronization and analytics | Community Health Toolkit

Introduction & Prerequisites to data synchronization and analytics

High level approach to data synchronization and analytics with CHT applications

The pages in this section apply to both CHT 3.x (beyond 3.12) and CHT 4.x.

CHT Sync schema differs from CHT Couch2pg.

Most CHT deployments require some sort of analytics so that stakeholders can make data driven decisions. CouchDB, which is the database used by the CHT, is not designed for analytics. It is a document database, which means that it is optimized for storing and retrieving documents, and not for aggregating data. For example, if you wanted to know how many patients were registered in a particular area, you would have to query the database for all the patients in that area, and then count them. This is not a very efficient process. It is much more efficient to store the number of patients in a particular area in a separate database, and update that number whenever a patient is registered or unregistered. This is what CHT Sync paired with CHT Pipeline is designed to do.

CHT Sync Introduction

Medic maintains CHT Sync which is an integrated solution designed to enable data synchronization between CouchDB and PostgreSQL for the purpose of analytics. It can easily be deployed using Docker. It is supported on CHT 3.12 and later, including CHT 4.x. By using CHT Sync, a CHT deployment can easily get analytics by using a data visualization tool. All tools are open-source and have no licensing fees.

CHT Sync has been designed to work in both local development environments for testing models or workflows, and in production environments. The setup can accommodate the needs of different environments.

CHT Sync Prerequisites


CHT Core Framework > + Create project issue

Introduction & Prerequisites to data synchronization and analytics

High level approach to data synchronization and analytics with CHT applications

The pages in this section apply to both CHT 3.x (beyond 3.12) and CHT 4.x.

CHT Sync schema differs from CHT Couch2pg.

Most CHT deployments require some sort of analytics so that stakeholders can make data driven decisions. CouchDB, which is the database used by the CHT, is not designed for analytics. It is a document database, which means that it is optimized for storing and retrieving documents, and not for aggregating data. For example, if you wanted to know how many patients were registered in a particular area, you would have to query the database for all the patients in that area, and then count them. This is not a very efficient process. It is much more efficient to store the number of patients in a particular area in a separate database, and update that number whenever a patient is registered or unregistered. This is what CHT Sync paired with CHT Pipeline is designed to do.

CHT Sync Introduction

Medic maintains CHT Sync which is an integrated solution designed to enable data synchronization between CouchDB and PostgreSQL for the purpose of analytics. It can easily be deployed using Docker. It is supported on CHT 3.12 and later, including CHT 4.x. By using CHT Sync, a CHT deployment can easily get analytics by using a data visualization tool. All tools are open-source and have no licensing fees.

CHT Sync has been designed to work in both local development environments for testing models or workflows, and in production environments. The setup can accommodate the needs of different environments.

CHT Sync Prerequisites


CHT Core Framework > Overview > CHT Sync

Data synchronization tools to enable analytics

CHT Core Framework > Overview > Data Flows

An overview of data flows in the CHT for analytics, impact monitoring, and data science

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/guides/data/analytics/production/index.html b/apps/guides/data/analytics/production/index.html index 30e45abeb6..a68129942e 100644 --- a/apps/guides/data/analytics/production/index.html +++ b/apps/guides/data/analytics/production/index.html @@ -1,9 +1,9 @@ -Production CHT Sync Setup | Community Health Toolkit +Production CHT Sync Setup | Community Health Toolkit

Production CHT Sync Setup

Setting up a production deployment of CHT Sync with the CHT

This is under active development. The page will be updated accordingly once it’s validated that the current approach is suited for being used in production. For the time being, you can try out and test CHT Sync locally.


CHT Core Framework > + Create project issue

Production CHT Sync Setup

Setting up a production deployment of CHT Sync with the CHT

This is under active development. The page will be updated accordingly once it’s validated that the current approach is suited for being used in production. For the time being, you can try out and test CHT Sync locally.


CHT Core Framework > Overview > CHT Core

The different pieces of a CHT project, how they interact, and what they’re used for

CHT Core Framework > Overview > CHT Sync

Data synchronization tools to enable analytics

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/guides/data/analytics/setup/index.html b/apps/guides/data/analytics/setup/index.html index 57f10f87aa..c207c26910 100644 --- a/apps/guides/data/analytics/setup/index.html +++ b/apps/guides/data/analytics/setup/index.html @@ -1,9 +1,9 @@ -Local CHT Sync Setup | Community Health Toolkit +Local CHT Sync Setup | Community Health Toolkit

Local CHT Sync Setup

Setting up a local deployment of CHT Sync with the CHT

Before setting up CHT Sync in production, it’s very handy to be able to run it locally. This will allow you to experiment with the data flow and easily query development data quickly and locally.

These instructions assume you’re running CHT Sync, CHT Core and PostgreSQL either locally on your workstation or on a local server. They are not meant to be used to deploy a secure, always on production instance.

Setup

Copy the values in env.template file to the .env file. For more information, see the references on the Environment variables page.

Run all CHT Sync services locally

This setup involves starting couch2pg, PostgreSQL, pgAdmin, DBT, and CouchDB.

Run the Docker containers and wait for every container to be up and running:

docker-compose -f docker-compose.couchdb.yml -f docker-compose.postgres.yml -f docker-compose.yml up -d
+ Create project issue

Local CHT Sync Setup

Setting up a local deployment of CHT Sync with the CHT

Before setting up CHT Sync in production, it’s very handy to be able to run it locally. This will allow you to experiment with the data flow and easily query development data quickly and locally.

These instructions assume you’re running CHT Sync, CHT Core and PostgreSQL either locally on your workstation or on a local server. They are not meant to be used to deploy a secure, always on production instance.

Setup

Copy the values in env.template file to the .env file. For more information, see the references on the Environment variables page.

Run all CHT Sync services locally

This setup involves starting couch2pg, PostgreSQL, pgAdmin, DBT, and CouchDB.

Run the Docker containers and wait for every container to be up and running:

docker-compose -f docker-compose.couchdb.yml -f docker-compose.postgres.yml -f docker-compose.yml up -d
 

You can verify this command worked by running docker ps. It should show 5 containers running including couch2pg, DBT, PostgreSQL, CouchDB and pgAdmin.

Now that all services are running, use pgAdmin to connect to server postgres:5432 with user postgres and password postgres. You should be able to see data being inserted into the v1.medic table when inserting sample data into the CouchDB instance.

Separate CouchDB instance

This setup involves starting couch2pg, PostgreSQL, pgAdmin and DBT. It assumes you have a CouchDB instance running, and you updated the .env CouchDB variables accordingly.

Run the Docker containers locally and wait for every container to be up and running:

docker-compose -f docker-compose.postgres.yml -f docker-compose.yml up -d
 

You can verify this command worked by running docker ps. It should show 4 containers running including couch2pg, DBT, PostgreSQL, and pgAdmin.

Separate CouchDB and PostgreSQL instances

This local setup involves starting couch2pg and DBT. It assumes that CouchDB and PostgreSQL instances are run separately from the Docker Compose provided with CHT Sync, and the .env variables were updated to match those instances details.

Run the Docker containers locally and wait for every container to be up and running:

docker-compose -f docker-compose.yml up -d
 

You can verify this command worked by running docker ps. It should show 2 containers running: couch2pg and DBT.

Cleanup

When you are done using the services, you can clean everything by running down.

For example, in the scenario of running all CHT Sync services, the command should look like:

docker compose -f docker-compose.couchdb.yml -f docker-compose.postgres.yml -f docker-compose.yml down
@@ -309,7 +309,8 @@
 CHT Core

The different pieces of a CHT project, how they interact, and what they’re used for

CHT Core Framework > Overview > CHT Sync

Data synchronization tools to enable analytics

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/guides/data/csv-to-docs/index.html b/apps/guides/data/csv-to-docs/index.html index 609740c1aa..1191d4f09b 100644 --- a/apps/guides/data/csv-to-docs/index.html +++ b/apps/guides/data/csv-to-docs/index.html @@ -1,9 +1,9 @@ -CSV to Docs | Community Health Toolkit +CSV to Docs | Community Health Toolkit

CSV to Docs

Seeding data with cht-conf

Seeding data with cht-conf

Users, contacts, and report data can be specified in comma-separated value (CSV) files, then converted to JavaScript Object Notation (JSON) files and uploaded into your instance using cht-conf. This documentation will cover the CSV notation used, fetching CSV files from Google Sheets, converting the CSV files into JSON docs, and then uploading the data from the JSON files to your instance.

Converting CSVs

Running cht-conf with the csv-to-docs action converts CSV files from the csv folder into JSON docs to be uploaded to your instance. The JSON files are stored in the json_docs folder. Instructions for creating the CSV files are in sections below.

Uploading CSVs

Running cht-conf with the upload-docs action will upload the JSON docs that were generated from the CSV files to your instance. For example, running cht --local upload-docs will upload the converted docs into your local instance. The target location --local can be replaced with an instance or URL. See cht-conf for detailed instructions.

Creating CSV files for Contacts, Reports

A separate CSV file is needed for each type of place, person, or report in your project’s local csv folder. The name of the file determines the type of doc created for rows contained in the file. The possible types are: report, person, and place. Each of these has a further specifier provided in the filename:

  • place.{place_type}.csv: where {place_type} is the type of place specified in the file. By default, the place types are one of clinic, health_center, or district_hospital. As of 3.7 of the Core Framework, the number of place types and their names can be configured — the {place_type} should match with the hierarchy names used.
  • person.{parent_place_type}.csv: where {parent_place_type} is the type of place to which the people in the file will belong.
  • report.{form_id}.csv: where {form_id} is the form ID for all the reports in the file. You will need one file per form ID

Here are some examples:

  • File named place.district_hospital.csv adds the property "type":"district_hospital"
  • File named person.clinic.csv add the property "type":"person"
  • File named report.immunization_visit.csv add the property "type":"report", "form":"immunization_visit"

In each of these files a header row is used to specify the JSON field names, and each subsequent row specifies the corresponding values for a doc. A _id field is automatically generated with a universally unique identifier.

Here is an example of a csv/person.clinic.csv file for people belonging to clinics:

name,sex,date_of_birth
+ Create project issue

CSV to Docs

Seeding data with cht-conf

Seeding data with cht-conf

Users, contacts, and report data can be specified in comma-separated value (CSV) files, then converted to JavaScript Object Notation (JSON) files and uploaded into your instance using cht-conf. This documentation will cover the CSV notation used, fetching CSV files from Google Sheets, converting the CSV files into JSON docs, and then uploading the data from the JSON files to your instance.

Converting CSVs

Running cht-conf with the csv-to-docs action converts CSV files from the csv folder into JSON docs to be uploaded to your instance. The JSON files are stored in the json_docs folder. Instructions for creating the CSV files are in sections below.

Uploading CSVs

Running cht-conf with the upload-docs action will upload the JSON docs that were generated from the CSV files to your instance. For example, running cht --local upload-docs will upload the converted docs into your local instance. The target location --local can be replaced with an instance or URL. See cht-conf for detailed instructions.

Creating CSV files for Contacts, Reports

A separate CSV file is needed for each type of place, person, or report in your project’s local csv folder. The name of the file determines the type of doc created for rows contained in the file. The possible types are: report, person, and place. Each of these has a further specifier provided in the filename:

  • place.{place_type}.csv: where {place_type} is the type of place specified in the file. By default, the place types are one of clinic, health_center, or district_hospital. As of 3.7 of the Core Framework, the number of place types and their names can be configured — the {place_type} should match with the hierarchy names used.
  • person.{parent_place_type}.csv: where {parent_place_type} is the type of place to which the people in the file will belong.
  • report.{form_id}.csv: where {form_id} is the form ID for all the reports in the file. You will need one file per form ID

Here are some examples:

  • File named place.district_hospital.csv adds the property "type":"district_hospital"
  • File named person.clinic.csv add the property "type":"person"
  • File named report.immunization_visit.csv add the property "type":"report", "form":"immunization_visit"

In each of these files a header row is used to specify the JSON field names, and each subsequent row specifies the corresponding values for a doc. A _id field is automatically generated with a universally unique identifier.

Here is an example of a csv/person.clinic.csv file for people belonging to clinics:

name,sex,date_of_birth
 Adriana Akiyama,female,1985-12-31
 Becky Backlund,female,1987-10-17
 Carson Crane,male,2015-01-23
@@ -457,7 +457,8 @@
 Reference >
 forms/ >
 contact

Contact Forms: Used for creating and editing people and places

-

\ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/guides/data/hydration/index.html b/apps/guides/data/hydration/index.html index 4a3610b861..b95db96cab 100644 --- a/apps/guides/data/hydration/index.html +++ b/apps/guides/data/hydration/index.html @@ -1,9 +1,9 @@ -Database document hydration | Community Health Toolkit +Database document hydration | Community Health Toolkit

Database document hydration

Overview of database document hydration

Documents are connected with each other via their document _id. + Create project issue

Database document hydration

Overview of database document hydration

Documents are connected with each other via their document _id. For example:

  • a contact document is connected to its parent by storing their _id in the parent property

  • a report document is connected to its submitter by storing their _id in the contact property

    See Also: DB Schema

To optimize database storage, documents are “minified” when stored and are “hydrated” when they are used by the app.

Minification

Minification means replacing a linked document’s content with an object that only contains its uuid. This is done to reduce duplication of data to save storage space on the client and server. Additionally, as we only have one copy of the data is easier to keep the information up to date.

Unminified object:

{
   "_id": "clinic_uuid",
   "name": "Clinic",
@@ -423,7 +423,8 @@
 

There are two types of hydration:

  • shallow hydration - when the id is replaced with the document’s content
  • deep hydration - when the id is replaced with the document’s content and, recursively, each ancestor is deeply hydrated
A hydrated contact has
  • deeply hydrated parent along with every ancestor
  • shallowly hydrated primary contact
  • shallowly hydrated linked_docs (as of 3.10)
A hydrated report has
  • a deeply hydrated submitter contact
  • a deeply hydrated patient, if the report has a patient_id/patient_uuid
  • a deeply hydrated place, if the report has a place_id
-

\ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/guides/data/impact-metrics/index.html b/apps/guides/data/impact-metrics/index.html index e652ed5dd3..8243231317 100644 --- a/apps/guides/data/impact-metrics/index.html +++ b/apps/guides/data/impact-metrics/index.html @@ -1,9 +1,9 @@ -CHT Impact Metrics | Community Health Toolkit +CHT Impact Metrics | Community Health Toolkit

CHT Impact Metrics

A set of impact metrics for monitoring priority use cases across the Community Health Toolkit

Impact monitoring is an essential part of both the Community Health Toolkit and Medic’s processes and ethos. We are committed to harnessing data to:

  1. Support our partners in data-driven operational and strategic decision making
  2. Inform our product roadmap and organizational strategy, and
  3. Participate in overarching policy discussions around community health.

As a member of the CHT community and potential CHT implementer, we encourage you to learn more about the recommended impact metrics for monitoring and evaluation across priority use cases by reviewing the metrics listed below.

Impact Metrics

The following list of Impact Metrics is comprehensive; it includes all of the Impact Metric that Medic and one or more additional CHT implementers have decided to share with the general public across workflows:

Caring Activities

  • Total number of caring activities logged on the CHT (total, per month)

Engagement

  • Total number of frontline health workers supported by the CHT (total, per month)
  • Total number of frontline health workers trained to use the CHT for a specific use case (ie. antenatal care) (total, per month)

Households

  • Total number of households registered in the CHT (total, per month)
  • Total number of new households registered in the CHT per month
  • Total number of registered households with one or more visits by a health worker logged in the CHT per month

Antenatal Care

  • Total number of pregnancy registrations logged in the CHT (total, per month)
  • Total number of pregnancy registrations logged in the first trimester in the CHT (total, per month)
  • Total number of confirmed deliveries logged in the CHT (total, per month)
  • Total number of confirmed deliveries occurring in a health facility logged in the CHT (total, per month)

Postnatal Care

  • Total number of postnatal visits logged in the CHT (total, per month)
  • Total number of women receiving the first postnatal care visit within 48h of delivery logged in the CHT (total, per month)

iCCM

  • Total number of assessments conducted on children under five (U5) in the CHT (total, per month)
  • Total number of resulting diagnoses from U5 child assessments in the CHT (total, per month)
  • Total number of resulting referrals from U5 child assessments in the CHT (total, per month)
  • Total number of assessments conducted where the U5 child was presenting symptoms in the CHT (total, per month)
  • Total number of U5 child assessments conducted within 72h of symptom onset logged in the CHT (total, per month)
  • Total number of U5 child assessments conducted within 24h of symptom onset logged in the CHT (total, per month)
  • Total number of patients receiving correct treatment at home from a healthcare worker, when treatment is recommended, logged in the CHT (total, per month)
  • Total number of patient referral follow-up visits with a health facility visit confirmed logged in the CHT (total, per month)

Family Planning

  • Total number of women counseled by healthcare workers on family planning logged in the CHT (total, per month)
  • Total number of women newly using family planning following a counseling session with a healthcare worker logged in the CHT (total, per month)

Malnutrition

  • Total number of malnutrition screenings completed for U5 children logged in the CHT (total, per month)
  • Total number of U5 child malnutrition cases identified via malnutrition screening logged in the CHT (total, per month)
  • Total number of U5 children with malnutrition receiving treatment and assessed to have recovered via a follow-up malnutrition screening logged in the CHT (total, per month)

Immunization

  • Total number of immunization screenings for U5 children logged in the CHT (total, per month)
  • Total number of U5 children receiving vaccinations logged in the CHT (total, per month)

COVID-19

  • Total number of community event-based surveillance signals for COVID-19 reported by health workers via the Software (daily, weekly, monthly, total)
  • Total number of COVID-19 cases registered for contact tracing in the Software (daily, weekly, monthly, total)
  • Total number of contacts of COVID-19 cases registered for contact tracing in the Software (daily, weekly, monthly, total)

CHT Core Framework > + Create project issue

CHT Impact Metrics

A set of impact metrics for monitoring priority use cases across the Community Health Toolkit

Impact monitoring is an essential part of both the Community Health Toolkit and Medic’s processes and ethos. We are committed to harnessing data to:

  1. Support our partners in data-driven operational and strategic decision making
  2. Inform our product roadmap and organizational strategy, and
  3. Participate in overarching policy discussions around community health.

As a member of the CHT community and potential CHT implementer, we encourage you to learn more about the recommended impact metrics for monitoring and evaluation across priority use cases by reviewing the metrics listed below.

Impact Metrics

The following list of Impact Metrics is comprehensive; it includes all of the Impact Metric that Medic and one or more additional CHT implementers have decided to share with the general public across workflows:

Caring Activities

  • Total number of caring activities logged on the CHT (total, per month)

Engagement

  • Total number of frontline health workers supported by the CHT (total, per month)
  • Total number of frontline health workers trained to use the CHT for a specific use case (ie. antenatal care) (total, per month)

Households

  • Total number of households registered in the CHT (total, per month)
  • Total number of new households registered in the CHT per month
  • Total number of registered households with one or more visits by a health worker logged in the CHT per month

Antenatal Care

  • Total number of pregnancy registrations logged in the CHT (total, per month)
  • Total number of pregnancy registrations logged in the first trimester in the CHT (total, per month)
  • Total number of confirmed deliveries logged in the CHT (total, per month)
  • Total number of confirmed deliveries occurring in a health facility logged in the CHT (total, per month)

Postnatal Care

  • Total number of postnatal visits logged in the CHT (total, per month)
  • Total number of women receiving the first postnatal care visit within 48h of delivery logged in the CHT (total, per month)

iCCM

  • Total number of assessments conducted on children under five (U5) in the CHT (total, per month)
  • Total number of resulting diagnoses from U5 child assessments in the CHT (total, per month)
  • Total number of resulting referrals from U5 child assessments in the CHT (total, per month)
  • Total number of assessments conducted where the U5 child was presenting symptoms in the CHT (total, per month)
  • Total number of U5 child assessments conducted within 72h of symptom onset logged in the CHT (total, per month)
  • Total number of U5 child assessments conducted within 24h of symptom onset logged in the CHT (total, per month)
  • Total number of patients receiving correct treatment at home from a healthcare worker, when treatment is recommended, logged in the CHT (total, per month)
  • Total number of patient referral follow-up visits with a health facility visit confirmed logged in the CHT (total, per month)

Family Planning

  • Total number of women counseled by healthcare workers on family planning logged in the CHT (total, per month)
  • Total number of women newly using family planning following a counseling session with a healthcare worker logged in the CHT (total, per month)

Malnutrition

  • Total number of malnutrition screenings completed for U5 children logged in the CHT (total, per month)
  • Total number of U5 child malnutrition cases identified via malnutrition screening logged in the CHT (total, per month)
  • Total number of U5 children with malnutrition receiving treatment and assessed to have recovered via a follow-up malnutrition screening logged in the CHT (total, per month)

Immunization

  • Total number of immunization screenings for U5 children logged in the CHT (total, per month)
  • Total number of U5 children receiving vaccinations logged in the CHT (total, per month)

COVID-19

  • Total number of community event-based surveillance signals for COVID-19 reported by health workers via the Software (daily, weekly, monthly, total)
  • Total number of COVID-19 cases registered for contact tracing in the Software (daily, weekly, monthly, total)
  • Total number of contacts of COVID-19 cases registered for contact tracing in the Software (daily, weekly, monthly, total)

CHT Core Framework > Overview > Data Flows

An overview of data flows in the CHT for analytics, impact monitoring, and data science

CHT Applications > Quick Guides > Performance > Telemetry

Performance data of certain user actions

-

\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/guides/data/index.html b/apps/guides/data/index.html index f224c7b94b..15df5b1b79 100644 --- a/apps/guides/data/index.html +++ b/apps/guides/data/index.html @@ -1,9 +1,9 @@ -Managing Data | Community Health Toolkit +Managing Data | Community Health Toolkit

Managing Data

Creating and managing data in CHT applications

Database document hydration

Overview of database document hydration

How to bulk load users

How to create users in bulk

CHT Impact Metrics

A set of impact metrics for monitoring priority use cases across the Community Health Toolkit

Detecting and fixing production data on training instances

How to monitor for production data on a training instance, and remediation techniques

Revalidate invalid reports

How to revalidate an invalid report

CSV to Docs

Seeding data with cht-conf

Data Synchronization and Analytics

Using CHT Sync and CHT Pipeline for data synchronization and analytics

-

Last modified 05.06.2020: Categorized guides (d26e182e)
\ No newline at end of file + Create project issue
\ No newline at end of file diff --git a/apps/guides/data/index.xml b/apps/guides/data/index.xml index 54a04dde5c..5b5c93903d 100644 --- a/apps/guides/data/index.xml +++ b/apps/guides/data/index.xml @@ -1,927 +1,12 @@ -Community Health Toolkit – Managing Datahttps://docs.communityhealthtoolkit.org/apps/guides/data/Recent content in Managing Data on Community Health ToolkitHugo -- gohugo.ioenApps: Database document hydrationhttps://docs.communityhealthtoolkit.org/apps/guides/data/hydration/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/hydration/ -<p>Documents are connected with each other via their document <code>_id</code>. -For example:</p> -<ul> -<li> -<p>a contact document is connected to its parent by storing their <code>_id</code> in the <code>parent</code> property</p> -</li> -<li> -<p>a report document is connected to its submitter by storing their <code>_id</code> in the <code>contact</code> property</p> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/core/overview/db-schema/">DB Schema</a></p> -</li> -</ul> -<p>To optimize database storage, documents are &ldquo;minified&rdquo; when stored and are &ldquo;hydrated&rdquo; when they are used by the app.</p> -<h3 id="minification">Minification</h3> -<p>Minification means replacing a linked document&rsquo;s content with an object that only contains its uuid. This is done to reduce duplication of data to save storage space on the client and server. Additionally, as we only have one copy of the data is easier to keep the information up to date.</p> -<p>Unminified object:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;clinic_uuid&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Clinic&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;clinic&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;health_center_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Health Center&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;health_center&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;district_hospital_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span> <span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;District&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;district_hospital&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;contact&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Primary contact&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;phone&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;555 111 222&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;linked_docs&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;tag1&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;sibling_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Sibling clinic&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;clinic&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;tag2&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;supervisor_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;The supervisor&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;person&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>when minified, becomes:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;clinic_uuid&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Clinic&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;clinic&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;health_center_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;district_hospital_id&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;contact&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact_id&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;linked_docs&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;tag1&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;sibling_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;tag2&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;supervisor_id&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>The following properties are minified:</p> -<ol> -<li><code>parent</code> and recursively, every ancestor</li> -<li><code>contact</code></li> -<li><code>patient</code> and <code>place</code> are removed from reports. The <code>place_id</code>, <code>place_uuid</code>, <code>patient_id</code>, or <code>patient_uuid</code> fields are available in the document.</li> -<li><code>linked_docs</code> when minifying a contact (<em>as of <code>3.10.0</code></em>)</li> -</ol> -<h3 id="hydration">Hydration</h3> -<p>Hydration represents the inverse process to minification, where a stored id representing a connected document is replaced with the corresponding document&rsquo;s content.</p> -<p>Minified doc:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;clinic_uuid&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Clinic&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;clinic&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;health_center_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;district_hospital_id&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;contact&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact_id&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;linked_docs&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;tag1&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;sibling_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;tag2&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;supervisor_id&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>when hydrated becomes:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;clinic_uuid&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Clinic&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;clinic&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;health_center_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Health Center&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;health_center&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;contact&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;supervisor_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Supervisor&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;person&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;parent_id&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;district_hospital_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span> <span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;District&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;district_hospital&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;contact&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;manager_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Manager&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;person&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;parent_id&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;contact&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Primary contact&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;phone&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;555 111 222&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;parent_id&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;linked_docs&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;tag1&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;sibling_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Sibling clinic&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;clinic&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;tag2&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;supervisor_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;The supervisor&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;person&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>There are two types of hydration:</p> -<ul> -<li><em>shallow</em> hydration - when the id is replaced with the document&rsquo;s content</li> -<li><em>deep</em> hydration - when the id is replaced with the document&rsquo;s content and, recursively, each ancestor is deeply hydrated</li> -</ul> -<h6 id="a-hydrated-contact-has">A hydrated contact has</h6> -<ul> -<li>deeply hydrated <code>parent</code> along with every ancestor</li> -<li>shallowly hydrated primary <code>contact</code></li> -<li>shallowly hydrated <code>linked_docs</code> (<em>as of 3.10</em>)</li> -</ul> -<h6 id="a-hydrated-report-has">A hydrated report has</h6> -<ul> -<li>a deeply hydrated submitter <code>contact</code></li> -<li>a deeply hydrated <code>patient</code>, if the report has a <code>patient_id</code>/<code>patient_uuid</code></li> -<li>a deeply hydrated <code>place</code>, if the report has a <code>place_id</code></li> -</ul>Apps: How to bulk load usershttps://docs.communityhealthtoolkit.org/apps/guides/data/users-bulk-load/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/users-bulk-load/ -<div class="pageinfo pageinfo-primary"> -<p>The bulk user upload feature is available in 3.16.0 and later versions of the CHT. As of CHT 3.17.0, when creating both a contact and a place, the contact will be set as the default contact of the place. User creation can be scripted using the <a href="https://docs.communityhealthtoolkit.org/apps/reference/api/#post-apiv2users">CHT API</a> directly or using the <a href="https://github.com/medic/cht-conf"><code>cht-conf</code> tool</a>, which is detailed in the <a href="https://docs.communityhealthtoolkit.org/apps/guides/data/csv-to-docs/">CSV-to-Docs guide</a>.</p> -<p>This feature can be used to load as many users as possible but works optimally with chunks of 1,000 users or less.</p> -</div> -<p>Steps to bulk load users:</p> -<ol> -<li>Using Google Sheets, populate a spreadsheet with the users to be imported.</li> -<li>Import the new users using the Admin UI in the CHT.</li> -<li>Handle any errors that may occur during importation.</li> -<li>When done, you will have created new users, new contacts and new places, all of which are correctly associated in CouchDB with the correct UUIDs.</li> -</ol> -<h2 id="workbook-instructions">Workbook Instructions</h2> -<p>The workbook contains a varying number of spreadsheets depending on the hierarchy in question. Users with different roles are placed in different spreadsheets as shown in the templates below. For example when creating users with chw and chw_supervisor roles, you will have &ldquo;contact.chw&rdquo;, &ldquo;contact.chw_VLOOKUP&rdquo;, &ldquo;contact.chw_supervisor&rdquo; and &ldquo;contact.chw_supervisor_VLOOKUP&rdquo; spreadsheets. These pair per role spreadsheets contain actual data on users and parent place data respectively.</p> -<p>There is another spreadsheet, &ldquo;place.type_VLOOKUP&rdquo;, which is required when creating user accounts, contacts and their places. This spreadsheet defines the name and type of places in your hierarchy and should match those in the app_settings.json file. Note that you will need to create the parent place before importing the users.</p> -<p>To get started, there are three different workbook templates available that are compatible with the <code>default</code> configuration of the CHT, they cater for use cases that you might encounter when creating users in bulk. You will notice some columns have an <code>:excluded</code> suffix. These are columns that are ignored by the API and allow addition of autocomplete and data validation within the spreadsheet to make it easier to work with.</p> -<p>Click on any of the use cases below to make a copy of the spreadsheet for the use case in question:</p> -<ul> -<li><a href="https://docs.google.com/spreadsheets/d/1zlvF5cWnV2n1rax1bAO2hSBCIxgD0c-5tZ-yh96kwws/copy">when you want to create user accounts only</a></li> -<li><a href="https://docs.google.com/spreadsheets/d/1y6wYqRIWiC2QZA7NaWfolP_Wf9FnSahjYHlL3iDYeJ4/copy">when you want to create user accounts and their contacts</a></li> -<li><a href="https://docs.google.com/spreadsheets/d/1yUenFP-5deQ0I9c-OYDTpbKYrkl3juv9djXoLLPoQ7Y/copy">when you want to create user accounts, their contacts and their places</a></li> -</ul> -<p>We will use the second use case to create user accounts and their contacts in the example below.</p> -<h2 id="contact-spreadsheet-instructions">Contact Spreadsheet Instructions</h2> -<p>The spreadsheet interfaces with the <a href="https://docs.communityhealthtoolkit.org/apps/reference/api/#get-apiv1users"><code>POST /api/v1/users</code> API</a> which works as though passing a JSON array of users. Rows in the spreadsheet represent a user while columns represent properties of the user. -Each column in the spreadsheet maps to an object property understood by the API to insert the users into the database. These properties can be found in <a href="https://docs.communityhealthtoolkit.org/apps/reference/api/#post-apiv1users">the Users API documentation</a>.</p> -<p>Contact spreadsheets are named according to the user role, for example when creating users who are chws and others who are chw_supervisors, the following contact spreadsheets are populated respectively: &ldquo;contact.chw&rdquo; and &ldquo;contact.chw_supervisor&rdquo;.</p> -<p>There are three sections to the contact spreadsheet:</p> -<p><img src="users-spreadsheet.png" alt="bulk user import spreadsheet with areas labeled"></p> -<h4 id="spreadsheet-area-1"><strong>Spreadsheet Area 1</strong></h4> -<p>These three columns are where you paste the results after running an import. See step 8 in &ldquo;Importing users example&rdquo; <a href="#importing-users-example">below</a>.</p> -<ol> -<li><code>import.status:excluded</code>: This field can have three values. Over time, they should all be <code>imported</code> or <code>skipped</code> as you will have processed all users on the list: -<ul> -<li><code>imported</code>- This user has already been successfully imported</li> -<li><code>skipped</code> - This user was skipped</li> -<li><code>error</code> - Contains errors that were encountered during importation. See <code>import.message:excluded</code> field for more information</li> -</ul> -</li> -<li><code>import.message:excluded</code>: The status of the last import. For example, <code>Imported successfully</code> or <code>Username 'mrjones' already taken</code></li> -<li><code>import.username:excluded</code>: Use this column to ensure you&rsquo;re matching the response with the correct user in the <code>contact.username</code> to the right</li> -</ol> -<h4 id="spreadsheet-area-2"><strong>Spreadsheet Area 2</strong></h4> -<p>This is where you enter your user data and contains the following columns:</p> -<ol> -<li><code>username</code>: username used to log into the application</li> -<li><code>Parent-Place:excluded</code>: existing parent place of the new user place. A drop-down populated from contact vlookup spreadsheet</li> -<li><code>User-Place-Type:excluded</code>: type of user place to be created. A drop-down populated from place vlookup spreadsheet</li> -<li><code>contact.first_name</code>: first name of the new user</li> -<li><code>contact.last_name</code>: last name of the new user</li> -<li><code>contact.sex</code>: sex of the new user</li> -<li><code>contact.phone</code>: phone number of the new user</li> -<li><code>email</code>: email of the new user (optional field)</li> -<li><code>contact.meta.created_by</code>: this column contains metadata on the person who created the user (optional field)</li> -<li><code>token_login</code>: whether the user should be sent login credentials via SMS as a link</li> -<li><code>contact.type</code> or <code>place.type</code>: when creating a user, is logged as contact. In the case of the <a href="https://github.com/medic/cht-core/tree/master/config/default/">default config</a>, possible values are district_hospital, health_center, clinic, and person</li> -<li><code>contact.contact_type</code> or <code>place.contact_type</code>: defines the type of contact being created depending on the deployment&rsquo;s configuration. It should match one of the contact types defined in the config&rsquo;s contact_types field</li> -<li><code>type</code>: role of the contact</li> -</ol> -<h4 id="spreadsheet-area-3"><strong>Spreadsheet Area 3</strong></h4> -<p>Columns in this area are automatically populated by the spreadsheet logic. -While this is needed to create a user, it is intentionally not editable and you will see this error when you try to edit data:</p> -<p><img src="users-spreadsheet-warning.png" alt="bulk user import spreadsheet warning"></p> -<p>However, for the mapping to occur as expected, you may edit the fields <code>contact.role</code>, <code>contact.contact_type</code>, <code>place.contact_type</code> and <code>type</code> to match your deployment&rsquo;s configuration.</p> -<p>Do not edit column headers in row 1. They are needed by the CHT to identify which data is in it. Changing the names will result in errors or missing data in the CHT.</p> -<h3 id="passwords">Passwords</h3> -<p>Passwords are automatically generated by the spreadsheet. Use caution when editing rows marked as <code>imported</code> in the Import.status:excluded column. -For example, if a user was imported two weeks ago and the token_login is set to <code>TRUE</code> and then back to <code>FALSE</code>, -the password will be regenerated and thus be different from the one the user is using to login. -If a change is made, you can use Google Sheets history (&ldquo;File&rdquo; -&gt; &ldquo;Version History&rdquo;) to retrieve the old value.</p> -<h2 id="importing-users-example">Importing users example</h2> -<ol> -<li> -<p>Create a copy of <a href="https://docs.google.com/spreadsheets/d/1yUenFP-5deQ0I9c-OYDTpbKYrkl3juv9djXoLLPoQ7Y/copy">this spreadsheet</a>. -Give it a descriptive name and note its location in Google Drive. You will always come back to your copy of this Sheet whenever you want to add a set of users.</p> -</li> -<li> -<p>Copy the &ldquo;contact.chw&rdquo; and &ldquo;contact.chw_VLOOKUP&rdquo; spreadsheets so that you have a set of the pair per level of your hierarchy. -If your hierarchy was &ldquo;Central -&gt; Supervisor -&gt; CHW&rdquo;, you would have 3 pairs (6 spreadsheets total). Be sure each spreadsheet is named accurately.</p> -</li> -<li> -<p>Open the spreadsheet and populate your list of parent places that you&rsquo;d like to use for your users. -In this example the &ldquo;Penda Ouedraogo&rdquo; place has gotten an updated UUID starting with &ldquo;bcc&rdquo; -<img src="importing-users-populate-parents-places.png" alt="populate your list of parent places"></p> -</li> -<li> -<p>Add the users you would like to create -<img src="importing-users-entering-data.png" alt="entering data into the spreadsheet"></p> -</li> -<li> -<p>Export spreadsheet into CSV -<img src="importing-users-export-csv.png" alt="export spreadsheet into CSV"></p> -</li> -<li> -<p>Access the <a href="https://docs.communityhealthtoolkit.org/apps/features/admin/">Admin Console</a> of your instance, go to &ldquo;Users&rdquo;, click &ldquo;Import from file&rdquo; and select your CSV file you just exported -<img src="importing-users-import-csv.png" alt="import CSV into CHT"></p> -</li> -<li> -<p>Be patient during import (testing showed ~0.4 seconds per record up to 500 records) -<img src="importing-users-import-progress.png" alt="progress feedback during CSV import"></p> -</li> -<li> -<p>Download the status file -<img src="importing-users-download-status-file.png" alt="download the status file"></p> -</li> -<li> -<p>Transfer errors back into spreadsheet. Make sure you copy all three columns A, B and C. -The usernames in column C must match those in column D of the original spreadsheet. -<img src="importing-users-transfer-errors.png" alt="transfers errors back into spreadsheet"></p> -</li> -<li> -<p>Fix the errors and export to CSV -<img src="importing-users-fix-errors.png" alt="fix errors and export to CSV"> -<br /> -<br /> -<img src="importing-users-export-csv.png" alt="export spreadsheet into CSV"></p> -</li> -<li> -<p>Import the fixed CSV, noting already import rows are skipped -<img src="importing-users-import-fixed-csv.png" alt="import the fixed CSV"></p> -</li> -<li> -<p>Deliver credentials to phone or to CHW, using care to not overshare the login and password</p> -</li> -</ol> -<h2 id="adding-new-places">Adding new places</h2> -<p>Over time, new places will be added to different levels of the hierarchy. -You will need to manually add these new places to the spreadsheet so that you can add users to the new places.</p> -<ol> -<li> -<p>Navigate in the CHT to the new site. In this case, it is &ldquo;Site Dieco&rdquo; that has been added (item 1). Copy the 36 character UUID from the URL (item 2). -<img src="adding-places-cht-new-site.png" alt="navigate in the CHT to the new site"></p> -</li> -<li> -<p>Open your existing Google Spreadsheet with your users. Find the hierarchy level you added your new site. -In this case &ldquo;Site Dieco&rdquo; is a CHW place, so we&rsquo;ll go to the &ldquo;contact.c62_chw_VLOOKUP&rdquo; spreadsheet. -Add some new rows at the bottom (item 1), enter the new place name in column A (item 2) and paste the UUID in column B (item 3) -<img src="adding-places-existing-spreadsheet.png" alt="open your existing spreadsheet"></p> -</li> -</ol> -<h2 id="trouble-shooting">Trouble shooting</h2> -<h3 id="wrong-type-this-is-not-a-person">&ldquo;Wrong type, this is not a person.&rdquo;</h3> -<p>If you have miss-matched contact types, you will get an error upon import:</p> -<blockquote> -<p><strong>Wrong type, this is not a person.</strong></p> -</blockquote> -<p>As of CHT 3.7.0, you&rsquo;re <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/hierarchy/#app_settingsjson-contact_types">allowed to declare different contact types</a> in your <code>app_settings.json</code>. If you have populated the <code>.contact_types[]</code> property in your JSON, you will need to update the automatic value of the <code>contact.contact_type</code> column. The default value is:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#204a87;font-weight:bold">if</span><span style="color:#ce5c00;font-weight:bold">(</span>NOT<span style="color:#ce5c00;font-weight:bold">(</span>ISBLANK<span style="color:#ce5c00;font-weight:bold">(</span>D2<span style="color:#ce5c00;font-weight:bold">))</span>,<span style="color:#4e9a06">&#34;person&#34;</span>,<span style="color:#4e9a06">&#34;&#34;</span><span style="color:#ce5c00;font-weight:bold">)</span> -</span></span></code></pre></div><p>Often times numbers are used in <code>app_settings.json</code> to declare <code>contact_types</code> matching numerical names from <code>place_hierarchy_types</code>. It might look like this:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;contact_types&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;c52_supervisor&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.c52_supervisor&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;group_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.c52_supervisor.plural&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;create_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.c52_supervisor.new&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;edit_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.c52_supervisor.edit&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;icon&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;icon-manager&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;create_form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;form:contact:c52_supervisor:create&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;edit_form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;form:contact:c52_supervisor:edit&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;person&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;c62_chw&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.c62_chw&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;group_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.c62_chw.plural&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;create_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.c62_chw.new&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;edit_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.c62_chw.edit&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parents&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;c60_chw_site&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;icon&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;icon-chw&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;create_form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;form:contact:c62_chw:create&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;edit_form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;form:contact:c62_chw:edit&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;person&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span></code></pre></div><p>In this case, you would need to change the value of the <code>contact.contact_type</code> column to match your new types. Based on the <code>contact.types</code> above, a CHW would be declared like this now:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#204a87;font-weight:bold">if</span><span style="color:#ce5c00;font-weight:bold">(</span>NOT<span style="color:#ce5c00;font-weight:bold">(</span>ISBLANK<span style="color:#ce5c00;font-weight:bold">(</span>D2<span style="color:#ce5c00;font-weight:bold">))</span>,<span style="color:#4e9a06">&#34;c62_chw&#34;</span>,<span style="color:#4e9a06">&#34;&#34;</span><span style="color:#ce5c00;font-weight:bold">)</span> -</span></span></code></pre></div><p>Be sure you copy this updated formula for all rows in the <code>contact.contact_type</code> column.</p> -<h3 id="access-denied">Access denied</h3> -<p>If a user you successfully created can not log in because they see an error:</p> -<p><img src="access.denied.png" alt="Access denied - You have insufficient privileges to view this page. Talk to an administrator to increase your privileges"></p> -<p>A possible cause of this is you have a bad role defined in your spreadsheet that doesn&rsquo;t match your configuration. For example, the the default role should be <code>chw_supervisor</code>, but here we see garbage characters were accidentally added. While the user was created without errors, they&rsquo;ll see the above error when they try to log in:</p> -<p><img src="bad.type.png" alt="Bad type declared in spreadsheet causes the user to have no role"></p> -<p>You may manually fix any users you imported to have the correct role. To ensure future users have valid roles to log in with, check the <code>/admin/#/authorization/roles</code> area on your CHT instance for valid roles:</p> -<p><img src="valid.roles.png" alt="CHT admin showing a list of valid roles in the &ldquo;Role&rdquo; column on the left"></p>Apps: CHT Impact Metricshttps://docs.communityhealthtoolkit.org/apps/guides/data/impact-metrics/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/impact-metrics/ -<p>Impact monitoring is an essential part of both the Community Health Toolkit and Medic&rsquo;s processes and ethos. We are committed to harnessing data to:</p> -<ol> -<li>Support our partners in data-driven operational and strategic decision making</li> -<li>Inform our product roadmap and organizational strategy, and</li> -<li>Participate in overarching policy discussions around community health.</li> -</ol> -<p>As a member of the CHT community and potential CHT implementer, we encourage you to learn more about the recommended impact metrics for monitoring and evaluation across priority use cases by reviewing the metrics listed below.</p> -<h2 id="impact-metrics">Impact Metrics</h2> -<p>The following list of Impact Metrics is comprehensive; it includes all of the Impact Metric that Medic and one or more additional CHT implementers have decided to share with the general public across workflows:</p> -<p><em>Caring Activities</em></p> -<ul> -<li>Total number of caring activities logged on the CHT (total, per month)</li> -</ul> -<p><em>Engagement</em></p> -<ul> -<li>Total number of frontline health workers supported by the CHT (total, per month)</li> -<li>Total number of frontline health workers trained to use the CHT for a specific use case (ie. antenatal care) (total, per month)</li> -</ul> -<p><em>Households</em></p> -<ul> -<li>Total number of households registered in the CHT (total, per month)</li> -<li>Total number of new households registered in the CHT per month</li> -<li>Total number of registered households with one or more visits by a health worker logged in the CHT per month</li> -</ul> -<p><em>Antenatal Care</em></p> -<ul> -<li>Total number of pregnancy registrations logged in the CHT (total, per month)</li> -<li>Total number of pregnancy registrations logged in the first trimester in the CHT (total, per month)</li> -<li>Total number of confirmed deliveries logged in the CHT (total, per month)</li> -<li>Total number of confirmed deliveries occurring in a health facility logged in the CHT (total, per month)</li> -</ul> -<p><em>Postnatal Care</em></p> -<ul> -<li>Total number of postnatal visits logged in the CHT (total, per month)</li> -<li>Total number of women receiving the first postnatal care visit within 48h of delivery logged in the CHT (total, per month)</li> -</ul> -<p><em>iCCM</em></p> -<ul> -<li>Total number of assessments conducted on children under five (U5) in the CHT (total, per month)</li> -<li>Total number of resulting diagnoses from U5 child assessments in the CHT (total, per month)</li> -<li>Total number of resulting referrals from U5 child assessments in the CHT (total, per month)</li> -<li>Total number of assessments conducted where the U5 child was presenting symptoms in the CHT (total, per month)</li> -<li>Total number of U5 child assessments conducted within 72h of symptom onset logged in the CHT (total, per month)</li> -<li>Total number of U5 child assessments conducted within 24h of symptom onset logged in the CHT (total, per month)</li> -<li>Total number of patients receiving correct treatment at home from a healthcare worker, when treatment is recommended, logged in the CHT (total, per month)</li> -<li>Total number of patient referral follow-up visits with a health facility visit confirmed logged in the CHT (total, per month)</li> -</ul> -<p><em>Family Planning</em></p> -<ul> -<li>Total number of women counseled by healthcare workers on family planning logged in the CHT (total, per month)</li> -<li>Total number of women newly using family planning following a counseling session with a healthcare worker logged in the CHT (total, per month)</li> -</ul> -<p><em>Malnutrition</em></p> -<ul> -<li>Total number of malnutrition screenings completed for U5 children logged in the CHT (total, per month)</li> -<li>Total number of U5 child malnutrition cases identified via malnutrition screening logged in the CHT (total, per month)</li> -<li>Total number of U5 children with malnutrition receiving treatment and assessed to have recovered via a follow-up malnutrition screening logged in the CHT (total, per month)</li> -</ul> -<p><em>Immunization</em></p> -<ul> -<li>Total number of immunization screenings for U5 children logged in the CHT (total, per month)</li> -<li>Total number of U5 children receiving vaccinations logged in the CHT (total, per month)</li> -</ul> -<p><em>COVID-19</em></p> -<ul> -<li>Total number of community event-based surveillance signals for COVID-19 reported by health workers via the Software (daily, weekly, monthly, total)</li> -<li>Total number of COVID-19 cases registered for contact tracing in the Software (daily, weekly, monthly, total)</li> -<li>Total number of contacts of COVID-19 cases registered for contact tracing in the Software (daily, weekly, monthly, total)</li> -</ul>Apps: Detecting and fixing production data on training instanceshttps://docs.communityhealthtoolkit.org/apps/guides/data/training-instance/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/training-instance/ -<p>After <a href="https://docs.communityhealthtoolkit.org/apps/guides/training/onboarding/">onboarding CHWs</a>, sometimes data ends up on the wrong CHT instance. There are some passive and active actions you can take to help deal with this situation.</p> -<h2 id="monitoring">Monitoring</h2> -<p>Monitoring is a good way to see if CHWs are sending forms to the wrong CHT server. By catching such a problem early, it may be easy to fix manually which avoids more laborious fixes on the command line for admins.</p> -<h3 id="manual-device-checks">Manual device checks</h3> -<p>Supervisors can actively monitor CHWs as they register their first household. If each CHW is given a deadline to do this, the Supervisor can follow up promptly with CHWs who have not met the deadline.</p> -<h3 id="centralized-database-checks">Centralized database checks</h3> -<p>From an administrative perspective, passive monitoring can be done by querying the Postgres instance that <a href="https://github.com/medic/cht-couch2pg/">couch2pg</a> is populating. If <code>2020-09-14</code> was the date to stop use the training instance, this SQL query would show CHWs using the wrong instance after <code>2020-09-14</code>:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>with forms as <span style="color:#ce5c00;font-weight:bold">(</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">select</span> -</span></span><span style="display:flex;"><span> doc <span style="color:#8f5902;font-style:italic">#&gt;&gt; &#39;{fields,inputs,user,contact_id}&#39; as chw_id,</span> -</span></span><span style="display:flex;"><span> count<span style="color:#ce5c00;font-weight:bold">(</span>*<span style="color:#ce5c00;font-weight:bold">)</span> as n_forms -</span></span><span style="display:flex;"><span> from -</span></span><span style="display:flex;"><span> couchdb -</span></span><span style="display:flex;"><span> where to_timestamp<span style="color:#ce5c00;font-weight:bold">((</span>doc -&gt;&gt; <span style="color:#4e9a06">&#39;reported_date&#39;</span> <span style="color:#ce5c00;font-weight:bold">)</span>::bigint / 1000<span style="color:#ce5c00;font-weight:bold">)</span> &gt; <span style="color:#4e9a06">&#39;2020-09-14 00:00:00&#39;</span> -</span></span><span style="display:flex;"><span> group by <span style="color:#0000cf;font-weight:bold">1</span> -</span></span><span style="display:flex;"><span><span style="color:#ce5c00;font-weight:bold">)</span>, chw_names as <span style="color:#ce5c00;font-weight:bold">(</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">select</span> -</span></span><span style="display:flex;"><span> doc -&gt;&gt; <span style="color:#4e9a06">&#39;_id&#39;</span> as chw_id, -</span></span><span style="display:flex;"><span> doc -&gt;&gt; <span style="color:#4e9a06">&#39;name&#39;</span> as chw_name -</span></span><span style="display:flex;"><span> from couchdb -</span></span><span style="display:flex;"><span> where doc -&gt;&gt; <span style="color:#4e9a06">&#39;role&#39;</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#4e9a06">&#39;chw&#39;</span> -</span></span><span style="display:flex;"><span> and doc -&gt;&gt; <span style="color:#4e9a06">&#39;name&#39;</span> not like <span style="color:#4e9a06">&#39;ASC%&#39;</span> -</span></span><span style="display:flex;"><span><span style="color:#ce5c00;font-weight:bold">)</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">select</span> -</span></span><span style="display:flex;"><span> n.chw_name, -</span></span><span style="display:flex;"><span> f.n_forms -</span></span><span style="display:flex;"><span>from -</span></span><span style="display:flex;"><span>forms f -</span></span><span style="display:flex;"><span>left join -</span></span><span style="display:flex;"><span>chw_names n -</span></span><span style="display:flex;"><span>using<span style="color:#ce5c00;font-weight:bold">(</span>chw_id<span style="color:#ce5c00;font-weight:bold">)</span> -</span></span><span style="display:flex;"><span>order by n_forms desc<span style="color:#000;font-weight:bold">;</span> -</span></span></code></pre></div><p>A limitation of this technique is that CHWs who have not synchronized their device will not show up. In this case, a supervisor proactively checking per above is a better failsafe.</p> -<h3 id="cht-based-supervisor-tasks">CHT based supervisor tasks</h3> -<p><a href="https://docs.communityhealthtoolkit.org/apps/features/supervision/#supervisor-tasks">Tasks for supervisors</a> can also be used for the CHWs that report to them. This can be used to trigger a task when a CHW hasn&rsquo;t submitted a task within a given period on production.</p> -<h2 id="remediation">Remediation</h2> -<p>In the case that production data has been entered into a training instance, care must be used to ensure data is not lost or overwritten. These steps assume you&rsquo;re on a Linux command line and have jq, curl and cat installed. The task is to carefully download all docs created after a certain date on a training, clean up extra docs not needed and then upload them to a production instance:</p> -<ol> -<li> -<p>Set the aliases and environment variables:</p> -<pre tabindex="0"><code>alias ts=&#39;date -u +&#34;%Y-%m-%dT%H:%M:%SZ&#34;&#39; -alias curlj=&#34;curl -H &#39;content-type:application/json&#39;&#34; -alias curljz=&#34;curl --compressed -H &#39;content-type:application/json&#39;&#34; -export COUCH_URL=https://admin:pass@dev.example.org -</code></pre></li> -<li> -<p>Ensure affected CHWs have synced their devices to the development instance one last time. If they have been offline for some time and gathered production data, signing out or deleting the app before synchronizing may result in lost data.</p> -</li> -<li> -<p>Find the date when CHWs should have started using production. Convert this to epoch with milliseconds. For example if the time was &ldquo;Tue, 01 Dec 2020 09:36:50 GMT&rdquo; then the epoch would be &ldquo;1606815410000&rdquo;</p> -</li> -<li> -<p>Using Fauxton, create a view to show all documents created after this date:</p> -<pre tabindex="0"><code>function (doc) { -if(doc.reported_date &gt; 1606815410000) { -emit(doc._id, 1); -} -} -</code></pre><p><img src="create.view.png" alt="Creating a view in Fauxton"></p> -</li> -<li> -<p>Download the UUIDs of the documents in the view, and convert it to raw format (uuids-2.txt):</p> -<pre tabindex="0"><code>curl &#34;$COUCH_URL/medic/_design/temp/_view/temp1&#34; &gt; uuids-1.txt -cat uuids-1.txt | jq -r &#39;.rows[].id&#39; &gt; uuids-2.txt -</code></pre></li> -<li> -<p>Download all the matching docs to docs.json:</p> -<pre tabindex="0"><code>cat uuids-2.txt | jq --raw-input --slurp &#39;{keys: split(&#34;\n&#34;) }&#39; | curljz -d@- &#34;$COUCH_URL/medic/_all_docs?include_docs=true&#34; | jq &#39;{docs: [ .rows[] | select(.doc).doc ]}&#39; &gt; docs.json -</code></pre></li> -<li> -<p>Change the fields by deleting &ldquo;_rev&rdquo; field and also &ldquo;_attachments&rdquo; if uploading in a different instance. This can be carefully done by manually editing the JSON locally before uploading.</p> -</li> -<li> -<p>Change the COUCH_URL to be your production instance by setting another export:</p> -<pre tabindex="0"><code>export COUCH_URL=http://admin:pass@app.example.org -</code></pre></li> -<li> -<p>Upload the docs, for example, the following command adds a field (updated=true) to the docs and uploads them:</p> -<pre tabindex="0"><code>cat docs.json | jq &#39;{&#34;docs&#34;:[ .docs[] | .updated = true ]}&#39; | curljz -d@- $COUCH_URL/medic/_bulk_docs &gt; results.json -</code></pre></li> -</ol>Apps: Revalidate invalid reportshttps://docs.communityhealthtoolkit.org/apps/guides/data/invalid-reports/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/invalid-reports/ -<p>You may encounter a dreaded case when reports coming in to a Medic Webapp instance have a red indicator instead of the green indicator.</p> -<p><img src="invalid_report.png" alt="Invalid Reports"></p> -<p>This may be caused by:</p> -<ul> -<li>Missing forms in the <code>app_settings</code> config.</li> -<li>Missing or incorrect fields in the input form. e.g Missing patient ID, or Patient ID with letters</li> -<li>Extra fields in the input form. This happens when you don&rsquo;t configure for some fields in the app_settings.json of the webapp</li> -<li>Configuring some forms in the wrong section of the app_settings i.e <code>registrations</code> and <code>patient_reports</code>. Forms that don&rsquo;t have a patient_id field because it is generated afterwards, e.g ANCR, IMMR, go to the <code>registrations</code> section, while form that have a patient_id field e.g ANCP, ANCV, IMMV; go to the <code>patient_reports</code> section</li> -</ul> -<h2 id="how-to-solve">How to solve</h2> -<p>To revalidate an invalid report, we need to clear the errors field on the doc (set it to []). Updating this field from Futon will not work and will result in an endless update process. The recommended way to do it is to download the doc, update it and then upload it. This will also ensure propagation and replication in couchdb.</p> -<p>To download a doc, use:</p> -<pre tabindex="0"><code>curl &#39;https://&lt;username&gt;:&lt;password&gt;@&lt;instance&gt;.app.medicmobile.org/medic/&lt;doc id&gt;&#39; &gt; filename.json -</code></pre><p><code>filename.json</code> is the local file in your computer you have stored the doc json</p> -<p>Update <code>filename.json</code>: Set &ldquo;errors&rdquo; to [] and remove &ldquo;accept_patient_reports&rdquo; transition so that sentinel can revalidate the report.</p> -<p>Upload <code>filename.json</code> with:</p> -<pre tabindex="0"><code>curl -X PUT hhttps://&lt;username&gt;:&lt;password&gt;@&lt;instance&gt;.app.medicmobile.org/medic/&lt;doc id&gt; -d @filename.json --header &#34;Content-Type: application/json&#34; -</code></pre>Apps: CSV to Docshttps://docs.communityhealthtoolkit.org/apps/guides/data/csv-to-docs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/csv-to-docs/ -<h2 id="seeding-data-with-cht-conf">Seeding data with cht-conf</h2> -<p>Users, contacts, and report data can be specified in comma-separated value (CSV) files, then converted to JavaScript Object Notation (JSON) files and uploaded into your instance using <a href="https://github.com/medic/cht-conf">cht-conf</a>. This documentation will cover the CSV notation used, fetching CSV files from Google Sheets, converting the CSV files into JSON docs, and then uploading the data from the JSON files to your instance.</p> -<h3 id="converting-csvs">Converting CSVs</h3> -<p>Running <code>cht-conf</code> with the <code>csv-to-docs</code> action converts CSV files from the <code>csv</code> folder into JSON docs to be uploaded to your instance. The JSON files are stored in the <code>json_docs</code> folder. Instructions for creating the CSV files are in sections below.</p> -<h3 id="uploading-csvs">Uploading CSVs</h3> -<p>Running <code>cht-conf</code> with the <code>upload-docs</code> action will upload the JSON docs that were generated from the CSV files to your instance. For example, running <code>cht --local upload-docs</code> will upload the converted docs into your local instance. The target location <code>--local</code> can be replaced with an instance or URL. See <a href="https://github.com/medic/cht-conf">cht-conf</a> for detailed instructions.</p> -<h3 id="creating-csv-files-for-contacts-reports">Creating CSV files for Contacts, Reports</h3> -<p>A separate CSV file is needed for each type of place, person, or report in your project&rsquo;s local <code>csv</code> folder. The name of the file determines the type of doc created for rows contained in the file. The possible types are: <code>report</code>, <code>person</code>, and <code>place</code>. Each of these has a further specifier provided in the filename:</p> -<ul> -<li><code>place.{place_type}.csv</code>: where <code>{place_type}</code> is the type of place specified in the file. By default, the place types are one of <code>clinic</code>, <code>health_center</code>, or <code>district_hospital</code>. As of 3.7 of the <a href="https://github.com/medic/cht-core">Core Framework</a>, the number of place types and their names can be configured — the <code>{place_type}</code> should match with the hierarchy names used.</li> -<li><code>person.{parent_place_type}.csv</code>: where <code>{parent_place_type}</code> is the type of place to which the people in the file will belong.</li> -<li><code>report.{form_id}.csv</code>: where <code>{form_id}</code> is the form ID for all the reports in the file. You will need one file per form ID</li> -</ul> -<p>Here are some examples:</p> -<ul> -<li>File named <code>place.district_hospital.csv</code> adds the property <code>&quot;type&quot;:&quot;district_hospital&quot;</code></li> -<li>File named <code>person.clinic.csv</code> add the property <code>&quot;type&quot;:&quot;person&quot;</code></li> -<li>File named <code>report.immunization_visit.csv</code> add the property <code>&quot;type&quot;:&quot;report&quot;, &quot;form&quot;:&quot;immunization_visit&quot;</code></li> -</ul> -<p>In each of these files a header row is used to specify the JSON field names, and each subsequent row specifies the corresponding values for a doc. A <code>_id</code> field is automatically generated with a universally unique identifier.</p> -<p>Here is an example of a <code>csv/person.clinic.csv</code> file for people belonging to clinics:</p> -<pre tabindex="0"><code class="language-csv" data-lang="csv">name,sex,date_of_birth -Adriana Akiyama,female,1985-12-31 -Becky Backlund,female,1987-10-17 -Carson Crane,male,2015-01-23 -</code></pre><p>Here is the table representation of the CSV:</p> -<table> -<thead> -<tr> -<th>name</th> -<th>sex</th> -<th>date_of_birth</th> -</tr> -</thead> -<tbody> -<tr> -<td>Adriana Akiyama</td> -<td>female</td> -<td>1985-12-31</td> -</tr> -<tr> -<td>Becky Backlund</td> -<td>female</td> -<td>1987-10-17</td> -</tr> -<tr> -<td>Carson Crane</td> -<td>male</td> -<td>2015-01-23</td> -</tr> -</tbody> -</table> -<p>Converting that CSV file to JSON docs with the <code>csv-to-docs</code> action would generate three files, one for each person. Here is one of the corresponding JSON files, <code>json_docs/dbfbc0f0-117a-59ec-9542-3313fb10ef25.doc.json</code>, which was created from the CSV data above:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;person&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Adriana Akiyama&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;sex&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;female&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;date_of_birth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;1985-12-31&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;dbfbc0f0-117a-59ec-9542-3313fb10ef25&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="special-notations">Special notations</h3> -<h4 id="specifying-property-type">Specifying property type</h4> -<p>By default, values are parsed as strings. To parse a CSV column as a different JSON type, append the JSON type name to the column definition, e.g.</p> -<pre><code>column_one,column_two:bool,column_three:int,column_four:float,column_five:date,column_six:timestamp -some string,true,1,2.3,2017-12-31,1513255007072 -</code></pre> -<p>This would create a structure such as:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;09efb53f-9cd8-524c-9dfd-f62c242f1817&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;column_one&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;some string&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;column_two&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;column_three&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;column_four&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">2.3</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;column_five&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2017-12-31T00:00:00.000Z&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;column_six&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1513255007072</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h5 id="excluding-column">Excluding column</h5> -<p>A special column type, <code>excluded</code>, is used for excluding a column from the final JSON data:</p> -<pre><code>my_column_that_will_not_be_a_property:excluded -</code></pre> -<p>This can be useful if using a column for doc references.</p> -<h4 id="available-types">Available types</h4> -<table> -<thead> -<tr> -<th>type</th> -<th>outcome</th> -</tr> -</thead> -<tbody> -<tr> -<td>date</td> -<td>Creates a date using Date() in javascript</td> -</tr> -<tr> -<td>rel-date</td> -<td>Creates a date with the addition of number days to the current date. A negative number results in a past date.</td> -</tr> -<tr> -<td>timestamp</td> -<td>Sets the string passed in as a timestamp number. If the timestamp in the csv is in milliseconds that will be used. If a date is passed it will be parsed and the milliseconds returned. EX: 04 Dec 1995 00:12:00 GMT becomes 818035920000</td> -</tr> -<tr> -<td>rel-timestamp</td> -<td>Creates a timestamp that is offset by milliseconds against the current(NOW) timestamp. A negative number results in a past timestamp</td> -</tr> -<tr> -<td>int</td> -<td>Parses the value as an int using Number.parseInt()</td> -</tr> -<tr> -<td>bool</td> -<td>Sets boolean value based on string passed, either <code>&quot;true&quot;</code> or <code>&quot;false&quot;</code></td> -</tr> -<tr> -<td>string</td> -<td>Sets the value as the string being interpreted. Can be omitted if values are only strings.</td> -</tr> -<tr> -<td>float</td> -<td>Sets value using Number.parseFloat()</td> -</tr> -</tbody> -</table> -<h3 id="including-another-doc">Including another doc</h3> -<p>Often times database documents need to include or refer to other documents in the database. This can be achieved with queries across CSV files, which is done by specifying a query in the column header. The query specifies the doc type (<code>person</code> or <code>place</code>) and matching condition.</p> -<p>For instance, to include the parent district&rsquo;s doc in a health center&rsquo;s doc, the <code>parent:place WHERE reference_id=COL_VAL</code> column header is used. The <code>COL_VAL</code> is a special notation for that column&rsquo;s value for the row, and it will be used to match against the <code>reference_id</code> field in all other places. Given these example CSVs, you can see the corresponding JSON structure:</p> -<p><strong><code>place.district.csv</code>:</strong></p> -<table> -<thead> -<tr> -<th>reference_id:excluded</th> -<th>is_name_generated</th> -<th>name</th> -<th>reported_date:timestamp</th> -</tr> -</thead> -<tbody> -<tr> -<td>district_1</td> -<td>false</td> -<td>D1</td> -<td>1544031155715</td> -</tr> -<tr> -<td>district_2</td> -<td>false</td> -<td>D2</td> -<td>1544031155715</td> -</tr> -<tr> -<td>district_3</td> -<td>false</td> -<td>D3</td> -<td>1544031155715</td> -</tr> -</tbody> -</table> -<p><strong><code>place.health_center.csv</code>:</strong></p> -<table> -<thead> -<tr> -<th>reference_id:excluded</th> -<th>parent:place WHERE reference_id=COL_VAL</th> -<th>is_name_generated</th> -<th>name</th> -<th>reported_date:timestamp</th> -</tr> -</thead> -<tbody> -<tr> -<td>health_center_1</td> -<td>district_1</td> -<td>false</td> -<td>HC1</td> -<td>1544031155715</td> -</tr> -<tr> -<td>health_center_2</td> -<td>district_2</td> -<td>false</td> -<td>HC2</td> -<td>1544031155715</td> -</tr> -<tr> -<td>health_center_3</td> -<td>district_3</td> -<td>false</td> -<td>HC3</td> -<td>1544031155715</td> -</tr> -</tbody> -</table> -<p><strong><code>480d0cd0-c021-5d55-8c63-d86576d592fc.doc.json</code></strong>:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;health_center&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;district_hospital&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;is_name_generated&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;false&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;D2&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;external_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;notes&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;geolocation&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;reported_date&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1544031155715</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;f223f240-5d6a-5a7a-91d4-46d3c59de73e&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;is_name_generated&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;false&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;HC7&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;external_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;notes&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;geolocation&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;reported_date&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1544031155715</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;480d0cd0-c021-5d55-8c63-d86576d592fc&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h5 id="including-value-from-another-doc">Including value from another doc</h5> -<p>Similar to including another doc, it is also possible to get the value of a specific field in another doc. For instance, if <code>parent:GET _id OF place WHERE reference_id=COL_VAL</code> were used in the example above, the <code>parent</code> field&rsquo;s value would have been set to the <code>_id</code> of the referred to doc instead of including the whole doc. Note that <code>_id</code> is a generated value included in all generated docs.</p> -<table> -<thead> -<tr> -<th>reference_id:excluded</th> -<th>parent:GET _id OF place WHERE reference_id=COL_VAL</th> -<th>is_name_generated</th> -<th>name</th> -<th>reported_date:timestamp</th> -</tr> -</thead> -<tbody> -<tr> -<td>health_center_1</td> -<td>district_1</td> -<td>false</td> -<td>HC1</td> -<td>1544031155715</td> -</tr> -<tr> -<td>health_center_2</td> -<td>district_2</td> -<td>false</td> -<td>HC2</td> -<td>1544031155715</td> -</tr> -<tr> -<td>health_center_3</td> -<td>district_3</td> -<td>false</td> -<td>HC3</td> -<td>1544031155715</td> -</tr> -</tbody> -</table> -<p>The resulting doc would be as follows, with the <code>_id</code> from <code>district_1</code> as the <code>parent</code> value:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;health_center&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;0c31056a-3a80-54dd-b136-46145d451a66&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;is_name_generated&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;false&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;HC3&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;external_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;notes&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;geolocation&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;reported_date&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1544031155715</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;45293356-353c-5eb1-9a41-baa3427b4f69&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h5 id="link-to-a-parent-document-already-existing-in-the-database">Link to a parent document already existing in the database</h5> -<p>You may wish to link the new record to a parent document that already exists in the database that was created in the past. -Get the UUID of the existing parent document and place it in the column named <code>parent._id</code>. Get the rest of the the parent UUIDs in the hierarchy lineage and track them in separate columns. It is important to track all levels of the hierarchy as certain app features (e.g Tasks) in the configuration could be directly referencing a UUID deeper in the hierrarchy.</p> -<p>For example, a parent document below can be linked as shown in the table</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;0c31056a-3a80-54dd-b136-46145d451a66&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;66142fef-c4b4-4578-94e2-3d7f1a304ef7&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><table> -<thead> -<tr> -<th>reference_id:excluded</th> -<th>parent._id</th> -<th>parent.parent._id</th> -<th>is_name_generated</th> -<th>name</th> -<th>reported_date:timestamp</th> -</tr> -</thead> -<tbody> -<tr> -<td>health_center_1</td> -<td>0c31056a-3a80-54dd-b136-46145d451a66</td> -<td>66142fef-c4b4-4578-94e2-3d7f1a304ef7</td> -<td>false</td> -<td>HC1</td> -<td>1544031155715</td> -</tr> -</tbody> -</table> -<p>The resulting doc would be as follows:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;health_center&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;0c31056a-3a80-54dd-b136-46145d451a66&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;66142fef-c4b4-4578-94e2-3d7f1a304ef7&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;is_name_generated&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;false&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;HC1&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;reported_date&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1544031155715</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;45293356-353c-5eb1-9a41-baa3427b4f69&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="creating-csv-files-for-users">Creating CSV files for users</h3> -<p>To create user accounts from CSV files, a <code>users.csv</code> file will be needed in the <code>csv</code> folder. The <code>users.csv</code> file requires columns for <code>username</code> and <code>password</code>. Additional columns can be used for any additional fields needed on the user&rsquo;s doc, for example <code>roles</code>, <code>phone</code>. Running the <code>csv-to-docs</code> <code>upload-docs</code> <code>create-users</code> actions in that order will generate the necessary JSON docs with a <code>users.csv</code> file in your working directory, and then create the people, places, and users.</p> -<p>The following sections describe the different ways to associate the new users to contacts.</p> -<h4 id="creating-new-users-with-existing-contacts">Creating new users with existing contacts</h4> -<p>When creating users that are associated to existing contacts, <code>contact</code> and <code>place</code> columns should be created. Each row should have the UUID of an existing person for <code>contact</code>, and an existing place for <code>place</code>.</p> -<h3 id="creating-new-users-with-new-contacts">Creating new users with new contacts</h3> -<p>To create new contacts for each new user provide values for <code>contact.name</code>, <code>place.name</code>, and <code>place.parent</code> (can be existing place), as seen in this example CSV:</p> -<pre tabindex="0"><code class="language-csv" data-lang="csv">username,password,roles,name,phone,contact.name,place.c_prop,place.type,place.name,place.parent -alice,Secret_1,district-admin,Alice Example,+123456789,Alice,p_val_a,health_center,alice area,district_uuid -bob,Secret_1,district-admin,bob Example,+123456789,bob,p_val_a,health_center,bob area,disctrict_uuid -</code></pre><p>The <code>username</code>, <code>password</code>, <code>contact.name</code>, <code>place.type</code>, <code>place.name</code> columns are required to have functional users with new places.</p> -<h3 id="creating-new-users-linked-to-contacts-in-csv-files">Creating new users linked to contacts in CSV files</h3> -<p>To associate the new users to contacts that will also be created with the <code>csv-to-docs</code> action, use reference queries to the contacts. -For instance, the column header for the person would be <code>contact:person WHERE reference_id=COL_VAL</code>, and for the place would be <code>place:GET _id OF place WHERE reference_id=COL_VAL</code>.</p> -<p>Here is a example of how the three CSV files need to be configured to setup a user linked to existing place and contact.</p> -<p><strong><code>csv/place.health_center.csv</code>:</strong></p> -<pre tabindex="0"><code class="language-csv" data-lang="csv">reference_id:excluded,parent:place WHERE reference_id=COL_VAL,is_name_generated,name,reported_date:timestamp -health_center_1,district_1,FALSE,HC1,1544031155715 -</code></pre><p>Generated JSON doc for the health center:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;health_center&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;district_hospital&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;is_name_generated&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;false&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;District1&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;external_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;notes&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;geolocation&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;reported_date&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1544031155715</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;e8f9739a-5d37-5b1e-be3c-a571b2c2409b&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;is_name_generated&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;FALSE&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;HC1&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;reported_date&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1544031155715</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;8606a91a-f454-56e3-a089-0b686af3c6b7&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p><strong><code>csv/person.csv</code>:</strong></p> -<pre tabindex="0"><code class="language-csv" data-lang="csv">reference_id:excluded,parent:place WHERE reference_id=COL_VAL,name,phone,sex,role,reported_date,patient_id -p_hc1,health_center_1,Bob Johnson 1,+16143291527,male,manager,1552494835669,60951 -p_hc2,health_center_1,Bob Johnson 2,+16143291528,male,manager,1552494835669,60951 -</code></pre><p>Generated JSON doc for the person:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;person&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;health_center&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;district_hospital&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;is_name_generated&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;false&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;District1&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;external_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;notes&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;geolocation&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;reported_date&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1544031155715</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;e8f9739a-5d37-5b1e-be3c-a571b2c2409b&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;is_name_generated&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;FALSE&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;HC1&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;reported_date&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1544031155715</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;8606a91a-f454-56e3-a089-0b686af3c6b7&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Bob Johnson 1&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;phone&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;+16143291527&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;sex&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;male&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;role&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;manager&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;reported_date&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;1552494835669&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;patient_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;60951&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;65c52076-84c5-53a2-baca-88e6ec6e0875&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p><strong><code>csv/users.csv</code></strong>:</p> -<pre tabindex="0"><code class="language-csv" data-lang="csv">username,password,roles,phone,contact:person WHERE reference_id=COL_VAL,place:GET _id OF place WHERE reference_id=COL_VAL -ac1,Secret_1,district_admin:red1,+123456789,p_hc1,health_center_1 -ac2,Secret_1,district_admin:supervisor,+123456789,p_hc2,health_center_1 -ac3,Secret_1,district_admin,+123456789,p_hc3,health_center_1 -ac4,Secret_1,district_admin,+123456789,p_hc4,health_center_1 -</code></pre><p>This will generate the <code>users.csv</code> file in the working directory which is used by the <code>create-users</code> action. The contact and place fields should be resolved to the actual UUIDs.</p> -<pre tabindex="0"><code class="language-csv" data-lang="csv">p_hc1&#34;username&#34;,&#34;password&#34;,&#34;roles&#34;,&#34;contact&#34;,&#34;phone&#34;,&#34;place&#34; -&#34;ac1&#34;,&#34;Secret_1&#34;,&#34;district_admin:red1&#34;,&#34;65c52076-84c5-53a2-baca-88e6ec6e0875&#34;,&#34;+123456789&#34;,&#34;8606a91a-f454-56e3-a089-0b686af3c6b7&#34; -&#34;ac2&#34;,&#34;Secret_1&#34;,&#34;district_admin:supervisor&#34;,&#34;b7d0dbd5-beeb-52a8-8e4c-513d0baece8e&#34;,&#34;+123456789&#34;,&#34;8606a91a-f454-56e3-a089-0b686af3c6b7&#34; -</code></pre><h3 id="using-csv-files-on-google-drive">Using CSV files on Google Drive</h3> -<p>Individual Google Sheets can be used for each CSV file, which can be exported manually to CSV file format, or linked to your project to be downloaded by cht-conf.</p> -<p>To fetch the files from Google Drive run the <code>fetch-csvs-from-google-drive</code> action in cht-conf. This will download the CSV files specified in the <code>csvs-on-google-drive.json</code> file, and place them into the <code>csv</code> folder.</p> -<h4 id="linking-to-google-drive">Linking to Google Drive</h4> -<p>The file <code>csvs-on-google-drive.json</code> in your project&rsquo;s home directory will consist of a key value pair for each CSV file. The keys must be the filename where the CSV will be stored locally — see the CSV <a href="#creating-csv-files-for-contacts-reports">file documentation above</a> for the notation. The value of each key must be the ID of the corresponding file in Google Drive — the ID can be obtained from the URL eg <code>https://docs.google.com/spreadsheets/d/{FILE_ID}/edit</code>.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;person.clinic.csv&#34;</span><span style="color:#000;font-weight:bold">:</span><span style="color:#4e9a06">&#34;google_drive_ID&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="google-drive-authentication">Google Drive authentication</h3> -<p>Cht-conf leverages Google authentication to access Google Drive. You will need to create a client_secrets file named <code>.gdrive.secrets.json</code> and place it in your working directory, and <a href="https://developers.google.com/identity/protocols/OAuth2InstalledApp">create a token</a>.</p> -<p>Create the <code>.gdrive.secrets.json</code> file by downloading the <code>client_secrets.json</code> from Google. You will need a <code>CLIENT_ID</code>, <code>CLIENT_SECRET</code> and <code>REDIRECT_URL</code>. You can find these pieces of information by going to the Developer Console, clicking your project &ndash;&gt; APIs &amp; auth &ndash;&gt; credentials &ndash;&gt; Download JSON. This will download the credentials but will need modified to be in this structure.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;client_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&lt;client_id&gt;.apps.googleusercontent.com&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;project_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;proj_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;auth_uri&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;https://accounts.google.com/o/oauth2/auth&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;token_uri&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;https://accounts.google.com/o/oauth2/token&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;auth_provider_x509_cert_url&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;https://www.googleapis.com/oauth2/v1/certs&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;client_secret&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;client_secret&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;redirect_uris&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#34;urn:ietf:wg:oauth:2.0:oob&#34;</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06">&#34;http://localhost&#34;</span><span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>See Google&rsquo;s docs <a href="https://github.com/googleapis/google-api-nodejs-client#user-content-oauth2-client">here</a> on Oauth2</p>Apps: Data Synchronization and Analyticshttps://docs.communityhealthtoolkit.org/apps/guides/data/analytics/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/analytics/ \ No newline at end of file +Managing Data on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/guides/data/Recent content in Managing Data on Community Health ToolkitHugo -- gohugo.ioenDatabase document hydrationhttps://docs.communityhealthtoolkit.org/apps/guides/data/hydration/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/hydration/Documents are connected with each other via their document _id. For example: +a contact document is connected to its parent by storing their _id in the parent property +a report document is connected to its submitter by storing their _id in the contact property +See Also: DB Schema +To optimize database storage, documents are &ldquo;minified&rdquo; when stored and are &ldquo;hydrated&rdquo; when they are used by the app. +Minification Minification means replacing a linked document&rsquo;s content with an object that only contains its uuid.How to bulk load usershttps://docs.communityhealthtoolkit.org/apps/guides/data/users-bulk-load/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/users-bulk-load/The bulk user upload feature is available in 3.16.0 and later versions of the CHT. As of CHT 3.17.0, when creating both a contact and a place, the contact will be set as the default contact of the place. User creation can be scripted using the CHT API directly or using the cht-conf tool, which is detailed in the CSV-to-Docs guide. +This feature can be used to load as many users as possible but works optimally with chunks of 1,000 users or less.CHT Impact Metricshttps://docs.communityhealthtoolkit.org/apps/guides/data/impact-metrics/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/impact-metrics/Impact monitoring is an essential part of both the Community Health Toolkit and Medic&rsquo;s processes and ethos. We are committed to harnessing data to: +Support our partners in data-driven operational and strategic decision making Inform our product roadmap and organizational strategy, and Participate in overarching policy discussions around community health. As a member of the CHT community and potential CHT implementer, we encourage you to learn more about the recommended impact metrics for monitoring and evaluation across priority use cases by reviewing the metrics listed below.Detecting and fixing production data on training instanceshttps://docs.communityhealthtoolkit.org/apps/guides/data/training-instance/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/training-instance/After onboarding CHWs, sometimes data ends up on the wrong CHT instance. There are some passive and active actions you can take to help deal with this situation. +Monitoring Monitoring is a good way to see if CHWs are sending forms to the wrong CHT server. By catching such a problem early, it may be easy to fix manually which avoids more laborious fixes on the command line for admins.Revalidate invalid reportshttps://docs.communityhealthtoolkit.org/apps/guides/data/invalid-reports/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/invalid-reports/You may encounter a dreaded case when reports coming in to a Medic Webapp instance have a red indicator instead of the green indicator. +This may be caused by: +Missing forms in the app_settings config. Missing or incorrect fields in the input form. e.g Missing patient ID, or Patient ID with letters Extra fields in the input form. This happens when you don&rsquo;t configure for some fields in the app_settings.json of the webapp Configuring some forms in the wrong section of the app_settings i.CSV to Docshttps://docs.communityhealthtoolkit.org/apps/guides/data/csv-to-docs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/csv-to-docs/Seeding data with cht-conf Users, contacts, and report data can be specified in comma-separated value (CSV) files, then converted to JavaScript Object Notation (JSON) files and uploaded into your instance using cht-conf. This documentation will cover the CSV notation used, fetching CSV files from Google Sheets, converting the CSV files into JSON docs, and then uploading the data from the JSON files to your instance. +Converting CSVs Running cht-conf with the csv-to-docs action converts CSV files from the csv folder into JSON docs to be uploaded to your instance. \ No newline at end of file diff --git a/apps/guides/data/invalid-reports/index.html b/apps/guides/data/invalid-reports/index.html index 06183b59ea..a854a829b7 100644 --- a/apps/guides/data/invalid-reports/index.html +++ b/apps/guides/data/invalid-reports/index.html @@ -1,9 +1,9 @@ -Revalidate invalid reports | Community Health Toolkit +Revalidate invalid reports | Community Health Toolkit

Revalidate invalid reports

How to revalidate an invalid report

You may encounter a dreaded case when reports coming in to a Medic Webapp instance have a red indicator instead of the green indicator.

Invalid Reports

This may be caused by:

  • Missing forms in the app_settings config.
  • Missing or incorrect fields in the input form. e.g Missing patient ID, or Patient ID with letters
  • Extra fields in the input form. This happens when you don’t configure for some fields in the app_settings.json of the webapp
  • Configuring some forms in the wrong section of the app_settings i.e registrations and patient_reports. Forms that don’t have a patient_id field because it is generated afterwards, e.g ANCR, IMMR, go to the registrations section, while form that have a patient_id field e.g ANCP, ANCV, IMMV; go to the patient_reports section

How to solve

To revalidate an invalid report, we need to clear the errors field on the doc (set it to []). Updating this field from Futon will not work and will result in an endless update process. The recommended way to do it is to download the doc, update it and then upload it. This will also ensure propagation and replication in couchdb.

To download a doc, use:

curl 'https://<username>:<password>@<instance>.app.medicmobile.org/medic/<doc id>' > filename.json
+ Create project issue

Revalidate invalid reports

How to revalidate an invalid report

You may encounter a dreaded case when reports coming in to a Medic Webapp instance have a red indicator instead of the green indicator.

Invalid Reports

This may be caused by:

  • Missing forms in the app_settings config.
  • Missing or incorrect fields in the input form. e.g Missing patient ID, or Patient ID with letters
  • Extra fields in the input form. This happens when you don’t configure for some fields in the app_settings.json of the webapp
  • Configuring some forms in the wrong section of the app_settings i.e registrations and patient_reports. Forms that don’t have a patient_id field because it is generated afterwards, e.g ANCR, IMMR, go to the registrations section, while form that have a patient_id field e.g ANCP, ANCV, IMMV; go to the patient_reports section

How to solve

To revalidate an invalid report, we need to clear the errors field on the doc (set it to []). Updating this field from Futon will not work and will result in an endless update process. The recommended way to do it is to download the doc, update it and then upload it. This will also ensure propagation and replication in couchdb.

To download a doc, use:

curl 'https://<username>:<password>@<instance>.app.medicmobile.org/medic/<doc id>' > filename.json
 

filename.json is the local file in your computer you have stored the doc json

Update filename.json: Set “errors” to [] and remove “accept_patient_reports” transition so that sentinel can revalidate the report.

Upload filename.json with:

curl -X PUT hhttps://<username>:<password>@<instance>.app.medicmobile.org/medic/<doc id> -d @filename.json --header "Content-Type: application/json"
 

CHT Applications > Features > Reports

Reports for Data & Report Management

-

\ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/guides/data/training-instance/index.html b/apps/guides/data/training-instance/index.html index 39e2ea9608..7dbf1e09ff 100644 --- a/apps/guides/data/training-instance/index.html +++ b/apps/guides/data/training-instance/index.html @@ -1,9 +1,9 @@ -Detecting and fixing production data on training instances | Community Health Toolkit +Detecting and fixing production data on training instances | Community Health Toolkit

Detecting and fixing production data on training instances

How to monitor for production data on a training instance, and remediation techniques

After onboarding CHWs, sometimes data ends up on the wrong CHT instance. There are some passive and active actions you can take to help deal with this situation.

Monitoring

Monitoring is a good way to see if CHWs are sending forms to the wrong CHT server. By catching such a problem early, it may be easy to fix manually which avoids more laborious fixes on the command line for admins.

Manual device checks

Supervisors can actively monitor CHWs as they register their first household. If each CHW is given a deadline to do this, the Supervisor can follow up promptly with CHWs who have not met the deadline.

Centralized database checks

From an administrative perspective, passive monitoring can be done by querying the Postgres instance that couch2pg is populating. If 2020-09-14 was the date to stop use the training instance, this SQL query would show CHWs using the wrong instance after 2020-09-14:

with forms as (
+ Create project issue

Detecting and fixing production data on training instances

How to monitor for production data on a training instance, and remediation techniques

After onboarding CHWs, sometimes data ends up on the wrong CHT instance. There are some passive and active actions you can take to help deal with this situation.

Monitoring

Monitoring is a good way to see if CHWs are sending forms to the wrong CHT server. By catching such a problem early, it may be easy to fix manually which avoids more laborious fixes on the command line for admins.

Manual device checks

Supervisors can actively monitor CHWs as they register their first household. If each CHW is given a deadline to do this, the Supervisor can follow up promptly with CHWs who have not met the deadline.

Centralized database checks

From an administrative perspective, passive monitoring can be done by querying the Postgres instance that couch2pg is populating. If 2020-09-14 was the date to stop use the training instance, this SQL query would show CHWs using the wrong instance after 2020-09-14:

with forms as (
   select
     doc #>> '{fields,inputs,user,contact_id}' as chw_id,
     count(*) as n_forms
@@ -340,7 +340,8 @@
 
  • Change the fields by deleting “_rev” field and also “_attachments” if uploading in a different instance. This can be carefully done by manually editing the JSON locally before uploading.

  • Change the COUCH_URL to be your production instance by setting another export:

    export COUCH_URL=http://admin:pass@app.example.org
     
  • Upload the docs, for example, the following command adds a field (updated=true) to the docs and uploads them:

    cat docs.json | jq '{"docs":[ .docs[] | .updated = true ]}' | curljz -d@- $COUCH_URL/medic/_bulk_docs > results.json
     
  • -

    Last modified 09.03.2023: Training card guide (#970) (fb07ed89)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/data/users-bulk-load/index.html b/apps/guides/data/users-bulk-load/index.html index ecf2848108..44a321968b 100644 --- a/apps/guides/data/users-bulk-load/index.html +++ b/apps/guides/data/users-bulk-load/index.html @@ -1,9 +1,9 @@ -How to bulk load users | Community Health Toolkit +How to bulk load users | Community Health Toolkit

    How to bulk load users

    How to create users in bulk

    The bulk user upload feature is available in 3.16.0 and later versions of the CHT. As of CHT 3.17.0, when creating both a contact and a place, the contact will be set as the default contact of the place. User creation can be scripted using the CHT API directly or using the cht-conf tool, which is detailed in the CSV-to-Docs guide.

    This feature can be used to load as many users as possible but works optimally with chunks of 1,000 users or less.

    Steps to bulk load users:

    1. Using Google Sheets, populate a spreadsheet with the users to be imported.
    2. Import the new users using the Admin UI in the CHT.
    3. Handle any errors that may occur during importation.
    4. When done, you will have created new users, new contacts and new places, all of which are correctly associated in CouchDB with the correct UUIDs.

    Workbook Instructions

    The workbook contains a varying number of spreadsheets depending on the hierarchy in question. Users with different roles are placed in different spreadsheets as shown in the templates below. For example when creating users with chw and chw_supervisor roles, you will have “contact.chw”, “contact.chw_VLOOKUP”, “contact.chw_supervisor” and “contact.chw_supervisor_VLOOKUP” spreadsheets. These pair per role spreadsheets contain actual data on users and parent place data respectively.

    There is another spreadsheet, “place.type_VLOOKUP”, which is required when creating user accounts, contacts and their places. This spreadsheet defines the name and type of places in your hierarchy and should match those in the app_settings.json file. Note that you will need to create the parent place before importing the users.

    To get started, there are three different workbook templates available that are compatible with the default configuration of the CHT, they cater for use cases that you might encounter when creating users in bulk. You will notice some columns have an :excluded suffix. These are columns that are ignored by the API and allow addition of autocomplete and data validation within the spreadsheet to make it easier to work with.

    Click on any of the use cases below to make a copy of the spreadsheet for the use case in question:

    We will use the second use case to create user accounts and their contacts in the example below.

    Contact Spreadsheet Instructions

    The spreadsheet interfaces with the POST /api/v1/users API which works as though passing a JSON array of users. Rows in the spreadsheet represent a user while columns represent properties of the user. + Create project issue

    How to bulk load users

    How to create users in bulk

    The bulk user upload feature is available in 3.16.0 and later versions of the CHT. As of CHT 3.17.0, when creating both a contact and a place, the contact will be set as the default contact of the place. User creation can be scripted using the CHT API directly or using the cht-conf tool, which is detailed in the CSV-to-Docs guide.

    This feature can be used to load as many users as possible but works optimally with chunks of 1,000 users or less.

    Steps to bulk load users:

    1. Using Google Sheets, populate a spreadsheet with the users to be imported.
    2. Import the new users using the Admin UI in the CHT.
    3. Handle any errors that may occur during importation.
    4. When done, you will have created new users, new contacts and new places, all of which are correctly associated in CouchDB with the correct UUIDs.

    Workbook Instructions

    The workbook contains a varying number of spreadsheets depending on the hierarchy in question. Users with different roles are placed in different spreadsheets as shown in the templates below. For example when creating users with chw and chw_supervisor roles, you will have “contact.chw”, “contact.chw_VLOOKUP”, “contact.chw_supervisor” and “contact.chw_supervisor_VLOOKUP” spreadsheets. These pair per role spreadsheets contain actual data on users and parent place data respectively.

    There is another spreadsheet, “place.type_VLOOKUP”, which is required when creating user accounts, contacts and their places. This spreadsheet defines the name and type of places in your hierarchy and should match those in the app_settings.json file. Note that you will need to create the parent place before importing the users.

    To get started, there are three different workbook templates available that are compatible with the default configuration of the CHT, they cater for use cases that you might encounter when creating users in bulk. You will notice some columns have an :excluded suffix. These are columns that are ignored by the API and allow addition of autocomplete and data validation within the spreadsheet to make it easier to work with.

    Click on any of the use cases below to make a copy of the spreadsheet for the use case in question:

    We will use the second use case to create user accounts and their contacts in the example below.

    Contact Spreadsheet Instructions

    The spreadsheet interfaces with the POST /api/v1/users API which works as though passing a JSON array of users. Rows in the spreadsheet represent a user while columns represent properties of the user. Each column in the spreadsheet maps to an object property understood by the API to insert the users into the database. These properties can be found in the Users API documentation.

    Contact spreadsheets are named according to the user role, for example when creating users who are chws and others who are chw_supervisors, the following contact spreadsheets are populated respectively: “contact.chw” and “contact.chw_supervisor”.

    There are three sections to the contact spreadsheet:

    bulk user import spreadsheet with areas labeled

    Spreadsheet Area 1

    These three columns are where you paste the results after running an import. See step 8 in “Importing users example” below.

    1. import.status:excluded: This field can have three values. Over time, they should all be imported or skipped as you will have processed all users on the list:
      • imported- This user has already been successfully imported
      • skipped - This user was skipped
      • error - Contains errors that were encountered during importation. See import.message:excluded field for more information
    2. import.message:excluded: The status of the last import. For example, Imported successfully or Username 'mrjones' already taken
    3. import.username:excluded: Use this column to ensure you’re matching the response with the correct user in the contact.username to the right

    Spreadsheet Area 2

    This is where you enter your user data and contains the following columns:

    1. username: username used to log into the application
    2. Parent-Place:excluded: existing parent place of the new user place. A drop-down populated from contact vlookup spreadsheet
    3. User-Place-Type:excluded: type of user place to be created. A drop-down populated from place vlookup spreadsheet
    4. contact.first_name: first name of the new user
    5. contact.last_name: last name of the new user
    6. contact.sex: sex of the new user
    7. contact.phone: phone number of the new user
    8. email: email of the new user (optional field)
    9. contact.meta.created_by: this column contains metadata on the person who created the user (optional field)
    10. token_login: whether the user should be sent login credentials via SMS as a link
    11. contact.type or place.type: when creating a user, is logged as contact. In the case of the default config, possible values are district_hospital, health_center, clinic, and person
    12. contact.contact_type or place.contact_type: defines the type of contact being created depending on the deployment’s configuration. It should match one of the contact types defined in the config’s contact_types field
    13. type: role of the contact

    Spreadsheet Area 3

    Columns in this area are automatically populated by the spreadsheet logic. While this is needed to create a user, it is intentionally not editable and you will see this error when you try to edit data:

    bulk user import spreadsheet warning

    However, for the mapping to occur as expected, you may edit the fields contact.role, contact.contact_type, place.contact_type and type to match your deployment’s configuration.

    Do not edit column headers in row 1. They are needed by the CHT to identify which data is in it. Changing the names will result in errors or missing data in the CHT.

    Passwords

    Passwords are automatically generated by the spreadsheet. Use caution when editing rows marked as imported in the Import.status:excluded column. For example, if a user was imported two weeks ago and the token_login is set to TRUE and then back to FALSE, @@ -361,7 +361,8 @@ : Post Apiv2users

    RESTful Application Programming Interfaces for integrating with CHT applications

    CHT Applications > Features > App Management

    An interface for non-technical administrative users to manage users and settings

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/guides/database/couch2pg-oom-errors/index.html b/apps/guides/database/couch2pg-oom-errors/index.html index 82d462918a..8f3542632e 100644 --- a/apps/guides/database/couch2pg-oom-errors/index.html +++ b/apps/guides/database/couch2pg-oom-errors/index.html @@ -1,9 +1,9 @@ -Fixing couch2pg Memory Errors | Community Health Toolkit +Fixing couch2pg Memory Errors | Community Health Toolkit

    Fixing couch2pg Memory Errors

    Dealing with out-of-memory errors in couch2pg

    Some times when couch2pg is replicating documents to postgres, it encounters very large info docs that are larger than the memory allocation of the document sync array and causes out-of-memory errors. + Create project issue

    Fixing couch2pg Memory Errors

    Dealing with out-of-memory errors in couch2pg

    Some times when couch2pg is replicating documents to postgres, it encounters very large info docs that are larger than the memory allocation of the document sync array and causes out-of-memory errors. To fix this, we need to delete this document so that couch2pg can proceed. Below are steps to follow to achieve this.

    1. Reduce the size of the replicated docs to a value of say 4 in the couch2pg.conf file so that you can get within the range of the failing document.
    2. Clone the existing couch2pg repo so that you can run couch2pg locally
    3. Edit the file lib/importer.js in the couch2pg source code to be able to log the doc-id of the problem doc
    4. Edit just logs doc_ids to the console, around line 100 of importer.js console.log(row.doc._id);
    5. Get the remote couch2pg environment variable settings and export them into your profile terminal
    6. Create an ssh reverse tunnel from the postgres server to your laptop
    7. Run couch2pg locally so that you can see the doc-ids on console till it fails
    8. From the ids printed on console try loading the docs in the couchdb web access(Futon or Fauxton), the problem doc is usually big and won’t load
    9. This will help you identify the problem doc
    10. Curl the document to your pc and back it up
    11. Back up the document for further analysis
    12. Delete the document using curl curl --head "<HOST>/<DB>/<DOC_ID>" This returns something like
    HTTP/1.1 200 OK
    @@ -315,7 +315,8 @@
     

    The ETag is the rev. Delete document with curl: curl -X DELETE "<HOST>/<DB>/<DOC_ID>?rev=<THE_REV>"


    Hosting > 3.x > AWS Hosting

    Hosting the CHT on Amazon EC2

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/guides/database/couchdb-authentication/index.html b/apps/guides/database/couchdb-authentication/index.html index 5d33ef54cf..342a6b592e 100644 --- a/apps/guides/database/couchdb-authentication/index.html +++ b/apps/guides/database/couchdb-authentication/index.html @@ -1,9 +1,9 @@ -CouchDB Authentication | Community Health Toolkit +CouchDB Authentication | Community Health Toolkit

    CouchDB Authentication

    Invalidating Sessions

    To invalidate a session in couchdb, there are two options:

    1. Change the session signing certificate on the server
    2. Change the password and/or salt for the user whose session should be invalidated

    There are drawbacks to note with each. Option 1 will invalidate all sessions; option 2 will invalidate all sessions for that user, and also their password.

    Because of the nature of couch’s session management, there is no way to see a list of active/open sessions. Invalidating a specific session key could be achieved by denying a cookie value in e.g. nginx or API, but this is unlikely to be of practical value.

    -

    Last modified 22.11.2022: Remove sensitive words (febd5442)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/database/database-conflicts/index.html b/apps/guides/database/database-conflicts/index.html index 80f94bfc90..c507a0f4ee 100644 --- a/apps/guides/database/database-conflicts/index.html +++ b/apps/guides/database/database-conflicts/index.html @@ -1,9 +1,9 @@ -Database Conflicts | Community Health Toolkit +Database Conflicts | Community Health Toolkit

    Database Conflicts

    How to handle conflicts with CouchDB documents

    Conflicts are a natural and unavoidable part of working in a distributed system.

    Conflicts occur when one client (eg PouchDB) attempts to replicate to another (eg CouchDB), and the document that the first has does not have the same tree of changes that the second one has.

    An example

    To make it clear what’s happening, let’s walk through an example. If you already understand conflicts feel free to skip this section.

    Let’s say you register a pregnancy in the UI. And then you notice that you got the LMP wrong, so you hit edit and quickly make the change.

    Meanwhile, sentinel notices that you registers a pregnancy, and before you have a chance to change the LMP, sets up a bunch of recurring messages, editing the document.

    Sentinel has now made a change to the first version of your document, and you’re trying to also make a change to the first version. These conflict.

    How to manage conflicts

    If a server you manage has a conflict, you should do the following:

    Identify why conflicts are occurring

    Take a look at the conflicts view, at https://yourserver/medic/_design/medic-conflicts/_view/conflicts. Each entry in that view looks like this:

    Database Conflicts

    How to handle conflicts with CouchDB documents

    Conflicts are a natural and unavoidable part of working in a distributed system.

    Conflicts occur when one client (eg PouchDB) attempts to replicate to another (eg CouchDB), and the document that the first has does not have the same tree of changes that the second one has.

    An example

    To make it clear what’s happening, let’s walk through an example. If you already understand conflicts feel free to skip this section.

    Let’s say you register a pregnancy in the UI. And then you notice that you got the LMP wrong, so you hit edit and quickly make the change.

    Meanwhile, sentinel notices that you registers a pregnancy, and before you have a chance to change the LMP, sets up a bunch of recurring messages, editing the document.

    Sentinel has now made a change to the first version of your document, and you’re trying to also make a change to the first version. These conflict.

    How to manage conflicts

    If a server you manage has a conflict, you should do the following:

    Identify why conflicts are occurring

    Take a look at the conflicts view, at https://yourserver/medic/_design/medic-conflicts/_view/conflicts. Each entry in that view looks like this:

    {
         "id": "1B7922A6-A6D6-C956-BBAE-DE5EB5A2E6C8",
         "key": [
             "1-82d9a42305a79e403d9eca6a9a9daae9"
    @@ -394,7 +394,8 @@
       ]
     }
     

    In this example, there are two references to 7FADDF76-55E4-4E50-9444-5E468E61EA83, both in the 1B7922A6-A6D6-C956-BBAE-DE5EB5A2E6C8 document, one at fields.inputs.contact._id and one at fields.patient_id.

    If you get stuck, feel free to raise a question in the CHT forum and people can help you out.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/database/index.html b/apps/guides/database/index.html index 75ce71e1cc..6b12af0079 100644 --- a/apps/guides/database/index.html +++ b/apps/guides/database/index.html @@ -1,9 +1,9 @@ -Managing Databases | Community Health Toolkit +Managing Databases | Community Health Toolkit

    Managing Databases

    Managing databases used by CHT applications

    CouchDB

    The CHT has a range of CouchDB databases for storing different types of data. By default, databases all start with the prefix “medic”.

    medic

    The main database, used to store all contact and report data. Data access is protected by API to provide protection on a per document basis.

    See Also: Database schema conventions

    medic-sentinel

    Stores the _local/sentinel-meta-data document which stores the sequence of the last processed change in the medic db. This is used so Sentinel can resume from where it left off and process all changes in order.

    This database also stores metadata about the documents in the “medic” database, such as when it was received and which Sentinel transitions have executed on this doc. The UUID of the metadata doc is the same as the UUID of the “medic” doc with “-info” appended at the end.

    For example:

    Managing Databases

    Managing databases used by CHT applications

    CouchDB

    The CHT has a range of CouchDB databases for storing different types of data. By default, databases all start with the prefix “medic”.

    medic

    The main database, used to store all contact and report data. Data access is protected by API to provide protection on a per document basis.

    See Also: Database schema conventions

    medic-sentinel

    Stores the _local/sentinel-meta-data document which stores the sequence of the last processed change in the medic db. This is used so Sentinel can resume from where it left off and process all changes in order.

    This database also stores metadata about the documents in the “medic” database, such as when it was received and which Sentinel transitions have executed on this doc. The UUID of the metadata doc is the same as the UUID of the “medic” doc with “-info” appended at the end.

    For example:

    {
       "_id": "f8cc78d0-31a7-44e8-8073-176adcc0dc7b-info",
       "_rev": "2-6e08756f62fa0595d87a3f50777758dc",
       "type": "info",
    @@ -309,7 +309,8 @@
       "initial_replication_date": "2018-08-14T10:02:13.625Z"
     }
     

    medic-logs

    Stores a record of when a user last attempted to replicate and how many docs they have access to. This can be useful when trying to diagnose issues with users getting too much access, or being unable to complete replication because their access is too broad.

    medic-vault

    Stores CHT credentials for authenticating with third party services. These credentials are encrypted for safety, and can only be updated using the Credentials API.

    medic-user-{username}-meta

    Used for documents which are only relevant to a single user, including:

    • “feedback”. Errors and exceptions caught in the browser, and user initiated feedback. Support staff must monitor these docs to detect any errors that are occurring on the client device.
    • “read”. Records that a document has been opened in the browser so it can be marked as read in the UI.
    • “telemetry”. Aggregate telemetry information for performance and usage metrics analysis.

      See Also: User telemetry

    medic-users-meta

    To make it easier to perform analysis of all the docs in each user’s “medic-user-{username}-meta” database, Sentinel replicates all the “feedback” and “telemetry” docs into this single database . This is used for reporting, monitoring, and usage analytics. The “feedback” and “telemetry” docs are deleted from the user’s “medic-user-{username}-meta” database after they have successfully been replicated to “medic-users-meta”.

    Replication to this database can be enabled via configuration from 3.5.0 and works without configuration from 3.10.0.

    _users

    This is the standard CouchDB database used to configure user authentication and authorization.

    medic-logs

    Stores meta data about the user including when they last connected to the server, and how many documents they are allowed to replicate. This can be useful for checking for connection issues and misconfigured users.

    PouchDB

    Used to store documents on the client device to allow for Offline-First access. Bidirectional replication is done on the “medic” and “medic-user-{username}-meta” databases. The “medic” database is only partially replicated so the user stores only a subset of the entire CouchDB database for performance and security reasons.

    See Also: CouchDB replication

    PostgreSQL

    Used to store data for performant analytical queries such as impact and monitoring dashboards. The CHT uses medic-couch2pg to handle replication of docs from “medic”, “medic-sentinel”, and “medic-users-meta” databases into Postgres.

    See Also: Data Flows for Analytics


    Contact Muting in SQL queries

    How to write SQL queries excluding muted contacts correctly

    Fixing couch2pg Memory Errors

    Dealing with out-of-memory errors in couch2pg

    CouchDB Authentication

    Invalidating Sessions

    Database Conflicts

    How to handle conflicts with CouchDB documents

    Querying Apdex Telemetry Data

    How to use SQL queries to view Apdex scores

    Connecting to RDBMS from MacOS

    How to connect to the PostgreSQL RDBMS server from MacOS

    Connecting to RDBMS from Windows

    How to connect to the PostgreSQL RDBMS server from Windows

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/database/index.xml b/apps/guides/database/index.xml index f8c3ed1717..04ed5da152 100644 --- a/apps/guides/database/index.xml +++ b/apps/guides/database/index.xml @@ -1,489 +1,14 @@ -Community Health Toolkit – Managing Databaseshttps://docs.communityhealthtoolkit.org/apps/guides/database/Recent content in Managing Databases on Community Health ToolkitHugo -- gohugo.ioenApps: Contact Muting in SQL querieshttps://docs.communityhealthtoolkit.org/apps/guides/database/muting_in_dashboards/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/database/muting_in_dashboards/ -<p>When a contact gets muted, two of <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/transitions/#muting">many things</a> happen:</p> -<ul> -<li>The target contact and all of its descendants have a <code>muted</code> property set equal to the date they were muted</li> -<li>an entry is added to the contact&rsquo;s <code>muting_history</code> in sentinel&rsquo;s info docs</li> -</ul> -<p>When building dashboards on Superset, Klipfolio, or other data visualization platforms, you might need to exclude these muted contacts from the visualized data. An easy way to do this is to check the contact&rsquo;s <code>muted</code> property which when present has the date value of when the contact was muted and when absent means that the contact is not muted. This works when you are only interested in seeing the latest data but it gets complicated when you want to look at a contact&rsquo;s mute state from a certain period in the past.</p> -<p>For example, if a contact was muted in February and unmuted in May; If you check the contact&rsquo;s mute state in March from June, you&rsquo;d find that the contact would not have the muted property as it would have been removed during the <code>unmute</code> in May. This is where the <code>muting_history</code> comes in. A contact&rsquo;s <code>muting_history</code> contains all mute and unmute events stored in a JSON array. An example of the mute/unmute entries is shown below:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_rev&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;3-01ecfdd2958baeaf16fc621c5622f4a9&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;info&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;doc_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;doc-id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;transitions&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;muting_history&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;date&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2021-04-07T13:41:09.769Z&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;muted&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;report_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;_id&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;date&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2021-06-07T13:41:09.769Z&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;muted&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;report_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;_id&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;latest_replication_date&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2020-10-30T15:46:45.482Z&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;initial_replication_date&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2020-10-30T15:46:45.482Z&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>If you extract this data into a separate table you can get a timeline that you can use to check if the contact was muted at a certain point in the past. An example of this approach is shown below where the data is first extracted using the query:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">SELECT</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">contact_muting_history</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact_uuid</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87">date</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">AS</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">muted_on</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">muted</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">report_id</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">FROM</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000;font-weight:bold">(</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">SELECT</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">doc</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#ce5c00;font-weight:bold">-&gt;&gt;</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#39;doc_id&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">AS</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">contact_uuid</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">couchdb</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">doc</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#ce5c00;font-weight:bold">-&gt;&gt;</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#39;muting_history&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">AS</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">muting_history</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">FROM</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">couchdb</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">WHERE</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">doc</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#ce5c00;font-weight:bold">-&gt;&gt;</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#39;type&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000;font-weight:bold">::</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87">text</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#39;info&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">AND</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">doc</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#ce5c00;font-weight:bold">-&gt;&gt;</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#39;muting_history&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">IS</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">NOT</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">NULL</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000;font-weight:bold">)</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">contact_muting_history</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">CROSS</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">JOIN</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">LATERAL</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">json_populate_recordset</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">null</span><span style="color:#000;font-weight:bold">::</span><span style="color:#000">record</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">contact_muting_history</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">muting_history</span><span style="color:#000;font-weight:bold">::</span><span style="color:#000">json</span><span style="color:#000;font-weight:bold">)</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">AS</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87">date</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87">text</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">muted</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">bool</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">report_id</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">uuid</span><span style="color:#000;font-weight:bold">);</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span></code></pre></div><p>The query above will give you a result set like the one below:</p> -<table> -<thead> -<tr> -<th>contact_uuid</th> -<th>muted_on</th> -<th>muted</th> -<th>report_id</th> -</tr> -</thead> -<tbody> -<tr> -<td>1</td> -<td>2021-06-07T13:41:11.119Z</td> -<td>True</td> -<td>*****</td> -</tr> -<tr> -<td>2</td> -<td>2020-12-24T18:02:53.190Z</td> -<td>True</td> -<td>*****</td> -</tr> -<tr> -<td>3</td> -<td>2021-01-24T17:36:31.917Z</td> -<td>True</td> -<td>*****</td> -</tr> -<tr> -<td>3</td> -<td>2021-04-15T16:56:05.984Z</td> -<td>False</td> -<td>*****</td> -</tr> -</tbody> -</table> -<p>You can query the table above (<code>mute_timeline</code>) to check if the contact was muted in a certain period in time as shown below:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">SELECT</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">contact_uuid</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">IS</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">NOT</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">NULL</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">AS</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">muted</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">FROM</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">mute_timeline</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">WHERE</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">contact_uuid</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#ce5c00;font-weight:bold">*****</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">AND</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">date_trunc</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;day&#39;</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">muted_on</span><span style="color:#000;font-weight:bold">)</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#ce5c00;font-weight:bold">&lt;=</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">date_trunc</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;day&#39;</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">EXAMPLE_DATE</span><span style="color:#000;font-weight:bold">)</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">AND</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">unmuted_on</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#ce5c00;font-weight:bold">&gt;=</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">date_trunc</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;day&#39;</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">EXAMPLE_DATE</span><span style="color:#000;font-weight:bold">)</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#39;1 day&#39;</span><span style="color:#000;font-weight:bold">::</span><span style="color:#204a87">interval</span><span style="color:#000;font-weight:bold">)</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span></code></pre></div><p>This query checks if a record exists for this contact where they were muted earlier or on <code>EXAMPLE_DATE</code> and unmuted on or after the <code>EXAMPLE_DATE</code>. For the earlier example where the contact was muted in February and unmuted in May if we pass February, March, or April as <code>EXAMPLE_DATE</code>, we find a record because our <code>unmuted_on</code> is always greater than <code>EXAMPLE_DATE</code>. If we pass May going forward, we find no records that match our condition.</p>Apps: Fixing couch2pg Memory Errorshttps://docs.communityhealthtoolkit.org/apps/guides/database/couch2pg-oom-errors/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/database/couch2pg-oom-errors/ -<p>Some times when couch2pg is replicating documents to postgres, it encounters very large info docs that are larger than the memory allocation of the document sync array and causes out-of-memory errors. -To fix this, we need to delete this document so that couch2pg can proceed. Below are steps to follow to achieve this.</p> -<ol> -<li>Reduce the size of the replicated docs to a value of say 4 in the couch2pg.conf file so that you can get within the range of the failing document.</li> -<li>Clone the existing <a href="https://github.com/medic/couch2pg">couch2pg</a> repo so that you can run couch2pg locally</li> -<li>Edit the file <code>lib/importer.js</code> in the couch2pg source code to be able to log the doc-id of the problem doc</li> -<li>Edit just logs doc_ids to the console, around line 100 of importer.js <code>console.log(row.doc._id);</code></li> -<li>Get the remote couch2pg environment variable settings and export them into your profile terminal</li> -<li>Create an ssh reverse tunnel from the postgres server to your laptop</li> -<li>Run couch2pg locally so that you can see the doc-ids on console till it fails</li> -<li>From the ids printed on console try loading the docs in the couchdb web access(Futon or Fauxton), the problem doc is usually big and won&rsquo;t load</li> -<li>This will help you identify the problem doc</li> -<li>Curl the document to your pc and back it up</li> -<li>Back up the document for further analysis</li> -<li>Delete the document using curl -<code>curl --head &quot;&lt;HOST&gt;/&lt;DB&gt;/&lt;DOC_ID&gt;&quot;</code> -This returns something like</li> -</ol> -<pre tabindex="0"><code>HTTP/1.1 200 OK -Cache-Control: must-revalidate -Content-Length: 307 -Content-Type: application/json -Date: Tue, 25 Jun 2019 11:58:29 GMT -ETag: &#34;2-6beeb38da9b096bacfe2fa769e5171be&#34; -Server: CouchDB/2.3.1 (Erlang OTP/21) -X-Couch-Request-ID: e4aa7a8696 -X-CouchDB-Body-Time: 0 -</code></pre><p>The ETag is the rev. Delete document with curl: <code>curl -X DELETE &quot;&lt;HOST&gt;/&lt;DB&gt;/&lt;DOC_ID&gt;?rev=&lt;THE_REV&gt;&quot;</code></p>Apps: CouchDB Authenticationhttps://docs.communityhealthtoolkit.org/apps/guides/database/couchdb-authentication/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/database/couchdb-authentication/ -<h3 id="to-invalidate-a-session-in-couchdb-there-are-two-options">To invalidate a session in couchdb, there are two options:</h3> -<ol> -<li>Change the session signing certificate on the server</li> -<li>Change the password and/or salt for the user whose session should be invalidated</li> -</ol> -<p>There are drawbacks to note with each. Option <strong>1</strong> will invalidate <em>all</em> sessions; option <strong>2</strong> will invalidate all sessions <em>for that user</em>, and also their password.</p> -<p>Because of the nature of couch&rsquo;s session management, there is no way to see a list of active/open sessions. Invalidating a specific session key could be achieved by denying a cookie value in e.g. nginx or API, but this is unlikely to be of practical value.</p>Apps: Database Conflictshttps://docs.communityhealthtoolkit.org/apps/guides/database/database-conflicts/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/database/database-conflicts/ -<p>Conflicts are a natural and unavoidable part of working in a distributed system.</p> -<p>Conflicts occur when one client (eg PouchDB) attempts to replicate to another (eg CouchDB), and the document that the first has does not have the same tree of changes that the second one has.</p> -<h2 id="an-example">An example</h2> -<p>To make it clear what&rsquo;s happening, let&rsquo;s walk through an example. If you already understand conflicts feel free to skip this section.</p> -<p>Let&rsquo;s say you register a pregnancy in the UI. And then you notice that you got the LMP wrong, so you hit edit and quickly make the change.</p> -<p>Meanwhile, sentinel notices that you registers a pregnancy, and before you have a chance to change the LMP, sets up a bunch of recurring messages, editing the document.</p> -<p>Sentinel has now made a change to the first version of your document, and you&rsquo;re trying to also make a change to the first version. These conflict.</p> -<h2 id="how-to-manage-conflicts">How to manage conflicts</h2> -<p>If a server you manage has a conflict, you should do the following:</p> -<h3 id="identify-why-conflicts-are-occurring">Identify why conflicts are occurring</h3> -<p>Take a look at the conflicts view, at <code>https://yourserver/medic/_design/medic-conflicts/_view/conflicts</code>. Each entry in that view looks like this:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;1B7922A6-A6D6-C956-BBAE-DE5EB5A2E6C8&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;1-82d9a42305a79e403d9eca6a9a9daae9&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;value&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">null</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span><span style="color:#a40000">,</span> -</span></span></code></pre></div><p>The <code>id</code> is the <code>_id</code> of the conflicting document, and the <code>key</code> is a list of conflicting <code>_rev</code>s.</p> -<p>For each conflicting document, download that document, as well as all the <code>_rev</code>s indicated in the <code>key</code> above. To download a document with a specific <code>_rev</code>, pass the <code>rev</code> parameter.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>curl <span style="color:#4e9a06">&#39;https://yourserver/medic/1B7922A6-A6D6-C956-BBAE-DE5EB5A2E6C8&#39;</span> &gt; doc.json -</span></span><span style="display:flex;"><span>curl <span style="color:#4e9a06">&#39;https://yourserver/medic/1B7922A6-A6D6-C956-BBAE-DE5EB5A2E6C8?rev=1-82d9a42305a79e403d9eca6a9a9daae9&#39;</span> &gt; doc-conflict1.json -</span></span></code></pre></div><p>Now you have all versions of the document, you can diff them and try to determine what went wrong.</p> -<p>A common problem, for example, might be sentinel hitting a document really quickly between you creating it and editing it.</p> -<p>A less common problem that requires some special attention, is UUID collisions (see below).</p> -<p>If you get stuck feel free to escalate to a developer, who can take a look.</p> -<h3 id="if-appropriate-raise-a-bug">If appropriate, raise a bug</h3> -<p>If you determine (or just suspect) that the problem could be in our code or data structures, feel free to raise a bug to development. For example, historically read status has been stored against the document, which can easily cause conflicts if you create a document and then instantly view it with sentinel processing in the background.</p> -<p>While some conflicts are inevitable, we want to architect away from them as much as possible.</p> -<h3 id="regardless-resolve-the-conflicts">Regardless, resolve the conflicts</h3> -<p>Now that you&rsquo;ve diagnosed the problem, and perhaps reported a bug, you should resolve the conflict.</p> -<p>This is <strong>extremely</strong> important. Conflicts cause saved changes to not appear against documents silently, and could cause important document changes (eg fixing someone&rsquo;s EDD) to not occur.</p> -<p>For a document to no longer be conflicted, there must only be one active <code>_rev</code>. You would do this by picking one rev and updating it with the changes you want to make, and then updating the others with the <code>_deleted: true</code> property.</p> -<p>You can tell that a document is no longer conflicted if they don&rsquo;t appear in the view, or if when you request the document with <code>?conflicts=true</code> the <code>_conflicts</code> property either doesn&rsquo;t appear or is empty:</p> -<pre tabindex="0"><code>https://yourserver/medic/yourdocid?conflicts=true -{ -&#34;_id&#34;: &#34;yourdocid&#34;, -&#34;_rev&#34;: &#34;2-the-current-rev&#34; -&#34;_conflicts&#34;: [ -&#34;2-a-conflicting-rev&#34; -] -} -</code></pre><p>In the above example, <code>yourdocid</code> has two revisions that conflict with each other. Here you would need to update one of the revs (it doesn&rsquo;t matter which) with the other&rsquo;s changes, then delete the other rev. You would then see:</p> -<pre tabindex="0"><code>https://yourserver/medic/yourdocid?conflicts=true -{ -&#34;_id&#34;: &#34;yourdocid&#34;, -&#34;_rev&#34;: &#34;3-the-new-rev&#34; -} -</code></pre><h4 id="a-trivial-example">A trivial example</h4> -<p>Let&rsquo;s say that you have two documents, and the diff between them looks something like this:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-diff" data-lang="diff"><span style="display:flex;"><span><span style="color:#000080;font-weight:bold">=================================================================== -</span></span></span><span style="display:flex;"><span><span style="color:#000080;font-weight:bold"></span><span style="color:#a40000">--- 277533E3-A41B-3C46-909F-BCA038197C1E___2-2fff60be1557fdfef9915aa09e1b5119.json -</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#00a000">+++ 277533E3-A41B-3C46-909F-BCA038197C1E___2-d2a7186380b72306d75cd64b64402575.json -</span></span></span><span style="display:flex;"><span><span style="color:#00a000"></span><span style="color:#800080;font-weight:bold">@@ -1,7 +1,7 @@ -</span></span></span><span style="display:flex;"><span><span style="color:#800080;font-weight:bold"></span> { -</span></span><span style="display:flex;"><span> &#34;_id&#34;: &#34;277533E3-A41B-3C46-909F-BCA038197C1E&#34;, -</span></span><span style="display:flex;"><span><span style="color:#a40000">- &#34;_rev&#34;: &#34;2-2fff60be1557fdfef9915aa09e1b5119&#34;, -</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#00a000">+ &#34;_rev&#34;: &#34;2-d2a7186380b72306d75cd64b64402575&#34;, -</span></span></span><span style="display:flex;"><span><span style="color:#00a000"></span> &#34;data&#34;: &#34;some shared data&#34;, -</span></span><span style="display:flex;"><span> &#34;read&#34;: [ -</span></span><span style="display:flex;"><span><span style="color:#a40000">- &#34;some_user&#34; -</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#00a000">+ &#34;admin&#34; -</span></span></span><span style="display:flex;"><span><span style="color:#00a000"></span> ] -</span></span><span style="display:flex;"><span> } -</span></span></code></pre></div><p>The problem here is clear: <code>some_user</code> and <code>admin</code> read the document at the same time. To solve this, we could add <code>some_user</code> to the revision with <code>admin</code> already in it, and then delete the <code>some_user</code> revision:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;277533E3-A41B-3C46-909F-BCA038197C1E&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_rev&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2-d2a7186380b72306d75cd64b64402575&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;data&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;some shared data&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;read&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;admin&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;some_user&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;277533E3-A41B-3C46-909F-BCA038197C1E&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_rev&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2-2fff60be1557fdfef9915aa09e1b5119&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_deleted&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;data&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;some shared data&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;read&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;some_user&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h4 id="resolving-uuid-collisions">Resolving UUID collisions</h4> -<p>A UUID collision is a rare event where two clients (eg two android phones running our application) generate the same UUID ID for two completely different documents.</p> -<p>You can tell when your conflict is a UUID collision as there is no common root between the two conflicting versions. For example, one might be of type person and one might of type data_record.</p> -<p>These situations are more complicated, and require that you essentially recreate all conflicting versions as new documents, and fix any linkages that exist in the database.</p> -<p>Let&rsquo;s say you find the following situation:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-diff" data-lang="diff"><span style="display:flex;"><span>{ -</span></span><span style="display:flex;"><span> &#34;_id&#34;: &#34;7FADDF76-55E4-4E50-9444-5E468E61EA83&#34; -</span></span><span style="display:flex;"><span><span style="color:#a40000">- &#34;_rev&#34;: &#34;1-e4da228c29dc4ebc8b156967bbf48bd1&#34;, -</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#00a000">+ &#34;_rev&#34;: &#34;1-ce40d1dc470643e2b9be9368ea9ff240&#34;, -</span></span></span><span style="display:flex;"><span><span style="color:#00a000"></span><span style="color:#a40000">- &#34;type&#34;: &#34;person&#34; -</span></span></span><span style="display:flex;"><span><span style="color:#a40000"></span><span style="color:#00a000">+ &#34;type&#34;: &#34;data_record&#34; -</span></span></span><span style="display:flex;"><span><span style="color:#00a000"></span>&lt;snip a bunch more stuff that doesn&#39;t relate to each other&gt; -</span></span><span style="display:flex;"><span>} -</span></span></code></pre></div><p>You will want to do four things:</p> -<ul> -<li>Download the <code>_rev</code> for the <code>person</code> and create a new document, with a new uuid, for that document (you can do this by uploadig the document without an <code>_id</code> or <code>_rev</code> parameter and let CouchDB generate them for you)</li> -<li>Do the same for the <code>data_record</code> version</li> -<li>Delete the main conflicting document <code>7FADDF76-55E4-4E50-9444-5E468E61EA83</code></li> -</ul> -<p>And finally, find any references to <code>7FADDF76-55E4-4E50-9444-5E468E61EA83</code>, work out which doc they were <em>supposed</em> to point to, and then edit those UUIDs to be the correct UUID <code>_id</code> from the docs you created above.</p> -<p>Because this should be a rare event and a generic view would be enormous, we do not ship a view that helps you find this out.</p> -<p>However, you can create your own view! You&rsquo;re going to want to create a DDOC specifically for this view. You can follow the following template to create what you want:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;_design/docs-by-reference&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;views&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;docs-by-reference&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;map&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;function(doc) {\n var KEYS = [];\n\n // TODO: consider switching this around to list allowed doc types\n if (doc._id.match(/-info$/) ||\n doc._id.match(/^_local/)) {\n return;\n }\n\n var goDeeper = function(obj, path) {\n Object.keys(obj).forEach(function(key) {\n if (typeof obj[key] === &#39;string&#39; &amp;&amp;\n KEYS.indexOf(obj[key]) !== -1) {\n emit(obj[key], path + &#39;.&#39; + key);\n }\n\n if (obj[key] &amp;&amp; typeof obj[key] === &#39;object&#39;) {\n goDeeper(obj[key], path + &#39;.&#39; + key);\n }\n });\n };\n\n goDeeper(doc, doc._id);\n}&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>In this, add any IDs you want to be found in the <code>KEYS</code> variable at the top of the function. So in our case, we would change <code>KEYS</code> to look like this:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">var</span> <span style="color:#000">KEYS</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;7FADDF76-55E4-4E50-9444-5E468E61EA83&#39;</span><span style="color:#000;font-weight:bold">]</span> -</span></span></code></pre></div><p>Upload this document to CouchDB (do not just add the view to an existing DDOC, as you will force all views on that DDOC to regenerate) and then warm up the views by querying it once (it may take a long time to run).</p> -<p>Once it is complete you can query the view again to return a list of documents that reference the ids you hard-coded above.</p> -<p>This will help you to identify which documents are affected by this change. Usually the only change needed is to change the ID located to the new ones you generated.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;total_rows&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;offset&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rows&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;1B7922A6-A6D6-C956-BBAE-DE5EB5A2E6C8&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;7FADDF76-55E4-4E50-9444-5E468E61EA83&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;value&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;1B7922A6-A6D6-C956-BBAE-DE5EB5A2E6C8.fields.inputs.contact._id&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;1B7922A6-A6D6-C956-BBAE-DE5EB5A2E6C8&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;7FADDF76-55E4-4E50-9444-5E468E61EA83&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;value&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;1B7922A6-A6D6-C956-BBAE-DE5EB5A2E6C8.fields.patient_id&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>In this example, there are two references to <code>7FADDF76-55E4-4E50-9444-5E468E61EA83</code>, both in the <code>1B7922A6-A6D6-C956-BBAE-DE5EB5A2E6C8</code> document, one at <code>fields.inputs.contact._id</code> and one at <code>fields.patient_id</code>.</p> -<p>If you get stuck, feel free to raise a question in the <a href="https://forum.communityhealthtoolkit.org">CHT forum</a> and people can help you out.</p>Apps: Querying Apdex Telemetry Datahttps://docs.communityhealthtoolkit.org/apps/guides/database/querying_apdex_telemetry/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/database/querying_apdex_telemetry/ -<p>Added in <code>4.7.0</code>, CHT now records the Apdex (Application Performance Index) that is an open standard for measuring performance of software applications.</p> -<p>Since Apdex is part of the <a href="https://docs.communityhealthtoolkit.org/apps/guides/performance/telemetry/">telemetry</a> system, it is possible to view Apdex data directly from CouchDB. However, it is more useful when aggregated across many users, interactions, and/or days. With this in mind, it is typically easier to query the data using SQL from an <a href="https://docs.communityhealthtoolkit.org/core/overview/data-flows-for-analytics/">analytics database</a>.</p> -<p>An example of an SQL to view the Apdex score:</p> -<pre tabindex="0"><code>WITH apdex_telemetry_data AS ( -SELECT -substring(metric from &#39;^(.*):apdex:&#39;) AS event_category, -CASE -WHEN metric LIKE &#39;%:satisfied&#39; THEN &#39;satisfied&#39; -WHEN metric LIKE &#39;%:tolerable&#39; THEN &#39;tolerable&#39; -WHEN metric LIKE &#39;%:frustrated&#39; THEN &#39;frustrated&#39; -END AS event_type, -SUM(count) AS event_count -FROM -useview_telemetry_metrics -WHERE metric LIKE &#39;%:apdex:%&#39; -GROUP BY event_category, event_type -), -apdex_scores AS ( -SELECT -event_category, -SUM(CASE WHEN event_type = &#39;satisfied&#39; THEN event_count ELSE 0 END) AS satisfied_count, -SUM(CASE WHEN event_type = &#39;tolerable&#39; THEN event_count ELSE 0 END) AS tolerable_count, -SUM(CASE WHEN event_type = &#39;frustrated&#39; THEN event_count ELSE 0 END) AS frustrated_count, -SUM(event_count) AS total_event_count -FROM apdex_telemetry_data -GROUP BY event_category -) -SELECT -event_category, -satisfied_count, -tolerable_count, -frustrated_count, -ROUND(((satisfied_count + (tolerable_count / 2.0)) / total_event_count)::numeric, 2) AS apdex_score -FROM apdex_scores -ORDER BY apdex_score asc; -</code></pre><p>The SQL query above calculates the Apdex scores for various events recorded in the <code>useview_telemetry_metrics</code> table, providing insights into the performance of different aspects of the CHT. In some cases, it is helpful to visualize the number of occurrences of each metric in a date range:</p> -<pre tabindex="0"><code>WITH -constants (days, start_date, end_date) AS (VALUES (&lt;count_days_in_range&gt;, &lt;YYYY-MM-DD&gt;, &lt;YYYY-MM-DD&gt;)), -apdex_metrics AS( -SELECT -substring(metric FROM &#39;^(.*):apdex:&#39;) AS metric, -SUM(COUNT) AS COUNT -FROM useview_telemetry_metrics, constants -WHERE -metric LIKE &#39;%:apdex:%&#39; -AND period_start BETWEEN constants.start_date::DATE AND constants.end_date::DATE -GROUP BY metric -), -apdex_result as ( -SELECT -metric, -CASE -WHEN apdex_metrics.metric = &#39;contact_list:load&#39; THEN &lt;count_in_a_day&gt; * constants.days -WHEN apdex_metrics.metric = &#39;contact_list:query&#39; THEN &lt;count_in_a_day&gt; * constants.days -WHEN apdex_metrics.metric = &#39;contact_detail:&lt;contact_type&gt;:load&#39; THEN &lt;count_in_a_day&gt; * constants.days -WHEN apdex_metrics.metric = &#39;contact_detail:&lt;contact_type&gt;:load&#39; THEN &lt;count_in_a_day&gt; * constants.days -WHEN apdex_metrics.metric = &#39;contact_detail:&lt;contact_type&gt;:load&#39; THEN &lt;count_in_a_day&gt; * constants.days -WHEN apdex_metrics.metric = &#39;enketo:contacts:&lt;contact_form&gt;:add:render&#39; THEN &lt;count_in_a_day&gt; * constants.days -WHEN apdex_metrics.metric = &#39;enketo:contacts:&lt;contact_form&gt;:add:save&#39; THEN &lt;count_in_a_day&gt; * constants.days -WHEN apdex_metrics.metric = &#39;enketo:reports:&lt;app_form&gt;:add:render&#39; THEN &lt;count_in_a_day&gt; * constants.days -WHEN apdex_metrics.metric = &#39;enketo:reports:&lt;app_form&gt;:add:save&#39; THEN &lt;count_in_a_day&gt; * constants.days -WHEN apdex_metrics.metric = &#39;report_list:load&#39; THEN &lt;count_in_a_day&gt; * constants.days -WHEN apdex_metrics.metric = &#39;report_list:query&#39; THEN &lt;count_in_a_day&gt; * constants.days -WHEN apdex_metrics.metric = &#39;report_detail:&lt;app_form_id&gt;:load&#39; THEN &lt;count_in_a_day&gt; * constants.days -WHEN apdex_metrics.metric = &#39;tasks:load&#39; THEN &lt;count_in_a_day&gt; * constants.days -WHEN apdex_metrics.metric = &#39;tasks:refresh&#39; THEN &lt;count_in_a_day&gt; * constants.days -WHEN apdex_metrics.metric = &#39;enketo:tasks:&lt;app_form_id&gt;:add:render&#39; THEN &lt;count_in_a_day&gt; * constants.days -WHEN apdex_metrics.metric = &#39;enketo:tasks:&lt;app_form_id&gt;:add:save&#39; THEN &lt;count_in_a_day&gt; * constants.days -WHEN apdex_metrics.metric = &#39;message_list:load&#39; THEN &lt;count_in_a_day&gt; * constants.days -WHEN apdex_metrics.metric = &#39;messages_detail:load&#39; THEN &lt;count_in_a_day&gt; * constants.days -WHEN apdex_metrics.metric = &#39;analytics:targets:load&#39; THEN &lt;count_in_a_day&gt; * constants.days -WHEN apdex_metrics.metric = &#39;boot_time&#39; THEN &lt;count_in_a_day&gt; * constants.days -END AS expected_count, -SUM(apdex_metrics.count) AS actual_count -FROM apdex_metrics, constants -GROUP BY apdex_metrics.metric, constants.days -) -SELECT -apdex_result.metric, -apdex_result.expected_count, -apdex_result.actual_count, -CASE -WHEN apdex_result.actual_count &gt;= apdex_result.expected_count THEN &#39;TRUE&#39; ELSE &#39;FALSE&#39; -END AS meet_expectation -FROM apdex_result -ORDER BY apdex_result.metric ASC -</code></pre><p>Such queries are instrumental in identifying areas of the CHT that may require performance improvements by highlighting how different parts of the application meet user expectations in terms of load times and Apdex score.</p>Apps: Connecting to RDBMS from MacOShttps://docs.communityhealthtoolkit.org/apps/guides/database/rdbms-from-mac/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/database/rdbms-from-mac/ -<p>Follow these steps on a Mac to generate your public/private keys and access the PostgreSQL server.</p> -<h2 id="access-terminal">Access Terminal</h2> -<p>Terminal (Terminal.app) is the terminal emulator included in the macOS operating system. You can use this application to generate your SSH key.</p> -<ol> -<li>Open a new <strong>Finder</strong> window</li> -<li>Navigate to the <strong>Applications</strong> folder</li> -<li>Navigate to the <strong>Utilities</strong> folder</li> -<li>Open the <strong>Terminal</strong> app</li> -</ol> -<p><img src="terminal.png" alt="Terminal"></p> -<h2 id="generate-key">Generate Key</h2> -<p>From Terminal, follow these instructions (see screenshot below):</p> -<ol> -<li>Type: <code>ssh-keygen -t rsa</code></li> -<li>Hit return to use the default file / location</li> -<li>Enter a <em>passphrase</em></li> -<li>Enter your <em>passphrase</em> again</li> -<li>Type: <code>cat ~/.ssh/id_rsa.pub</code></li> -</ol> -<p>In the screenshot below:</p> -<ul> -<li><code>(a)</code> is the location and filename of your <em><strong>private</strong></em> key</li> -<li><code>(b)</code> is the location and filename of your <em><strong>public</strong></em> key</li> -<li><code>(c)</code> are the contents of your <em><strong>public</strong></em> key</li> -</ul> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -You will need to provide the contents of <code>(c)</code> to your Medic contact or RDBMS administrator. It should start with <code>ssh-rsa</code> and end with something that looks like an email address. -</div> -<p><img src="ssh-commands.png" alt="SSH Commands"></p> -<h2 id="connect-to-postgresql">Connect to PostgreSQL</h2> -<p>Copy your public key and provide it to your Medic contact or RDBMS administrator. Your public key is not sensitive and can be shared over slack, github, etc&hellip;</p> -<p>Once the RDBMS administrator has added your public SSH key on RDBMS, the administrator will provide you with login credentials to the SSH server as well as for PostgreSQL.</p> -<p>Verify you can successfully connect to to the SSH server with <code>ssh -i ~/.ssh/id_rsa &lt;user&gt;@&lt;rdbms host&gt; -p &lt;port&gt;</code>. If your setup is correct, you should login to the server and see the prompt of your terminal change to <code>&lt;user&gt;@rdbms:~$</code></p> -<p>You should be able to access PostgreSQL from a SQL client using the provided credentials. Some common SQL clients include: <a href="https://www.pgadmin.org/">pgAdmin</a>, <a href="https://dbeaver.io/">DBeaver</a>, <a href="https://eggerapps.at/postico/">Postico</a>.</p> -<p>From your SQL client, use the settings mentioned below to connect. Be sure to select the <em><strong>Private Key</strong></em> that you generated above.</p> -<table> -<thead> -<tr> -<th>Field</th> -<th>Value</th> -</tr> -</thead> -<tbody> -<tr> -<td>Host</td> -<td><code>localhost</code></td> -</tr> -<tr> -<td>Host Port</td> -<td><code>5432</code></td> -</tr> -<tr> -<td>User</td> -<td><code>&lt;provided by Medic&gt;</code></td> -</tr> -<tr> -<td>Password</td> -<td><code>&lt;provided by Medic&gt;</code></td> -</tr> -<tr> -<td>Database</td> -<td><code>&lt;provided by Medic&gt;</code></td> -</tr> -<tr> -<td>SSH Host</td> -<td><code>rdbms.dev.medicmobile.org</code></td> -</tr> -<tr> -<td>SSH Port</td> -<td><code>33696</code></td> -</tr> -<tr> -<td>SSH User</td> -<td><code>&lt;provided by Medic&gt;</code></td> -</tr> -<tr> -<td>SSH Password</td> -<td>N/A - Use Private Key</td> -</tr> -<tr> -<td>Private Key</td> -<td>Choose the location of the private key generated above</td> -</tr> -</tbody> -</table> -<p><img src="connection-settings.png" alt="PG Connection Settings"></p>Apps: Connecting to RDBMS from Windowshttps://docs.communityhealthtoolkit.org/apps/guides/database/rdbms-from-windows/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/database/rdbms-from-windows/ -<p>Connecting to RDBMS, the PostgreSQL server, is pretty stratightforward in nix systems. In Windows there are a couple of things you need to do to get it up and running.</p> -<h2 id="ssh-key-generation-and-importing">SSH Key Generation and Importing</h2> -<ol> -<li> -<p>Download Puttygen from <a href="https://www.ssh.com/ssh/putty/download">here</a></p> -</li> -<li> -<p>Run Puttygen Go to Windows Start menu → All Programs → PuTTY→ PuTTYgen.</p> -</li> -<li> -<p>Create a new key pair for your computer.</p> -</li> -</ol> -<p><img src="puttygen-run-key-generate.png" alt="Putty-gen-key-pair"></p> -<ol start="4"> -<li>Convert the key generated from ssh2 format to openssh. Puttygen <a href="https://stackoverflow.com/questions/2224066/how-to-convert-ssh-keypairs-generated-using-puttygen-windows-into-key-pairs-us/2224204#2224204">supports this</a>.</li> -</ol> -<pre tabindex="0"><code>1. Open PuttyGen -2. Click Load -3. Load your private key -4. Go to Conversions-&gt;Export OpenSSH and export your private key -5. Copy your private key to ~/.ssh/id_dsa (or id_rsa). -6. Create the RFC 4716 version of the public key using ssh-keygen -ssh-keygen -e -f ~/.ssh/id_dsa &gt; ~/.ssh/id_dsa_com.pub -6. Convert the RFC 4716 version of the public key to the OpenSSH format: -ssh-keygen -i -f ~/.ssh/id_dsa_com.pub &gt; ~/.ssh/id_dsa.pub -</code></pre><ol start="5"> -<li>Import the file using PuTTYgen:</li> -</ol> -<p><img src="putty-gen-import.png" alt="puttty-import-key"></p> -<ol start="6"> -<li>Save it as PuTTY Private Key File .ppk:</li> -</ol> -<p><img src="putty-save-key.png" alt="puttty-save-key"></p> -<ol start="7"> -<li>Add the key (.ppk) to Pageant (PuTTY authentication agent):</li> -</ol> -<p><img src="putty-add-key.png" alt="puttty-add-key"></p> -<ol start="8"> -<li>Now you can connect to RDBMS using PuTTY:</li> -</ol> -<p><img src="puttty-connect.png" alt="puttty-connect"></p> -<p><img src="putty-connect-final.png" alt="puttty-connect"></p> -<p>If you are connecting to rdbms in order to use a PostgreSQL client. You may not need to tunnel the connection. The client can do it for you.</p> -<p><img src="rdbms_connect_1.png" alt="rdbms_1"></p> -<p><img src="rdbms_connect_2.png" alt="rdbms_2"></p> \ No newline at end of file +Managing Databases on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/guides/database/Recent content in Managing Databases on Community Health ToolkitHugo -- gohugo.ioenContact Muting in SQL querieshttps://docs.communityhealthtoolkit.org/apps/guides/database/muting_in_dashboards/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/database/muting_in_dashboards/When a contact gets muted, two of many things happen: +The target contact and all of its descendants have a muted property set equal to the date they were muted an entry is added to the contact&rsquo;s muting_history in sentinel&rsquo;s info docs When building dashboards on Superset, Klipfolio, or other data visualization platforms, you might need to exclude these muted contacts from the visualized data. An easy way to do this is to check the contact&rsquo;s muted property which when present has the date value of when the contact was muted and when absent means that the contact is not muted.Fixing couch2pg Memory Errorshttps://docs.communityhealthtoolkit.org/apps/guides/database/couch2pg-oom-errors/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/database/couch2pg-oom-errors/Some times when couch2pg is replicating documents to postgres, it encounters very large info docs that are larger than the memory allocation of the document sync array and causes out-of-memory errors. To fix this, we need to delete this document so that couch2pg can proceed. Below are steps to follow to achieve this. +Reduce the size of the replicated docs to a value of say 4 in the couch2pg.conf file so that you can get within the range of the failing document.CouchDB Authenticationhttps://docs.communityhealthtoolkit.org/apps/guides/database/couchdb-authentication/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/database/couchdb-authentication/To invalidate a session in couchdb, there are two options: Change the session signing certificate on the server Change the password and/or salt for the user whose session should be invalidated There are drawbacks to note with each. Option 1 will invalidate all sessions; option 2 will invalidate all sessions for that user, and also their password. +Because of the nature of couch&rsquo;s session management, there is no way to see a list of active/open sessions.Database Conflictshttps://docs.communityhealthtoolkit.org/apps/guides/database/database-conflicts/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/database/database-conflicts/Conflicts are a natural and unavoidable part of working in a distributed system. +Conflicts occur when one client (eg PouchDB) attempts to replicate to another (eg CouchDB), and the document that the first has does not have the same tree of changes that the second one has. +An example To make it clear what&rsquo;s happening, let&rsquo;s walk through an example. If you already understand conflicts feel free to skip this section.Querying Apdex Telemetry Datahttps://docs.communityhealthtoolkit.org/apps/guides/database/querying_apdex_telemetry/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/database/querying_apdex_telemetry/Added in 4.7.0, CHT now records the Apdex (Application Performance Index) that is an open standard for measuring performance of software applications. +Since Apdex is part of the telemetry system, it is possible to view Apdex data directly from CouchDB. However, it is more useful when aggregated across many users, interactions, and/or days. With this in mind, it is typically easier to query the data using SQL from an analytics database.Connecting to RDBMS from MacOShttps://docs.communityhealthtoolkit.org/apps/guides/database/rdbms-from-mac/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/database/rdbms-from-mac/Follow these steps on a Mac to generate your public/private keys and access the PostgreSQL server. +Access Terminal Terminal (Terminal.app) is the terminal emulator included in the macOS operating system. You can use this application to generate your SSH key. +Open a new Finder window Navigate to the Applications folder Navigate to the Utilities folder Open the Terminal app Generate Key From Terminal, follow these instructions (see screenshot below): +Type: ssh-keygen -t rsa Hit return to use the default file / location Enter a passphrase Enter your passphrase again Type: cat ~/.Connecting to RDBMS from Windowshttps://docs.communityhealthtoolkit.org/apps/guides/database/rdbms-from-windows/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/database/rdbms-from-windows/Connecting to RDBMS, the PostgreSQL server, is pretty stratightforward in nix systems. In Windows there are a couple of things you need to do to get it up and running. +SSH Key Generation and Importing Download Puttygen from here +Run Puttygen Go to Windows Start menu → All Programs → PuTTY→ PuTTYgen. +Create a new key pair for your computer. +Convert the key generated from ssh2 format to openssh. Puttygen supports this. \ No newline at end of file diff --git a/apps/guides/database/muting_in_dashboards/index.html b/apps/guides/database/muting_in_dashboards/index.html index 9e8841fb1d..7d022b1e80 100644 --- a/apps/guides/database/muting_in_dashboards/index.html +++ b/apps/guides/database/muting_in_dashboards/index.html @@ -1,9 +1,9 @@ -Contact Muting in SQL queries | Community Health Toolkit +Contact Muting in SQL queries | Community Health Toolkit

    Contact Muting in SQL queries

    How to write SQL queries excluding muted contacts correctly

    When a contact gets muted, two of many things happen:

    • The target contact and all of its descendants have a muted property set equal to the date they were muted
    • an entry is added to the contact’s muting_history in sentinel’s info docs

    When building dashboards on Superset, Klipfolio, or other data visualization platforms, you might need to exclude these muted contacts from the visualized data. An easy way to do this is to check the contact’s muted property which when present has the date value of when the contact was muted and when absent means that the contact is not muted. This works when you are only interested in seeing the latest data but it gets complicated when you want to look at a contact’s mute state from a certain period in the past.

    For example, if a contact was muted in February and unmuted in May; If you check the contact’s mute state in March from June, you’d find that the contact would not have the muted property as it would have been removed during the unmute in May. This is where the muting_history comes in. A contact’s muting_history contains all mute and unmute events stored in a JSON array. An example of the mute/unmute entries is shown below:

    Contact Muting in SQL queries

    How to write SQL queries excluding muted contacts correctly

    When a contact gets muted, two of many things happen:

    • The target contact and all of its descendants have a muted property set equal to the date they were muted
    • an entry is added to the contact’s muting_history in sentinel’s info docs

    When building dashboards on Superset, Klipfolio, or other data visualization platforms, you might need to exclude these muted contacts from the visualized data. An easy way to do this is to check the contact’s muted property which when present has the date value of when the contact was muted and when absent means that the contact is not muted. This works when you are only interested in seeing the latest data but it gets complicated when you want to look at a contact’s mute state from a certain period in the past.

    For example, if a contact was muted in February and unmuted in May; If you check the contact’s mute state in March from June, you’d find that the contact would not have the muted property as it would have been removed during the unmute in May. This is where the muting_history comes in. A contact’s muting_history contains all mute and unmute events stored in a JSON array. An example of the mute/unmute entries is shown below:

    {
         "_id": "_id",
         "_rev": "3-01ecfdd2958baeaf16fc621c5622f4a9",
         "type": "info",
    @@ -347,7 +347,8 @@
             date_trunc('day',muted_on) <= date_trunc('day', EXAMPLE_DATE) AND 
             unmuted_on >= (date_trunc('day', EXAMPLE_DATE) + '1 day'::interval)
     

    This query checks if a record exists for this contact where they were muted earlier or on EXAMPLE_DATE and unmuted on or after the EXAMPLE_DATE. For the earlier example where the contact was muted in February and unmuted in May if we pass February, March, or April as EXAMPLE_DATE, we find a record because our unmuted_on is always greater than EXAMPLE_DATE. If we pass May going forward, we find no records that match our condition.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/database/querying_apdex_telemetry/index.html b/apps/guides/database/querying_apdex_telemetry/index.html index 3d57243565..7833c02c06 100644 --- a/apps/guides/database/querying_apdex_telemetry/index.html +++ b/apps/guides/database/querying_apdex_telemetry/index.html @@ -1,9 +1,9 @@ -Querying Apdex Telemetry Data | Community Health Toolkit +Querying Apdex Telemetry Data | Community Health Toolkit

    Querying Apdex Telemetry Data

    How to use SQL queries to view Apdex scores

    Added in 4.7.0, CHT now records the Apdex (Application Performance Index) that is an open standard for measuring performance of software applications.

    Since Apdex is part of the telemetry system, it is possible to view Apdex data directly from CouchDB. However, it is more useful when aggregated across many users, interactions, and/or days. With this in mind, it is typically easier to query the data using SQL from an analytics database.

    An example of an SQL to view the Apdex score:

    WITH apdex_telemetry_data AS (
    + Create project issue

    Querying Apdex Telemetry Data

    How to use SQL queries to view Apdex scores

    Added in 4.7.0, CHT now records the Apdex (Application Performance Index) that is an open standard for measuring performance of software applications.

    Since Apdex is part of the telemetry system, it is possible to view Apdex data directly from CouchDB. However, it is more useful when aggregated across many users, interactions, and/or days. With this in mind, it is typically easier to query the data using SQL from an analytics database.

    An example of an SQL to view the Apdex score:

    WITH apdex_telemetry_data AS (
       SELECT
         substring(metric from '^(.*):apdex:') AS event_category,
         CASE
    @@ -391,7 +391,8 @@
     Quick Guides >
     Database
     : Postgresql

    Managing databases used by CHT applications

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/guides/database/rdbms-from-mac/index.html b/apps/guides/database/rdbms-from-mac/index.html index f924a4baff..17f104c564 100644 --- a/apps/guides/database/rdbms-from-mac/index.html +++ b/apps/guides/database/rdbms-from-mac/index.html @@ -1,9 +1,9 @@ -Connecting to RDBMS from MacOS | Community Health Toolkit +Connecting to RDBMS from MacOS | Community Health Toolkit

    Connecting to RDBMS from MacOS

    How to connect to the PostgreSQL RDBMS server from MacOS

    Follow these steps on a Mac to generate your public/private keys and access the PostgreSQL server.

    Access Terminal

    Terminal (Terminal.app) is the terminal emulator included in the macOS operating system. You can use this application to generate your SSH key.

    1. Open a new Finder window
    2. Navigate to the Applications folder
    3. Navigate to the Utilities folder
    4. Open the Terminal app

    Terminal

    Generate Key

    From Terminal, follow these instructions (see screenshot below):

    1. Type: ssh-keygen -t rsa
    2. Hit return to use the default file / location
    3. Enter a passphrase
    4. Enter your passphrase again
    5. Type: cat ~/.ssh/id_rsa.pub

    In the screenshot below:

    • (a) is the location and filename of your private key
    • (b) is the location and filename of your public key
    • (c) are the contents of your public key

    SSH Commands

    Connect to PostgreSQL

    Copy your public key and provide it to your Medic contact or RDBMS administrator. Your public key is not sensitive and can be shared over slack, github, etc…

    Once the RDBMS administrator has added your public SSH key on RDBMS, the administrator will provide you with login credentials to the SSH server as well as for PostgreSQL.

    Verify you can successfully connect to to the SSH server with ssh -i ~/.ssh/id_rsa <user>@<rdbms host> -p <port>. If your setup is correct, you should login to the server and see the prompt of your terminal change to <user>@rdbms:~$

    You should be able to access PostgreSQL from a SQL client using the provided credentials. Some common SQL clients include: pgAdmin, DBeaver, Postico.

    From your SQL client, use the settings mentioned below to connect. Be sure to select the Private Key that you generated above.

    FieldValue
    Hostlocalhost
    Host Port5432
    User<provided by Medic>
    Password<provided by Medic>
    Database<provided by Medic>
    SSH Hostrdbms.dev.medicmobile.org
    SSH Port33696
    SSH User<provided by Medic>
    SSH PasswordN/A - Use Private Key
    Private KeyChoose the location of the private key generated above

    PG Connection Settings


    CHT Applications > + Create project issue

    Connecting to RDBMS from MacOS

    How to connect to the PostgreSQL RDBMS server from MacOS

    Follow these steps on a Mac to generate your public/private keys and access the PostgreSQL server.

    Access Terminal

    Terminal (Terminal.app) is the terminal emulator included in the macOS operating system. You can use this application to generate your SSH key.

    1. Open a new Finder window
    2. Navigate to the Applications folder
    3. Navigate to the Utilities folder
    4. Open the Terminal app

    Terminal

    Generate Key

    From Terminal, follow these instructions (see screenshot below):

    1. Type: ssh-keygen -t rsa
    2. Hit return to use the default file / location
    3. Enter a passphrase
    4. Enter your passphrase again
    5. Type: cat ~/.ssh/id_rsa.pub

    In the screenshot below:

    • (a) is the location and filename of your private key
    • (b) is the location and filename of your public key
    • (c) are the contents of your public key

    SSH Commands

    Connect to PostgreSQL

    Copy your public key and provide it to your Medic contact or RDBMS administrator. Your public key is not sensitive and can be shared over slack, github, etc…

    Once the RDBMS administrator has added your public SSH key on RDBMS, the administrator will provide you with login credentials to the SSH server as well as for PostgreSQL.

    Verify you can successfully connect to to the SSH server with ssh -i ~/.ssh/id_rsa <user>@<rdbms host> -p <port>. If your setup is correct, you should login to the server and see the prompt of your terminal change to <user>@rdbms:~$

    You should be able to access PostgreSQL from a SQL client using the provided credentials. Some common SQL clients include: pgAdmin, DBeaver, Postico.

    From your SQL client, use the settings mentioned below to connect. Be sure to select the Private Key that you generated above.

    FieldValue
    Hostlocalhost
    Host Port5432
    User<provided by Medic>
    Password<provided by Medic>
    Database<provided by Medic>
    SSH Hostrdbms.dev.medicmobile.org
    SSH Port33696
    SSH User<provided by Medic>
    SSH PasswordN/A - Use Private Key
    Private KeyChoose the location of the private key generated above

    PG Connection Settings


    CHT Applications > Quick Guides > Database > RDBMS from Windows

    How to connect to the PostgreSQL RDBMS server from Windows

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/database/rdbms-from-windows/index.html b/apps/guides/database/rdbms-from-windows/index.html index 53e6aaea77..1fce5430d2 100644 --- a/apps/guides/database/rdbms-from-windows/index.html +++ b/apps/guides/database/rdbms-from-windows/index.html @@ -1,9 +1,9 @@ -Connecting to RDBMS from Windows | Community Health Toolkit +Connecting to RDBMS from Windows | Community Health Toolkit

    Connecting to RDBMS from Windows

    How to connect to the PostgreSQL RDBMS server from Windows

    Connecting to RDBMS, the PostgreSQL server, is pretty stratightforward in nix systems. In Windows there are a couple of things you need to do to get it up and running.

    SSH Key Generation and Importing

    1. Download Puttygen from here

    2. Run Puttygen Go to Windows Start menu → All Programs → PuTTY→ PuTTYgen.

    3. Create a new key pair for your computer.

    Putty-gen-key-pair

    1. Convert the key generated from ssh2 format to openssh. Puttygen supports this.
    1. Open PuttyGen
    + Create project issue

    Connecting to RDBMS from Windows

    How to connect to the PostgreSQL RDBMS server from Windows

    Connecting to RDBMS, the PostgreSQL server, is pretty stratightforward in nix systems. In Windows there are a couple of things you need to do to get it up and running.

    SSH Key Generation and Importing

    1. Download Puttygen from here

    2. Run Puttygen Go to Windows Start menu → All Programs → PuTTY→ PuTTYgen.

    3. Create a new key pair for your computer.

    Putty-gen-key-pair

    1. Convert the key generated from ssh2 format to openssh. Puttygen supports this.
    1. Open PuttyGen
     2. Click Load
     3. Load your private key
     4. Go to Conversions->Export OpenSSH and export your private key
    @@ -314,7 +314,8 @@
     Quick Guides >
     Database >
     RDBMS from MacOS

    How to connect to the PostgreSQL RDBMS server from MacOS

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/guides/debugging/index.html b/apps/guides/debugging/index.html index 49230b9c59..747c83fa7e 100644 --- a/apps/guides/debugging/index.html +++ b/apps/guides/debugging/index.html @@ -1,9 +1,9 @@ -Debugging Applications | Community Health Toolkit +Debugging Applications | Community Health Toolkit

    Debugging Applications

    Guides for debugging common scenarios

    Obtaining Browser and Phone Logs

    How to obtain Android and browser client logs

    Replicating Production Data Locally

    How to copy data from an instance to a local CouchDB database and app

    Securely Sharing Your Development Environment

    Use a publicly accessible Linux web server to forward https requests to your development environment

    Sharing 4.x Logs

    How to easily share logs from your CHT 4.x instance to get support

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/debugging/index.xml b/apps/guides/debugging/index.xml index 6c17d39a73..2f6d24580a 100644 --- a/apps/guides/debugging/index.xml +++ b/apps/guides/debugging/index.xml @@ -1,247 +1,5 @@ -Community Health Toolkit – Debugging Applicationshttps://docs.communityhealthtoolkit.org/apps/guides/debugging/Recent content in Debugging Applications on Community Health ToolkitHugo -- gohugo.ioenApps: Obtaining Browser and Phone Logshttps://docs.communityhealthtoolkit.org/apps/guides/debugging/obtaining-logs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/debugging/obtaining-logs/ -<p>There are many places where useful logs reside. This details all those places, and the easiest way to get a hold of them.</p> -<h2 id="on-a-laptop-or-desktop">On a laptop or desktop</h2> -<p>To check if there are relevant logs open up the developer console in your browser. The shortcut is probably COMMAND+OPTION+I on MacOS, or CTRL+SHIFT+I on Linux and Windows. Click the console tab and copy out any errors or logging that you think is relevant.</p> -<h2 id="on-a-phone">On a phone</h2> -<p>There are two types of logs updated by Android devices depending on what information is needed. If you trying to get either of the logs from a device for the first time you need to set it up for USB debugging.</p> -<ol> -<li> -<p>Enable USB debugging on the phone. This varies from phone to phone, but here is the <a href="https://developer.android.com/studio/debug/dev-options#enable">official Android documentation</a>.</p> -</li> -<li> -<p>Connect your phone by USB to the computer.</p> -</li> -<li> -<p>On your phone, you will see a popup <em>&ldquo;Allow USB debugging. The computer&rsquo;s RSA fingerprint&hellip;&rdquo;</em>, click <em>&ldquo;OK&rdquo;</em>.</p> -<p><img src="https://docs.communityhealthtoolkit.org/apps/guides/debugging/images/allow_usb_debugging.png" alt="Allow USB debugging"></p> -</li> -</ol> -<h3 id="browser-logs">Browser logs</h3> -<p>Now that you&rsquo;ve enabled USB debugging on the phone you can access the dev console on the phone via USB from your desktop browser. This allows the same level of debugging and inspection you have on a desktop browser, but from your phone&rsquo;s browser in the CHT app. Follow the <a href="https://developer.chrome.com/docs/devtools/remote-debugging/webviews/#open_a_webview_in_devtools">official Android documentation</a> to access the console.</p> -<h3 id="android-logs">Android logs</h3> -<p>The Android log is written to from the cht-android wrapper which captures errors like application crashes or failing integrations between Android apps.</p> -<ol> -<li> -<p>To install the <code>adb</code> command, follow the instructions under the <a href="https://docs.communityhealthtoolkit.org/contribute/code/android/development-setup/#debug-tool-adb">Development Environment &gt; Debug tool adb</a> section.</p> -</li> -<li> -<p>Within a command line session, write the following command: <code>adb start-server</code>.</p> -</li> -<li> -<p>To check if your phone is properly connected, write the command <code>adb devices</code>. This will list the devices connected.</p> -<p><img src="https://docs.communityhealthtoolkit.org/apps/guides/debugging/images/adb_devices.png" alt="ADB Devices"></p> -</li> -<li> -<p>Type the command <code>adb logcat &gt; phone.log</code> to get the android logs in the file <code>phone.log</code>. The log will be written to the <code>phone.log</code> file as long as this command is running. You can stop the recording at any time pressing <em>Ctrl+C</em>. Now if you reproduce the error on the phone you can look for any useful information being written to the <code>phone.log</code>.</p> -</li> -</ol> -<p>This will get all the logs from the device, not just logs for the app you want to debug. You can pass arguments to <code>adb</code> to only get the logs you want.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>adb logcat MedicMobile:V AndroidRuntime:E chromium:V <span style="color:#4e9a06">&#39;*:S&#39;</span> &gt; phone.log -</span></span></code></pre></div><p>Alternatively you can use <code>grep</code> to filter the logs down to only those from the relevant app.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>adb logcat <span style="color:#000;font-weight:bold">|</span> grep MedicMobile &gt; phone.log -</span></span></code></pre></div><h2 id="on-the-server">On the server</h2> -<p>Some unexpected errors are caught and stored in <code>feedback</code> docs and stored on the phone and later synced to CouchDB on the server. To access these, look for docs in the <code>medic-user-{username}-meta</code> or <code>medic-users-meta</code> databases. This is particularly useful to debug issues where you do not have physical access to the device. More information is available under <a href="https://docs.communityhealthtoolkit.org/apps/guides/database/">Managing Databases</a>.</p>Apps: Replicating Production Data Locallyhttps://docs.communityhealthtoolkit.org/apps/guides/debugging/replicating-production-data-locally/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/debugging/replicating-production-data-locally/ -<p>Sometimes there will be a production problem that you need to dig into locally to solve. This guide explains how to:</p> -<ul> -<li>Copy the data from an instance to a local CouchDB database</li> -<li>Run a local webapp instance with that data</li> -</ul> -<h2 id="first-a-note-about-data-safety">First, a note about data safety</h2> -<p>Production data is medical data. It&rsquo;s HIV statuses and pregnancies. It&rsquo;s important, and it&rsquo;s not yours. If you&rsquo;re downloading it, do so on an encrypted drive and delete it once you&rsquo;re done with it.</p> -<h2 id="step-1-get-the-data">Step 1: Get the data</h2> -<p>First thing is to get the data onto your local CouchDB. It&rsquo;s advisable to create a new DB for this, so that you have a fresh untouched collection of data that isn&rsquo;t mixed in with anything you have locally.</p> -<h3 id="small-production-instances">Small production instances</h3> -<p>If there isn&rsquo;t much data you can replicate the entire DB locally. You can initiate this either from your local Fauxton, or from the command line. You must use an administrator username and password for this at both the source and destination.</p> -<p>For Fauxton, navigate to http://localhost:5984/_utils/#/replication/_create -For command line, see: <a href="https://docs.couchdb.org/en/stable/api/server/common.html#replicate">https://docs.couchdb.org/en/stable/api/server/common.html#replicate</a></p> -<p>Note that replication may stall on one document, and you may end up with your local DB having one less document than the source. This is due to how our URLs are setup: the replicator gets confused and considers <code>login</code> (ie <code>https://url/login</code>) to be a document. You can safely ignore this difference - you&rsquo;re good to go once your destination database has one less document than the source.</p> -<h3 id="large-production-instances">Large production instances</h3> -<p>If the instance is too large to replicate locally (or you are too impatient), you can replicate the data accessible to a single user. This process downloads a user&rsquo;s data into a browser and then copies that data into a CouchDB database.</p> -<ol> -<li>Open Firefox and navigate to <code>about:config</code>. Set <code>security.csp.enable</code> to <code>false</code> to disable Content Security Policies.</li> -<li>Navigate to the instance and login as the user with the data you want. <em>(If you want more data, like an entire district, you could consider logging in as a new user with a contact document at your desired place in the contact hierarchy. But that is an exercise for the reader)</em></li> -<li>Wait for the data to replicate. You know this is done once the app lets you interact with it. <em>(If you want to get the user&rsquo;s data before purging, consider disabling purging. Another exercise for the reader)</em></li> -<li>Make sure your local CouchDB has CORS enabled: <a href="https://docs.couchdb.org/en/stable/config/http.html#cors">https://docs.couchdb.org/en/stable/config/http.html#cors</a>. Consider using <a href="https://github.com/pouchdb/add-cors-to-couchdb#user-content-what-it-does">add-cors-to-couchdb</a> or its recommended settings.</li> -<li>Allow your CouchDB to be accessible via <code>https</code>. One way is to run <code>ngrok http PORT</code>. The <code>PORT</code> <em>(<code>5984</code> by default)</em> can be found in database configuration using fauxton under the <code>chttpd</code> section. This will make your CouchDB accessible via a url like <code>https://abcd1234.ngrok.io</code>.</li> -<li>Open the console in Firefox and run <code>await PouchDB.replicate('medic-user-XXX', 'https://your:admin@abcd1234.ngrok.io/YYY');</code>. Here <code>XXX</code> is the name you logged in as, and <code>YYY</code> is the name of a database in which to store the data. -<ul> -<li><strong>Note</strong>: If you get 401s make sure that: your CouchDB credentials are right; and you don&rsquo;t have a local session in the same browser already (session cookies can take precedence over basic auth); and if you&rsquo;re running CouchDB in Docker you have exposed both <code>5984</code> and <code>5986</code> to localhost.</li> -</ul> -</li> -<li>Wait for the replication to complete. In Fauxton you should see the database YYY with the same number of documents reported during the user&rsquo;s initial replication.</li> -<li>Log out of the instance and clear your data from the developer console (Application -&gt; Clear storage).</li> -<li>Navigate to <code>about:config</code>. Set <code>security.csp.enable</code> to <code>true</code> to re-enable Content Security Policies.</li> -</ol> -<h3 id="regardless-do-this-too">Regardless, do this too</h3> -<p>To log in as a specific prod user you need to also copy them from the prod <code>_users</code> database into your own local <code>_users</code> database. The simplest way to do this is to just open the DB in Fauxton, find the document and copy it on your clipboard, then create a new document in your local <code>_users</code> DB and paste it in, deleting the <code>_rev</code> property.</p> -<p>You could also use this opportunity to change the password to something easier to work with locally. To do this, add a <code>password</code> property into the document with the password you want in plain text. CouchDB will convert this to a properly hashed password on save.</p> -<h2 id="step-2-run-it-locally">Step 2, run it locally</h2> -<p>First you need to decide if you need a local development environment (unless you already have one, in which case you might as well use it), or are happy to just use <a href="https://github.com/medic/horticulturalist">Horticulturalist</a>.</p> -<p>A local development environment will be useful to you if:</p> -<ul> -<li>You want to change code locally</li> -<li>You want to see useful, non-minified stack traces, or otherwise browse / step through non-minified code in the browser</li> -<li>You want to deploy a version of the code older than <code>2.14.0</code>, as they are not available in Horticulturalist&rsquo;s repos.</li> -</ul> -<h3 id="option-1-local-development-environment">Option 1, local development environment</h3> -<p>If you don&rsquo;t already have a local dev env, follow the instructions on the <a href="https://docs.communityhealthtoolkit.org/contribute/code/core/dev-environment/">development setup instructions</a>.</p> -<p>Then you need to:</p> -<ol> -<li>Push the code you want to run via <code>COUCH_URL=http://your:admin@localhost:5984/YYY npm run build-dev</code>.</li> -<li>Start API and Sentinel by running <code>COUCH_URL=http://your:admin@localhost:5984/YYY node api/server</code>.</li> -</ol> -<p>Once you&rsquo;ve done all of that you should be able to log in with your user.</p> -<h3 id="option-2-horticulturalist">Option 2, horticulturalist</h3> -<p>Follow the instructions on the <a href="https://github.com/medic/horticulturalist">horticulturalist repo</a> to get it installed. Then:</p> -<ol> -<li>Make sure that the <code>medic</code> DB doesn&rsquo;t exist, so that you have a fresh database.</li> -<li>Replicate your local PROD DB into a new <code>medic</code> database using the Fauxton console.</li> -<li>Run <code>horti --local --bootstrap=XXX</code>, where <code>XXX</code> is the version you want to use (maybe the same one as production?)</li> -</ol> -<p>You should now be able to log in as that user locally!</p>Apps: Securely Sharing Your Development Environmenthttps://docs.communityhealthtoolkit.org/apps/guides/debugging/secure-sharing-of-developer-instance/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/debugging/secure-sharing-of-developer-instance/ -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Warning</h4> -<p>Be extra careful with this process! The end result will be that your development instance will be accessible to the internet. If you have simple logins and passwords like &ldquo;admin/test.223&rdquo; because you thought it was just your local dev instance and it doesn&rsquo;t matter, now it matters! Whenever you&rsquo;re not using the SSH tunnel for testing, shut it down so there&rsquo;s no more remote access.</p> -<p>Never expose a development instance to the internet where you&rsquo;ve replicated production data locally. Well, maybe not never, but with extreme care and intention.</p> -<p>Also not - if you only want to test with mobile devices that require a valid TLS certificate, do not use method. Instead, use <a href="https://docs.communityhealthtoolkit.org/contribute/code/core/dev-environment/#nginx-local-ip">nginx-local-ip</a></p> -</div> -<h2 id="overview">Overview</h2> -<p>When using a local <a href="https://docs.communityhealthtoolkit.org/contribute/code/core/dev-environment/">development environment</a>, you may want to share your work with other collaborators. By using a publicly accessible web server, you can receive the secure https requests and forward them back to your CHT instance which doesn&rsquo;t have https set up:</p> -<p><a href="https://docs.communityhealthtoolkit.org/apps/guides/debugging/images/SSH.tunnel.diagram.svg"><img src="https://docs.communityhealthtoolkit.org/apps/guides/debugging/images/SSH.tunnel.diagram.svg" width=100% height=100%></a></p> -<p>Once you have this web server set up, you may continue to use it whenever you want by simply reconnecting to it via the secure tunnel.</p> -<h2 id="prerequisites">Prerequisites</h2> -<p>This guide assumes:</p> -<ul> -<li>You have a local <a href="https://docs.communityhealthtoolkit.org/contribute/code/core/dev-environment/">dev instance</a> set up of cht-core</li> -<li>You have the <a href="https://play.google.com/store/apps/details?id=org.medicmobile.webapp.mobile&amp;hl=en_US">generic Medic app</a> installed on your Android device. This version allows you to enter a custom CHT URL on first run.</li> -<li>You have an Ubuntu &gt;18.04 server with a public IP and a DNS entry that you can SSH into and have sudo on</li> -<li>You have Apache &gt;2.4.29 installed on the Ubuntu server and can add a new vhost to it, including an SSL cert. (nginx could be used instead as well, but not covered here)</li> -<li>You have certbot installed from letsencrypt.org</li> -</ul> -<p>The steps in this guide can be done on any of the cheap server providers out there (<a href="https://digitalocean.com">Digital Ocean</a> has a $5/mo server).</p> -<h2 id="steps">Steps</h2> -<ol> -<li> -<p>Create a DNS entry. Let&rsquo;s assume it&rsquo;s <code>cht.domain.com</code>. It should point to the IP of your Ubuntu server. If you do not already have a domain name with DNS services that you can use, you can sign up for a free service to do this like <a href="https://www.noip.com/remote-access">noip.com</a>.</p> -</li> -<li> -<p>On your Ubuntu server, create a new apache vhost in <code>/etc/apache2/sites-available/100-cht.domain.com.conf</code> with the following contents:</p> -<pre tabindex="0"><code>&lt;VirtualHost *:80&gt; -ServerName cht.domain.com -RewriteEngine on -RewriteRule (.*) https://cht.domain.com%{REQUEST_URI} -&lt;/VirtualHost&gt; -&lt;IfModule mod_ssl.c&gt; -&lt;VirtualHost *:443&gt; -ServerName cht.domain.com -SSLEngine On -&lt;IfModule mod_headers.c&gt; -Header always set Strict-Transport-Security &#34;max-age=63072000; preload&#34; -&lt;/IfModule&gt; -RewriteEngine on -Include /etc/letsencrypt/options-ssl-apache.conf -SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem -SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key -ProxyPass / http://localhost:8081/ -ProxyPassReverse / http://localhost:8081/ -RequestHeader set X-Forwarded-Proto &#34;https&#34; -&lt;/VirtualHost&gt; -&lt;/IfModule&gt; -</code></pre></li> -<li> -<p>Enable the new site: <code>a2ensite 100-cht.domain.com</code></p> -</li> -<li> -<p>Restart apache and ensure there&rsquo;s no errors: <code>apachectl restart</code></p> -</li> -<li> -<p>Create the TLS certificate: <code>certbot -d cht.domain.com</code></p> -</li> -<li> -<p>When prompted choose no redirect: &ldquo;No redirect - Make no further changes to the webserver configuration.&rdquo;</p> -</li> -<li> -<p>Restart apache and ensure there&rsquo;s no errors: <code>apachectl restart</code></p> -</li> -<li> -<p>In a browser, test that you can connect to your server with no errors at <a href="https://cht.domain.com">https://cht.domain.com</a> (you may get a <code>500</code> error, but you shouldn&rsquo;t get any TLS errors)</p> -</li> -<li> -<p>Ensure your cht-core local dev instance is running by going to http://localhost:5988/</p> -</li> -<li> -<p>On your local dev box, set up the SSH tunnel with: <code>ssh -NT -R 8081:127.0.0.1:5988 cht.domain.com</code></p> -</li> -<li> -<p>This assumes your local username is the same as it is on cht.domain.com. This command will hang and you may exit when down with <code>ctrl + c</code></p> -</li> -<li> -<p>In a browser, test again that you now see your local dev instance and it loads correctly at <a href="https://cht.domain.com">https://cht.domain.com</a></p> -</li> -<li> -<p>If needed, reset the Medic app on your phone so that it prompts which instance to use</p> -</li> -<li> -<p>In the app on your phone, choose &ldquo;custom&rdquo; for which instance to use and enter <a href="https://cht.domain.com">https://cht.domain.com</a>. You should now see your local dev instance in the CHT Android device. Happy testing!</p> -</li> -</ol> -<h2 id="tunnel-command-breakdown">Tunnel command breakdown</h2> -<p>From the SSH command in step 10 above:</p> -<p><a href="https://docs.communityhealthtoolkit.org/apps/guides/debugging/images/ssh.ports.svg"><img src="https://docs.communityhealthtoolkit.org/apps/guides/debugging/images/ssh.ports.svg" width=100% height=100%></a></p> -<ol> -<li><code>8081</code> - Remote port on cht.domain.com to listen to. This is the same port that apache redirects to in step 2 above.</li> -<li><code>127.0.0.1</code> - Host to send forwarded traffic to. In this case, your local machine.</li> -<li><code>5988</code> - Local port where traffic from step one will be sent. In this case, your instance of the CHT</li> -<li><code>cht.domain.com</code> - Public domain where you have an SSH account and we&rsquo;ll attach port <code>8081</code> to from step 1.</li> -</ol>Apps: Sharing 4.x Logshttps://docs.communityhealthtoolkit.org/apps/guides/debugging/sharing-4x-logs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/debugging/sharing-4x-logs/ -<p>CHT 4.x moves from a monolithic container MedicOS to discrete containers, each service hosting one service of the CHT. When troubleshooting an issue with your CHT instance, it can be hard to list each container, see it&rsquo;s status, gather up logs for each container and then share all this information with Medic or other support staff. To ease this pain, a script was written which automates the process.</p> -<h2 id="prerequisites">Prerequisites</h2> -<p>This assumes you&rsquo;re running CHT 4.x have access to the command line on the server where it&rsquo;s running locally or via SSH. While the script will work with CHT 3.x instances, the amount of logs that a <code>docker logs</code> call yields isn&rsquo;t very helpful. To see more about CHT 3.x logs, see <a href="https://docs.communityhealthtoolkit.org/hosting/3.x/ec2-setup-guide/#troubleshooting">&ldquo;Investigating logs inside Medic OS&rdquo;</a></p> -<p>This guide also assumes you have the <a href="https://github.com/medic/cht-core/">CHT Core repo checked out</a> so that you have a copy of the <code>compress_and_archive_docker_logs.sh</code> script. If you do not have it checked out, you can manually create a local copy with this <code>curl</code> command:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#000">curl -o compress_and_archive_docker_logs.sh https://raw.githubusercontent.com/medic/cht-core/master/scripts/compress_and_archive_docker_logs.sh</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#000">chmod +x compress_and_archive_docker_logs.sh</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span></code></pre></div><h2 id="calling-the-script">Calling the script</h2> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -<p>Be aware of two important features of this script:</p> -<ul> -<li>It will get logs for ALL docker containers running, even if they&rsquo;re not part of the CHT</li> -<li>Logs on production CHT instances will contain PII/PHI and should be handled with care</li> -</ul> -</div> -<p>The script defaults to getting the past 24 hours of logs and can be called from anywhere on your system as long as you specify the full path to the script. Here it&rsquo;s being called from with in the <code>cht-core/scripts</code> directory:</p> -<pre tabindex="0"><code>./compress_and_archive_docker_logs.sh -</code></pre><p>If you&rsquo;d liked to get more logs than the most recent 24 hours, pass in an argument of hours. Here we&rsquo;re asking for 2 days worth of logs by using <code>48</code> as the argument:</p> -<pre tabindex="0"><code>./compress_and_archive_docker_logs.sh 48 -</code></pre><p>Depending on this volume of your logs, this may take a while. This is what the output of the script is when it&rsquo;s completed:</p> -<pre tabindex="0"><code>Wait while the script gathers stats and logs about the CHT containers. -Be patient, this might take a moment... -Done! -/home/cht-user/.medic/support_logs/cht-docker-logs-2023-02-14T15.04.45-08.00.tar.gz -NOTE: Please remove the file when done as it may contain PII/PHI. -</code></pre><p>The files are saved in your home directory (<code>/home/cht-user/</code> in this case) and are timestamped with the creation date and timezone offset (<code>2023-02-14T15.04.45-08.00</code>).</p> -<p>You can now share this <code>tar.gz</code> file with support staff as needed. Again, be careful with it as <strong>it will contain PII/PHI if you ran this against a production instance</strong>.</p> -<h2 id="archive-contents">Archive contents</h2> -<p>First, let&rsquo;s look at the running containers when we called the script by calling <code>docker ps --format '{{.Names}}'</code>:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>new_project_nginx_1 -</span></span><span style="display:flex;"><span>new_project_sentinel_1 -</span></span><span style="display:flex;"><span>new_project_api_1 -</span></span><span style="display:flex;"><span>new_project_haproxy_1 -</span></span><span style="display:flex;"><span>new_project_couchdb_1 -</span></span><span style="display:flex;"><span>new_project_healthcheck_1 -</span></span><span style="display:flex;"><span>new_project-dir-cht-upgrade-service-1 -</span></span></code></pre></div><p>Now if we uncompress the tarball created above and list the contents, it should look very similar when we call <code>cd ~/.medic/support_logs&amp;&amp;tar xzvf cht-docker-logs-2023-02-14T15.04.45-08.00.tar.gz</code>:</p> -<pre tabindex="0"><code>docker_ps.log -docker_stats.log -new_project-dir-cht-upgrade-service-1.log -new_project_api_1.log -new_project_couchdb_1.log -new_project_haproxy_1.log -new_project_healthcheck_1.log -new_project_nginx_1.log -new_project_sentinel_1.log -</code></pre><p>There&rsquo;s one file per container, each with the logs from that container. As well there&rsquo;s two other files:</p> -<ul> -<li><code>docker_ps.log</code> - the output of <code>docker ps</code></li> -<li><code>docker_stats.log</code> - the output of <code>docker stats</code></li> -</ul> \ No newline at end of file +Debugging Applications on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/guides/debugging/Recent content in Debugging Applications on Community Health ToolkitHugo -- gohugo.ioenObtaining Browser and Phone Logshttps://docs.communityhealthtoolkit.org/apps/guides/debugging/obtaining-logs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/debugging/obtaining-logs/There are many places where useful logs reside. This details all those places, and the easiest way to get a hold of them. +On a laptop or desktop To check if there are relevant logs open up the developer console in your browser. The shortcut is probably COMMAND+OPTION+I on MacOS, or CTRL+SHIFT+I on Linux and Windows. Click the console tab and copy out any errors or logging that you think is relevant.Replicating Production Data Locallyhttps://docs.communityhealthtoolkit.org/apps/guides/debugging/replicating-production-data-locally/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/debugging/replicating-production-data-locally/Sometimes there will be a production problem that you need to dig into locally to solve. This guide explains how to: +Copy the data from an instance to a local CouchDB database Run a local webapp instance with that data First, a note about data safety Production data is medical data. It&rsquo;s HIV statuses and pregnancies. It&rsquo;s important, and it&rsquo;s not yours. If you&rsquo;re downloading it, do so on an encrypted drive and delete it once you&rsquo;re done with it.Securely Sharing Your Development Environmenthttps://docs.communityhealthtoolkit.org/apps/guides/debugging/secure-sharing-of-developer-instance/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/debugging/secure-sharing-of-developer-instance/Warning Be extra careful with this process! The end result will be that your development instance will be accessible to the internet. If you have simple logins and passwords like &ldquo;admin/test.223&rdquo; because you thought it was just your local dev instance and it doesn&rsquo;t matter, now it matters! Whenever you&rsquo;re not using the SSH tunnel for testing, shut it down so there&rsquo;s no more remote access. +Never expose a development instance to the internet where you&rsquo;ve replicated production data locally.Sharing 4.x Logshttps://docs.communityhealthtoolkit.org/apps/guides/debugging/sharing-4x-logs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/debugging/sharing-4x-logs/CHT 4.x moves from a monolithic container MedicOS to discrete containers, each service hosting one service of the CHT. When troubleshooting an issue with your CHT instance, it can be hard to list each container, see it&rsquo;s status, gather up logs for each container and then share all this information with Medic or other support staff. To ease this pain, a script was written which automates the process. +Prerequisites This assumes you&rsquo;re running CHT 4. \ No newline at end of file diff --git a/apps/guides/debugging/obtaining-logs/index.html b/apps/guides/debugging/obtaining-logs/index.html index 7dba12230e..27c2772f27 100644 --- a/apps/guides/debugging/obtaining-logs/index.html +++ b/apps/guides/debugging/obtaining-logs/index.html @@ -1,9 +1,9 @@ -Obtaining Browser and Phone Logs | Community Health Toolkit +Obtaining Browser and Phone Logs | Community Health Toolkit

    Obtaining Browser and Phone Logs

    How to obtain Android and browser client logs

    There are many places where useful logs reside. This details all those places, and the easiest way to get a hold of them.

    On a laptop or desktop

    To check if there are relevant logs open up the developer console in your browser. The shortcut is probably COMMAND+OPTION+I on MacOS, or CTRL+SHIFT+I on Linux and Windows. Click the console tab and copy out any errors or logging that you think is relevant.

    On a phone

    There are two types of logs updated by Android devices depending on what information is needed. If you trying to get either of the logs from a device for the first time you need to set it up for USB debugging.

    1. Enable USB debugging on the phone. This varies from phone to phone, but here is the official Android documentation.

    2. Connect your phone by USB to the computer.

    3. On your phone, you will see a popup “Allow USB debugging. The computer’s RSA fingerprint…”, click “OK”.

      Allow USB debugging

    Browser logs

    Now that you’ve enabled USB debugging on the phone you can access the dev console on the phone via USB from your desktop browser. This allows the same level of debugging and inspection you have on a desktop browser, but from your phone’s browser in the CHT app. Follow the official Android documentation to access the console.

    Android logs

    The Android log is written to from the cht-android wrapper which captures errors like application crashes or failing integrations between Android apps.

    1. To install the adb command, follow the instructions under the Development Environment > Debug tool adb section.

    2. Within a command line session, write the following command: adb start-server.

    3. To check if your phone is properly connected, write the command adb devices. This will list the devices connected.

      ADB Devices

    4. Type the command adb logcat > phone.log to get the android logs in the file phone.log. The log will be written to the phone.log file as long as this command is running. You can stop the recording at any time pressing Ctrl+C. Now if you reproduce the error on the phone you can look for any useful information being written to the phone.log.

    This will get all the logs from the device, not just logs for the app you want to debug. You can pass arguments to adb to only get the logs you want.

    adb logcat MedicMobile:V AndroidRuntime:E chromium:V '*:S' > phone.log
    + Create project issue

    Obtaining Browser and Phone Logs

    How to obtain Android and browser client logs

    There are many places where useful logs reside. This details all those places, and the easiest way to get a hold of them.

    On a laptop or desktop

    To check if there are relevant logs open up the developer console in your browser. The shortcut is probably COMMAND+OPTION+I on MacOS, or CTRL+SHIFT+I on Linux and Windows. Click the console tab and copy out any errors or logging that you think is relevant.

    On a phone

    There are two types of logs updated by Android devices depending on what information is needed. If you trying to get either of the logs from a device for the first time you need to set it up for USB debugging.

    1. Enable USB debugging on the phone. This varies from phone to phone, but here is the official Android documentation.

    2. Connect your phone by USB to the computer.

    3. On your phone, you will see a popup “Allow USB debugging. The computer’s RSA fingerprint…”, click “OK”.

      Allow USB debugging

    Browser logs

    Now that you’ve enabled USB debugging on the phone you can access the dev console on the phone via USB from your desktop browser. This allows the same level of debugging and inspection you have on a desktop browser, but from your phone’s browser in the CHT app. Follow the official Android documentation to access the console.

    Android logs

    The Android log is written to from the cht-android wrapper which captures errors like application crashes or failing integrations between Android apps.

    1. To install the adb command, follow the instructions under the Development Environment > Debug tool adb section.

    2. Within a command line session, write the following command: adb start-server.

    3. To check if your phone is properly connected, write the command adb devices. This will list the devices connected.

      ADB Devices

    4. Type the command adb logcat > phone.log to get the android logs in the file phone.log. The log will be written to the phone.log file as long as this command is running. You can stop the recording at any time pressing Ctrl+C. Now if you reproduce the error on the phone you can look for any useful information being written to the phone.log.

    This will get all the logs from the device, not just logs for the app you want to debug. You can pass arguments to adb to only get the logs you want.

    adb logcat MedicMobile:V AndroidRuntime:E chromium:V '*:S' > phone.log
     

    Alternatively you can use grep to filter the logs down to only those from the relevant app.

    adb logcat | grep MedicMobile > phone.log
     

    On the server

    Some unexpected errors are caught and stored in feedback docs and stored on the phone and later synced to CouchDB on the server. To access these, look for docs in the medic-user-{username}-meta or medic-users-meta databases. This is particularly useful to debug issues where you do not have physical access to the device. More information is available under Managing Databases.


    CHT Applications > Quick Guides > @@ -308,7 +308,8 @@ Sharing 4.x Logs

    How to easily share logs from your CHT 4.x instance to get support

    Hosting > 4.x > Logs

    What to do when you need to find server side errors in CHT 4.x

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/debugging/replicating-production-data-locally/index.html b/apps/guides/debugging/replicating-production-data-locally/index.html index 947e5a9ea7..20bdf6de3b 100644 --- a/apps/guides/debugging/replicating-production-data-locally/index.html +++ b/apps/guides/debugging/replicating-production-data-locally/index.html @@ -1,9 +1,9 @@ -Replicating Production Data Locally | Community Health Toolkit +Replicating Production Data Locally | Community Health Toolkit

    Replicating Production Data Locally

    How to copy data from an instance to a local CouchDB database and app

    Sometimes there will be a production problem that you need to dig into locally to solve. This guide explains how to:

    • Copy the data from an instance to a local CouchDB database
    • Run a local webapp instance with that data

    First, a note about data safety

    Production data is medical data. It’s HIV statuses and pregnancies. It’s important, and it’s not yours. If you’re downloading it, do so on an encrypted drive and delete it once you’re done with it.

    Step 1: Get the data

    First thing is to get the data onto your local CouchDB. It’s advisable to create a new DB for this, so that you have a fresh untouched collection of data that isn’t mixed in with anything you have locally.

    Small production instances

    If there isn’t much data you can replicate the entire DB locally. You can initiate this either from your local Fauxton, or from the command line. You must use an administrator username and password for this at both the source and destination.

    For Fauxton, navigate to http://localhost:5984/_utils/#/replication/_create + Create project issue

    Replicating Production Data Locally

    How to copy data from an instance to a local CouchDB database and app

    Sometimes there will be a production problem that you need to dig into locally to solve. This guide explains how to:

    • Copy the data from an instance to a local CouchDB database
    • Run a local webapp instance with that data

    First, a note about data safety

    Production data is medical data. It’s HIV statuses and pregnancies. It’s important, and it’s not yours. If you’re downloading it, do so on an encrypted drive and delete it once you’re done with it.

    Step 1: Get the data

    First thing is to get the data onto your local CouchDB. It’s advisable to create a new DB for this, so that you have a fresh untouched collection of data that isn’t mixed in with anything you have locally.

    Small production instances

    If there isn’t much data you can replicate the entire DB locally. You can initiate this either from your local Fauxton, or from the command line. You must use an administrator username and password for this at both the source and destination.

    For Fauxton, navigate to http://localhost:5984/_utils/#/replication/_create For command line, see: https://docs.couchdb.org/en/stable/api/server/common.html#replicate

    Note that replication may stall on one document, and you may end up with your local DB having one less document than the source. This is due to how our URLs are setup: the replicator gets confused and considers login (ie https://url/login) to be a document. You can safely ignore this difference - you’re good to go once your destination database has one less document than the source.

    Large production instances

    If the instance is too large to replicate locally (or you are too impatient), you can replicate the data accessible to a single user. This process downloads a user’s data into a browser and then copies that data into a CouchDB database.

    1. Open Firefox and navigate to about:config. Set security.csp.enable to false to disable Content Security Policies.
    2. Navigate to the instance and login as the user with the data you want. (If you want more data, like an entire district, you could consider logging in as a new user with a contact document at your desired place in the contact hierarchy. But that is an exercise for the reader)
    3. Wait for the data to replicate. You know this is done once the app lets you interact with it. (If you want to get the user’s data before purging, consider disabling purging. Another exercise for the reader)
    4. Make sure your local CouchDB has CORS enabled: https://docs.couchdb.org/en/stable/config/http.html#cors. Consider using add-cors-to-couchdb or its recommended settings.
    5. Allow your CouchDB to be accessible via https. One way is to run ngrok http PORT. The PORT (5984 by default) can be found in database configuration using fauxton under the chttpd section. This will make your CouchDB accessible via a url like https://abcd1234.ngrok.io.
    6. Open the console in Firefox and run await PouchDB.replicate('medic-user-XXX', 'https://your:admin@abcd1234.ngrok.io/YYY');. Here XXX is the name you logged in as, and YYY is the name of a database in which to store the data.
      • Note: If you get 401s make sure that: your CouchDB credentials are right; and you don’t have a local session in the same browser already (session cookies can take precedence over basic auth); and if you’re running CouchDB in Docker you have exposed both 5984 and 5986 to localhost.
    7. Wait for the replication to complete. In Fauxton you should see the database YYY with the same number of documents reported during the user’s initial replication.
    8. Log out of the instance and clear your data from the developer console (Application -> Clear storage).
    9. Navigate to about:config. Set security.csp.enable to true to re-enable Content Security Policies.

    Regardless, do this too

    To log in as a specific prod user you need to also copy them from the prod _users database into your own local _users database. The simplest way to do this is to just open the DB in Fauxton, find the document and copy it on your clipboard, then create a new document in your local _users DB and paste it in, deleting the _rev property.

    You could also use this opportunity to change the password to something easier to work with locally. To do this, add a password property into the document with the password you want in plain text. CouchDB will convert this to a properly hashed password on save.

    Step 2, run it locally

    First you need to decide if you need a local development environment (unless you already have one, in which case you might as well use it), or are happy to just use Horticulturalist.

    A local development environment will be useful to you if:

    • You want to change code locally
    • You want to see useful, non-minified stack traces, or otherwise browse / step through non-minified code in the browser
    • You want to deploy a version of the code older than 2.14.0, as they are not available in Horticulturalist’s repos.

    Option 1, local development environment

    If you don’t already have a local dev env, follow the instructions on the development setup instructions.

    Then you need to:

    1. Push the code you want to run via COUCH_URL=http://your:admin@localhost:5984/YYY npm run build-dev.
    2. Start API and Sentinel by running COUCH_URL=http://your:admin@localhost:5984/YYY node api/server.

    Once you’ve done all of that you should be able to log in with your user.

    Option 2, horticulturalist

    Follow the instructions on the horticulturalist repo to get it installed. Then:

    1. Make sure that the medic DB doesn’t exist, so that you have a fresh database.
    2. Replicate your local PROD DB into a new medic database using the Fauxton console.
    3. Run horti --local --bootstrap=XXX, where XXX is the version you want to use (maybe the same one as production?)

    You should now be able to log in as that user locally!


    CHT Applications > Quick Guides > Performance > Replication

    Settings for downloading copies of data onto a user’s device.

    -

    Last modified 21.08.2023: feat: remove grunt (c38caa1d)
    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/guides/debugging/secure-sharing-of-developer-instance/index.html b/apps/guides/debugging/secure-sharing-of-developer-instance/index.html index 2d5588e91b..00a5184194 100644 --- a/apps/guides/debugging/secure-sharing-of-developer-instance/index.html +++ b/apps/guides/debugging/secure-sharing-of-developer-instance/index.html @@ -1,9 +1,9 @@ -Securely Sharing Your Development Environment | Community Health Toolkit +Securely Sharing Your Development Environment | Community Health Toolkit

    Securely Sharing Your Development Environment

    Use a publicly accessible Linux web server to forward https requests to your development environment

    Overview

    When using a local development environment, you may want to share your work with other collaborators. By using a publicly accessible web server, you can receive the secure https requests and forward them back to your CHT instance which doesn’t have https set up:

    Once you have this web server set up, you may continue to use it whenever you want by simply reconnecting to it via the secure tunnel.

    Prerequisites

    This guide assumes:

    • You have a local dev instance set up of cht-core
    • You have the generic Medic app installed on your Android device. This version allows you to enter a custom CHT URL on first run.
    • You have an Ubuntu >18.04 server with a public IP and a DNS entry that you can SSH into and have sudo on
    • You have Apache >2.4.29 installed on the Ubuntu server and can add a new vhost to it, including an SSL cert. (nginx could be used instead as well, but not covered here)
    • You have certbot installed from letsencrypt.org

    The steps in this guide can be done on any of the cheap server providers out there (Digital Ocean has a $5/mo server).

    Steps

    1. Create a DNS entry. Let’s assume it’s cht.domain.com. It should point to the IP of your Ubuntu server. If you do not already have a domain name with DNS services that you can use, you can sign up for a free service to do this like noip.com.

    2. On your Ubuntu server, create a new apache vhost in /etc/apache2/sites-available/100-cht.domain.com.conf with the following contents:

      <VirtualHost *:80>
      + Create project issue

    Securely Sharing Your Development Environment

    Use a publicly accessible Linux web server to forward https requests to your development environment

    Overview

    When using a local development environment, you may want to share your work with other collaborators. By using a publicly accessible web server, you can receive the secure https requests and forward them back to your CHT instance which doesn’t have https set up:

    Once you have this web server set up, you may continue to use it whenever you want by simply reconnecting to it via the secure tunnel.

    Prerequisites

    This guide assumes:

    • You have a local dev instance set up of cht-core
    • You have the generic Medic app installed on your Android device. This version allows you to enter a custom CHT URL on first run.
    • You have an Ubuntu >18.04 server with a public IP and a DNS entry that you can SSH into and have sudo on
    • You have Apache >2.4.29 installed on the Ubuntu server and can add a new vhost to it, including an SSL cert. (nginx could be used instead as well, but not covered here)
    • You have certbot installed from letsencrypt.org

    The steps in this guide can be done on any of the cheap server providers out there (Digital Ocean has a $5/mo server).

    Steps

    1. Create a DNS entry. Let’s assume it’s cht.domain.com. It should point to the IP of your Ubuntu server. If you do not already have a domain name with DNS services that you can use, you can sign up for a free service to do this like noip.com.

    2. On your Ubuntu server, create a new apache vhost in /etc/apache2/sites-available/100-cht.domain.com.conf with the following contents:

      <VirtualHost *:80>
           ServerName cht.domain.com
           RewriteEngine on
           RewriteRule (.*) https://cht.domain.com%{REQUEST_URI}
      @@ -324,7 +324,8 @@
       </VirtualHost>
       </IfModule>
       
    3. Enable the new site: a2ensite 100-cht.domain.com

    4. Restart apache and ensure there’s no errors: apachectl restart

    5. Create the TLS certificate: certbot -d cht.domain.com

    6. When prompted choose no redirect: “No redirect - Make no further changes to the webserver configuration.”

    7. Restart apache and ensure there’s no errors: apachectl restart

    8. In a browser, test that you can connect to your server with no errors at https://cht.domain.com (you may get a 500 error, but you shouldn’t get any TLS errors)

    9. Ensure your cht-core local dev instance is running by going to http://localhost:5988/

    10. On your local dev box, set up the SSH tunnel with: ssh -NT -R 8081:127.0.0.1:5988 cht.domain.com

    11. This assumes your local username is the same as it is on cht.domain.com. This command will hang and you may exit when down with ctrl + c

    12. In a browser, test again that you now see your local dev instance and it loads correctly at https://cht.domain.com

    13. If needed, reset the Medic app on your phone so that it prompts which instance to use

    14. In the app on your phone, choose “custom” for which instance to use and enter https://cht.domain.com. You should now see your local dev instance in the CHT Android device. Happy testing!

    Tunnel command breakdown

    From the SSH command in step 10 above:

    1. 8081 - Remote port on cht.domain.com to listen to. This is the same port that apache redirects to in step 2 above.
    2. 127.0.0.1 - Host to send forwarded traffic to. In this case, your local machine.
    3. 5988 - Local port where traffic from step one will be sent. In this case, your instance of the CHT
    4. cht.domain.com - Public domain where you have an SSH account and we’ll attach port 8081 to from step 1.
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/guides/debugging/sharing-4x-logs/index.html b/apps/guides/debugging/sharing-4x-logs/index.html index e6f575e1ea..5980cdb864 100644 --- a/apps/guides/debugging/sharing-4x-logs/index.html +++ b/apps/guides/debugging/sharing-4x-logs/index.html @@ -1,9 +1,9 @@ -Sharing 4.x Logs | Community Health Toolkit +Sharing 4.x Logs | Community Health Toolkit

    Sharing 4.x Logs

    How to easily share logs from your CHT 4.x instance to get support

    CHT 4.x moves from a monolithic container MedicOS to discrete containers, each service hosting one service of the CHT. When troubleshooting an issue with your CHT instance, it can be hard to list each container, see it’s status, gather up logs for each container and then share all this information with Medic or other support staff. To ease this pain, a script was written which automates the process.

    Prerequisites

    This assumes you’re running CHT 4.x have access to the command line on the server where it’s running locally or via SSH. While the script will work with CHT 3.x instances, the amount of logs that a docker logs call yields isn’t very helpful. To see more about CHT 3.x logs, see “Investigating logs inside Medic OS”

    This guide also assumes you have the CHT Core repo checked out so that you have a copy of the compress_and_archive_docker_logs.sh script. If you do not have it checked out, you can manually create a local copy with this curl command:

    curl -o compress_and_archive_docker_logs.sh https://raw.githubusercontent.com/medic/cht-core/master/scripts/compress_and_archive_docker_logs.sh
    + Create project issue

    Sharing 4.x Logs

    How to easily share logs from your CHT 4.x instance to get support

    CHT 4.x moves from a monolithic container MedicOS to discrete containers, each service hosting one service of the CHT. When troubleshooting an issue with your CHT instance, it can be hard to list each container, see it’s status, gather up logs for each container and then share all this information with Medic or other support staff. To ease this pain, a script was written which automates the process.

    Prerequisites

    This assumes you’re running CHT 4.x have access to the command line on the server where it’s running locally or via SSH. While the script will work with CHT 3.x instances, the amount of logs that a docker logs call yields isn’t very helpful. To see more about CHT 3.x logs, see “Investigating logs inside Medic OS”

    This guide also assumes you have the CHT Core repo checked out so that you have a copy of the compress_and_archive_docker_logs.sh script. If you do not have it checked out, you can manually create a local copy with this curl command:

    curl -o compress_and_archive_docker_logs.sh https://raw.githubusercontent.com/medic/cht-core/master/scripts/compress_and_archive_docker_logs.sh
     chmod +x compress_and_archive_docker_logs.sh
     

    Calling the script

    The script defaults to getting the past 24 hours of logs and can be called from anywhere on your system as long as you specify the full path to the script. Here it’s being called from with in the cht-core/scripts directory:

    ./compress_and_archive_docker_logs.sh
     

    If you’d liked to get more logs than the most recent 24 hours, pass in an argument of hours. Here we’re asking for 2 days worth of logs by using 48 as the argument:

    ./compress_and_archive_docker_logs.sh 48
    @@ -334,7 +334,8 @@
     Quick Guides >
     Debugging >
     Browser and Phone Logs

    How to obtain Android and browser client logs

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/guides/forms/additional-docs/index.html b/apps/guides/forms/additional-docs/index.html index 917095569f..c16bc4729d 100644 --- a/apps/guides/forms/additional-docs/index.html +++ b/apps/guides/forms/additional-docs/index.html @@ -1,9 +1,9 @@ -Creating Additional Docs from App Forms | Community Health Toolkit +Creating Additional Docs from App Forms | Community Health Toolkit

    Creating Additional Docs from App Forms

    Integration for sending and receiving SMS

    In version 2.13.0 and higher, you can configure app forms to generate additional docs upon submission. You can create one or more docs using variations on the configuration described below. One case where this can be used is to register a newborn from a delivery report, as shown below. First, here is an overview of what you can do and how the configuration should look in XML:

    Extra Docs

    Extra docs can be added by defining structures in the model with the attribute

    db-doc="true"
    + Create project issue

    Creating Additional Docs from App Forms

    Integration for sending and receiving SMS

    In version 2.13.0 and higher, you can configure app forms to generate additional docs upon submission. You can create one or more docs using variations on the configuration described below. One case where this can be used is to register a newborn from a delivery report, as shown below. First, here is an overview of what you can do and how the configuration should look in XML:

    Extra Docs

    Extra docs can be added by defining structures in the model with the attribute

    db-doc="true"
     

    Example Form Model

    <data>
       <root_prop_1>val A</root_prop_1>
       <other_doc db-doc="true">
    @@ -434,7 +434,8 @@
     Reference >
     forms/ >
     contact

    Contact Forms: Used for creating and editing people and places

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/forms/app-form-sms/index.html b/apps/guides/forms/app-form-sms/index.html index 02c9ca0d83..3817d6b356 100644 --- a/apps/guides/forms/app-form-sms/index.html +++ b/apps/guides/forms/app-form-sms/index.html @@ -1,9 +1,9 @@ -Making Calls and Sending SMS from App Forms | Community Health Toolkit +Making Calls and Sending SMS from App Forms | Community Health Toolkit

    Making Calls and Sending SMS from App Forms

    Trigger calls and SMS from within the form, or send an SMS once submitted.

    Triggering Calls and SMS

    When an XForm is loaded on a phone you can start a phone call or trigger the sending of an SMS within the form itself. This can be useful if within a task or assessment, you want to tell the user to contact a patient, or perhaps a health worker at a facility.

    To set up the call or SMS you’ll need to create a link with tel: or sms: within a note field. To create the link, use the markdown link format, eg [Call Patient](tel:+2547009875000). You can specify the content of the SMS by using the body parameter, eg [Send SMS](sms://+25470098765000?body=Hello World!).

    The phone number and message can be generated from fields within the XForm. For instance, if you have patient_name, patient_phone, and message fields, you can generate the SMS as follows:

    Making Calls and Sending SMS from App Forms

    Trigger calls and SMS from within the form, or send an SMS once submitted.

    Triggering Calls and SMS

    When an XForm is loaded on a phone you can start a phone call or trigger the sending of an SMS within the form itself. This can be useful if within a task or assessment, you want to tell the user to contact a patient, or perhaps a health worker at a facility.

    To set up the call or SMS you’ll need to create a link with tel: or sms: within a note field. To create the link, use the markdown link format, eg [Call Patient](tel:+2547009875000). You can specify the content of the SMS by using the body parameter, eg [Send SMS](sms://+25470098765000?body=Hello World!).

    The phone number and message can be generated from fields within the XForm. For instance, if you have patient_name, patient_phone, and message fields, you can generate the SMS as follows:

    • XLSForm [Send SMS to ${patient_name}](sms://${patient_phone}?body=${message})

    • XForm [Send SMS to <output value=" /data/patient_name "/>](sms://<output value=" /data/patient_phone "/>?body=<output value=" /data/message "/>)

    If you want to use a button to make the action more obvious, this can be done using HTML and CSS within the note:

    [<span style='background-color: #CC0000; color:white; padding: 1em; text-decoration: none; '>Call the patient</span>](tel:${patient_phone})
     

    Note that the SMS link notation can be interpreted differently from one phone to another. Some devices work well with sms:${phone}?body=${message}, others with sms://${phone}?body=${message}. You may find these SMS link tests helpful in determining what works on devices for your deployment.

    Sending reports as SMS

    To define that an XForm should be converted to an SMS, add the field xml2sms to the form’s CouchDB doc and assign it a truthy value (either a boolean or a string).
    When submitting such a form, along with creating the report document, the app will try to compact the report’s content into an SMS that would be sent to the configured Gateway phone number.

    There are two formats available - either using the ODK’s compact record representation for SMS, or Medic’s custom format. @@ -350,7 +350,8 @@ Reference > forms/ > contact

    Contact Forms: Used for creating and editing people and places

    -

    Last modified 27.06.2023: chore: improve grammar (13c416e3)
    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/guides/forms/form-inputs/index.html b/apps/guides/forms/form-inputs/index.html index d92665571f..298668689f 100644 --- a/apps/guides/forms/form-inputs/index.html +++ b/apps/guides/forms/form-inputs/index.html @@ -1,9 +1,9 @@ -Input data available in forms | Community Health Toolkit +Input data available in forms | Community Health Toolkit

    Input data available in forms

    Data accessible from within CHT forms

    CHT forms have access to varying amounts of input data depending on the type of form and its source.

    contact forms

    Available data:

    Initial contact data in contact forms

    Create forms

    Forms for adding contacts have access to a small group of fields contained in a top-level group that is named for the contact_type id of the contact being added (so person, clinic, etc). (This is the same group used to create the contact document when the form is completed.)

    typenamelabelhint
    begin grouppersonNO_LABEL
    hiddenparentParent IdContains the doc id for the new contact’s parent contact doc.
    hiddencontact_typeContact TypeThe contact_type id of the contact.
    end group

    Edit forms

    Forms for editing contacts have access to all the contact’s current data. These fields are contained in a top-level group that is named for the contact_type id of the contact being added (so person, clinic, etc). If fields in the top-level group are edited by the form, these changes will be saved to the contact’s doc.

    In addition, the contact’s parent data is hydrated so that the form has access to the data stored on the parent contact doc in the parent group.

    typenamelabelhint
    begin grouppersonNO_LABEL
    begin groupparentParentContains the data for the contact’s parent contact doc.
    hiddennameParent nameThe name of the parent contact
    end groupparent
    end group

    app forms

    Available data:

    Form source

    If a form is created from the “People” tab, inputs/source will be set to “contact”. If a form is created from a task, inputs/source will be set to “task”.

    inputs data for contact in app forms

    app forms with a contact in context have access to that contact’s data in the inputs/contact group.

    The contact group contains all the fields from the doc of the contact in context. If a place is in context, the primary person for that place will be hydrated in the inputs/contact/contact group. Alternatively, when a person is in context, the parent place for the person will be hydrated in inputs/contact/parent.

    contact-summary data

    app forms with a contact in context can access the contact-summary data associated with the contact. This is done by referencing an instance named contact-summary. E.g. instance('contact-summary')/context/${variable}. See the reference documentation for more information.

    inputs data from task

    app forms created via a task have access to any data supplied by the task in the inputs group.


    user data

    Both app and contact forms can access the current user’s data at inputs/user. The data provided is simply the user-settings doc for the user (e.g. org.couchdb.user:username) plus an additional language field that contains the user’s currently selected language code.

    Example of saving user data as metadata on a report

    typenamelabelcalculation
    begin groupinputsNO_LABEL
    begin groupuserNO_LABEL
    stringcontact_idUser’s contact id
    stringfacility_idId for user’s facility
    namenameUsername
    end group
    end group
    calculatecreated_byUsername that created report../inputs/user/name
    calculatecreated_by_person_uuidUUID that created report../inputs/user/contact_id
    calculatecreated_by_place_uuidFacility of user that created report../inputs/user/facility_id

    Loading the user’s contact data

    While the inputs/user group does not contain the user’s contact data, it does contain the user’s contact_id which can be used to load the contact doc via a contact selector.

    Contact selector

    Using a contact selector allows you to load data from the selected contact (person or place). The contact’s id can be provided by the form or the user can search for an existing contact.

    To select a contact in a form, create a field with the type string and set the appearance to select-contact type-{{contact_type_1}} type-{{contact_type_2}} .... Setting multiple contact_type ids allows the user to search among multiple types of contacts. If no contact type appearance is specified then all contact types will be queried when searching.

    Searching for a contact

    When a contact selector question is visible in a form, the user can search for a contact by typing in the search box.

    A value can be pre-selected for the search box via a calculate expression. By default, the user can change the selected value, but this can be prevented by setting read_only = true.

    typenamelabelappearancecalculate
    stringperson_idPerson IDselect-contact type-person
    stringhousehold_idHousehold IDselect-contact type-clinic../inputs/contact/parent/_id

    Loading descendants of the current contact

    Use the appearance descendant-of-current-contact to load the current contact’s descendants when opening an app form or contact form from the “People” tab.

    typenamelabelappearance
    stringhousehold_membersHousehold’s membersselect-contact type-person descendant-of-current-contact

    Loading additional contact data

    Additional data about the contact can be loaded by adding fields to the same group as the contact selector. The field name must match the name of a field on the contact doc. The data will be loaded when the contact is selected and will overwrite any existing data in the field.

    typenamelabelappearance
    begin groupselected_personNO_LABEL
    stringperson_idPerson IDselect-contact type-person
    stringnameName of selected person
    end groupselected_person

    Loading contact data for use in other expressions

    One powerful way to use the contact selector is to automatically load data about an existing contact for usage in various expressions within the form. This can be done with a combination of functionality we have already seen!

    In the following example, we are loading the name of the current user from the associated contact doc:

    typenamelabelappearancecalculate
    begin groupinputsNO_LABEL
    begin groupuserNO_LABEL
    hiddencontact_idUser’s Contact ID
    end groupuser
    end groupinputs
    begin groupintroNO_LABELfield-list
    begin groupcurrent_userNO_LABEL
    string_idUser’s Contact IDhidden select-contact type-person${contact_id}
    stringnameUser’s namehidden
    end groupcurrent_user
    calculateuser_nameSupervisor name../current_user/name
    notewelcomeWelcome ${user_name}!
    end groupintro

    Conditionally selecting/loading contact data for the current contact

    As noted above, when opening an app form with a contact in context (e.g. when opening the form from the “People” tab), the contact’s data will be available in the inputs/contact group. However, this data is not available when opening the form from the “Reports” tab since there is no contact in context.

    A common pattern for enabling consistent form behavior in this case is to use a contact selector in the inputs/contact group and make the inputs group only relevant when the default source value is not overridden (the source field is populated with specific values when the form is opened from the “People” tab or from a task). With this pattern, the contact selector will only be visible when the form is opened from the “Reports” tab and the user can select the contact they want to report on. This will ensure that the inputs/contact data is always available regardless of how the form is opened.

    Then, this contact data can be used to link the report created from the form to the person or place in context (or the person selected when opening the form). Getting the values for _id or patient_id and setting them to patient_id or patient_uuid on the final report will link that report to the contact. Then the report will be displayed on the contact’s summary page.

    typenamelabelappearancecalculaterelevantdefault
    begin groupinputsNO_LABELfield-list./source = ‘user’
    hiddensourceSourceuser
    begin groupcontactNO_LABEL
    string_idPatient IDselect-contact type-person
    stringpatient_idMedic IDhidden
    end groupcontact
    end groupinputs
    calculatepatient_uuidPatient UUID../contact/_id
    calculatepatient_idPatient ID../contact/patient_id

    CHT Applications > + Create project issue

    Input data available in forms

    Data accessible from within CHT forms

    CHT forms have access to varying amounts of input data depending on the type of form and its source.

    contact forms

    Available data:

    Initial contact data in contact forms

    Create forms

    Forms for adding contacts have access to a small group of fields contained in a top-level group that is named for the contact_type id of the contact being added (so person, clinic, etc). (This is the same group used to create the contact document when the form is completed.)

    typenamelabelhint
    begin grouppersonNO_LABEL
    hiddenparentParent IdContains the doc id for the new contact’s parent contact doc.
    hiddencontact_typeContact TypeThe contact_type id of the contact.
    end group

    Edit forms

    Forms for editing contacts have access to all the contact’s current data. These fields are contained in a top-level group that is named for the contact_type id of the contact being added (so person, clinic, etc). If fields in the top-level group are edited by the form, these changes will be saved to the contact’s doc.

    In addition, the contact’s parent data is hydrated so that the form has access to the data stored on the parent contact doc in the parent group.

    typenamelabelhint
    begin grouppersonNO_LABEL
    begin groupparentParentContains the data for the contact’s parent contact doc.
    hiddennameParent nameThe name of the parent contact
    end groupparent
    end group

    app forms

    Available data:

    Form source

    If a form is created from the “People” tab, inputs/source will be set to “contact”. If a form is created from a task, inputs/source will be set to “task”.

    inputs data for contact in app forms

    app forms with a contact in context have access to that contact’s data in the inputs/contact group.

    The contact group contains all the fields from the doc of the contact in context. If a place is in context, the primary person for that place will be hydrated in the inputs/contact/contact group. Alternatively, when a person is in context, the parent place for the person will be hydrated in inputs/contact/parent.

    contact-summary data

    app forms with a contact in context can access the contact-summary data associated with the contact. This is done by referencing an instance named contact-summary. E.g. instance('contact-summary')/context/${variable}. See the reference documentation for more information.

    inputs data from task

    app forms created via a task have access to any data supplied by the task in the inputs group.


    user data

    Both app and contact forms can access the current user’s data at inputs/user. The data provided is simply the user-settings doc for the user (e.g. org.couchdb.user:username) plus an additional language field that contains the user’s currently selected language code.

    Example of saving user data as metadata on a report

    typenamelabelcalculation
    begin groupinputsNO_LABEL
    begin groupuserNO_LABEL
    stringcontact_idUser’s contact id
    stringfacility_idId for user’s facility
    namenameUsername
    end group
    end group
    calculatecreated_byUsername that created report../inputs/user/name
    calculatecreated_by_person_uuidUUID that created report../inputs/user/contact_id
    calculatecreated_by_place_uuidFacility of user that created report../inputs/user/facility_id

    Loading the user’s contact data

    While the inputs/user group does not contain the user’s contact data, it does contain the user’s contact_id which can be used to load the contact doc via a contact selector.

    Contact selector

    Using a contact selector allows you to load data from the selected contact (person or place). The contact’s id can be provided by the form or the user can search for an existing contact.

    To select a contact in a form, create a field with the type string and set the appearance to select-contact type-{{contact_type_1}} type-{{contact_type_2}} .... Setting multiple contact_type ids allows the user to search among multiple types of contacts. If no contact type appearance is specified then all contact types will be queried when searching.

    Searching for a contact

    When a contact selector question is visible in a form, the user can search for a contact by typing in the search box.

    A value can be pre-selected for the search box via a calculate expression. By default, the user can change the selected value, but this can be prevented by setting read_only = true.

    typenamelabelappearancecalculate
    stringperson_idPerson IDselect-contact type-person
    stringhousehold_idHousehold IDselect-contact type-clinic../inputs/contact/parent/_id

    Loading descendants of the current contact

    Use the appearance descendant-of-current-contact to load the current contact’s descendants when opening an app form or contact form from the “People” tab.

    typenamelabelappearance
    stringhousehold_membersHousehold’s membersselect-contact type-person descendant-of-current-contact

    Loading additional contact data

    Additional data about the contact can be loaded by adding fields to the same group as the contact selector. The field name must match the name of a field on the contact doc. The data will be loaded when the contact is selected and will overwrite any existing data in the field.

    typenamelabelappearance
    begin groupselected_personNO_LABEL
    stringperson_idPerson IDselect-contact type-person
    stringnameName of selected person
    end groupselected_person

    Loading contact data for use in other expressions

    One powerful way to use the contact selector is to automatically load data about an existing contact for usage in various expressions within the form. This can be done with a combination of functionality we have already seen!

    In the following example, we are loading the name of the current user from the associated contact doc:

    typenamelabelappearancecalculate
    begin groupinputsNO_LABEL
    begin groupuserNO_LABEL
    hiddencontact_idUser’s Contact ID
    end groupuser
    end groupinputs
    begin groupintroNO_LABELfield-list
    begin groupcurrent_userNO_LABEL
    string_idUser’s Contact IDhidden select-contact type-person${contact_id}
    stringnameUser’s namehidden
    end groupcurrent_user
    calculateuser_nameSupervisor name../current_user/name
    notewelcomeWelcome ${user_name}!
    end groupintro

    Conditionally selecting/loading contact data for the current contact

    As noted above, when opening an app form with a contact in context (e.g. when opening the form from the “People” tab), the contact’s data will be available in the inputs/contact group. However, this data is not available when opening the form from the “Reports” tab since there is no contact in context.

    A common pattern for enabling consistent form behavior in this case is to use a contact selector in the inputs/contact group and make the inputs group only relevant when the default source value is not overridden (the source field is populated with specific values when the form is opened from the “People” tab or from a task). With this pattern, the contact selector will only be visible when the form is opened from the “Reports” tab and the user can select the contact they want to report on. This will ensure that the inputs/contact data is always available regardless of how the form is opened.

    Then, this contact data can be used to link the report created from the form to the person or place in context (or the person selected when opening the form). Getting the values for _id or patient_id and setting them to patient_id or patient_uuid on the final report will link that report to the contact. Then the report will be displayed on the contact’s summary page.

    typenamelabelappearancecalculaterelevantdefault
    begin groupinputsNO_LABELfield-list./source = ‘user’
    hiddensourceSourceuser
    begin groupcontactNO_LABEL
    string_idPatient IDselect-contact type-person
    stringpatient_idMedic IDhidden
    end groupcontact
    end groupinputs
    calculatepatient_uuidPatient UUID../contact/_id
    calculatepatient_idPatient ID../contact/patient_id

    CHT Applications > Reference > forms/

    Forms: All the App forms, Contact forms, and Collect forms

    CHT Applications > Reference > @@ -308,7 +308,8 @@ Quick Guides > Tasks > Passing Data from Tasks to Forms

    Demonstrates how to pass data into an application form via tasks

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/forms/google-drive/index.html b/apps/guides/forms/google-drive/index.html index 4bd2957a41..f4b2ea3478 100644 --- a/apps/guides/forms/google-drive/index.html +++ b/apps/guides/forms/google-drive/index.html @@ -1,9 +1,9 @@ -Fetching forms from Google Drive | Community Health Toolkit +Fetching forms from Google Drive | Community Health Toolkit

    Fetching forms from Google Drive

    Using cht-conf to obtain form files stored in Google Drive

    To work collaboratively on form design it can be helpful to keep XLSForms in Google Drive. The fetch-forms-from-google-drive action downloads these XLSForms so that they can be converted to XForms and uploaded to your CHT app.

    This action requires the following files in the top-level folder of your CHT app config: forms-on-google-drive.json and .gdrive.secrets.json.

    forms-on-google-drive.json

    This JSON file contains key value pairs, where the key is the relative path and name for the downloaded file, and the value is the file’s Google Drive File ID.

    To get the File ID for the form open the form in Google Sheets. The URL will look something like https://docs.google.com/spreadsheets/d/12345ABCDEF/edit#gid=555666888 . The ID is the middle portion 12345ABCDEF

    Fetching forms from Google Drive

    Using cht-conf to obtain form files stored in Google Drive

    To work collaboratively on form design it can be helpful to keep XLSForms in Google Drive. The fetch-forms-from-google-drive action downloads these XLSForms so that they can be converted to XForms and uploaded to your CHT app.

    This action requires the following files in the top-level folder of your CHT app config: forms-on-google-drive.json and .gdrive.secrets.json.

    forms-on-google-drive.json

    This JSON file contains key value pairs, where the key is the relative path and name for the downloaded file, and the value is the file’s Google Drive File ID.

    To get the File ID for the form open the form in Google Sheets. The URL will look something like https://docs.google.com/spreadsheets/d/12345ABCDEF/edit#gid=555666888 . The ID is the middle portion 12345ABCDEF

    {
       "app/sample_report.xlsx": "12345ABCDEF",
       "contact/person-create.xlsx": "ABCDEF123456",
     }
    @@ -317,7 +317,8 @@
     

    You will be prompted for an access code from your authenticated user. Follow the prompts through Google’s authentication page and copy the access code into your command line. The forms will then be downloaded to the locations set in the forms-on-google-drive.json file.


    CHT Applications > Quick Guides > Forms

    Managing and using forms in the CHT

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/forms/index.html b/apps/guides/forms/index.html index 4a62b7d63b..5af1a43674 100644 --- a/apps/guides/forms/index.html +++ b/apps/guides/forms/index.html @@ -1,9 +1,9 @@ -Building and Maintaining Forms | Community Health Toolkit +Building and Maintaining Forms | Community Health Toolkit

    Building and Maintaining Forms

    Managing and using forms in the CHT

    Configuring UHC Mode

    How to enable Universal Health Coverage monitoring with UHC Mode

    Creating Additional Docs from App Forms

    Integration for sending and receiving SMS

    Making Calls and Sending SMS from App Forms

    Trigger calls and SMS from within the form, or send an SMS once submitted.

    Fetching forms from Google Drive

    Using cht-conf to obtain form files stored in Google Drive

    Input data available in forms

    Data accessible from within CHT forms

    Including Multimedia in Forms

    How to include multimedia files in forms

    Tracking Wealth Quintiles

    How to track wealth quintiles on the profile of each family member in the household

    Customizing Titles in the Reports List

    Customizing the title shown in the Reports list

    Versioning forms

    Record the version of the form when creating reports

    -

    Last modified 05.06.2020: Categorized guides (d26e182e)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/forms/index.xml b/apps/guides/forms/index.xml index 567347aed1..93a7c7b199 100644 --- a/apps/guides/forms/index.xml +++ b/apps/guides/forms/index.xml @@ -1,1084 +1,15 @@ -Community Health Toolkit – Building and Maintaining Formshttps://docs.communityhealthtoolkit.org/apps/guides/forms/Recent content in Building and Maintaining Forms on Community Health ToolkitHugo -- gohugo.ioenApps: Configuring UHC Modehttps://docs.communityhealthtoolkit.org/apps/guides/forms/uhc-mode/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/uhc-mode/ -<p><em>Introduced in v2.18.0</em></p> -<figure class="right col-6 col-lg-3"><a href="UHC.gif"> -<img src="UHC.gif" -alt="UHC Mode screenshot"/> </a> -</figure> -<p>The CHT&rsquo;s <a href="https://docs.communityhealthtoolkit.org/apps/features/uhc-mode/">UHC Mode</a> empowers CHWs to provide equitable and timely care to families in their catchment area. The Community Health Toolkit supports this use-case by displaying the number of visits made to a household and highlighting households which haven&rsquo;t met their visit goal in red at the top of the contact list.</p> -<p>The date last visited is colored red whenever the date is 30 days or more in the past. The date last visited is displayed as a relative date. This period is described in terms of &ldquo;days&rdquo; up until two months, so &ldquo;Visited 3 days ago&rdquo; or &ldquo;Visited 36 days ago&rdquo;. After two months, we simply say &ldquo;2 months&rdquo; or &ldquo;3 months&rdquo;.</p> -<h2 id="household-visits">Household Visits</h2> -<figure class="right col-6 col-lg-3"><a href="sort-dropdown.png"> -<img src="sort-dropdown.png" -alt="Contact sorting screenshot"/> </a> -</figure> -<p>The date of the last visit is displayed and contacts can be sorted by last visit date to allow the CHW plan their work.</p> -<p>&ldquo;Date last visited&rdquo; and &ldquo;Visits this month&rdquo; are different data points:</p> -<ul> -<li>Date last visited is calculated based upon a rolling count of &ldquo;how many days ago&rdquo; and is not tied to the calendar at all.</li> -<li>Visits this month are tied directly to the calendar.</li> -</ul> -<h2 id="configuration">Configuration</h2> -<p>Any patient- or household-level form or forms can be used to update the date last visited. You may run into performance issues if you configure this to look at forms submitted very frequently. For example, five forms submitted only once a month would work better than two forms submitted every day.</p> -<h3 id="uhc-permissions">UHC Permissions</h3> -<p>To see the date last visited, grant the relevant user role (usually CHW) the <code>can_view_last_visited_date</code> permission. Once that permission is enabled, you&rsquo;ll be able to display the date the household was last visited in the list.</p> -<p>To view UHC metrics, the <code>can_view_uhc_stats</code> permission needs to be granted to the CHW user role.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;permissions&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;can_view_last_visited_date&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> <span style="color:#4e9a06">&#34;chwUserRole&#34;</span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span><span style="color:#a40000">,</span> -</span></span></code></pre></div><h3 id="forms">Forms</h3> -<p>Indicate which forms should be included in the calculation of the date last visited. You can use forms at the person or household level. Add the <code>visited_contact_uuid</code> field to forms to track visits. This should be a <code>calculate</code> field that contains the place UUID of the visited contact. Ensure this is a top-level field and not in any group. When the form is submitted it would be seen as <code>doc.fields.visited_contact_uuid</code>.</p> -<h3 id="settings">Settings</h3> -<p>Information on when to begin counting visits, household visit goals, and default contact sorting is defined in <code>base_settings.json</code>.</p> -<table> -<thead> -<tr> -<th>Setting</th> -<th>Description</th> -<th>Default</th> -<th>Version</th> -</tr> -</thead> -<tbody> -<tr> -<td>uhc.contacts_default_sort</td> -<td><ul><li>&ldquo;alpha&rdquo;: Sort contacts alphanumerically</li><li>&ldquo;last_visited_date&rdquo;: sort contacts by the date they were most recently visited.</li></ul></td> -<td>&ldquo;alpha&rdquo;</td> -<td>2.18.0</td> -</tr> -<tr> -<td>uhc.visit_count.month_start_date</td> -<td>The date of each month when the visit count is reset to 0.</td> -<td>1</td> -<td>2.18.0</td> -</tr> -<tr> -<td>uhc.visit_count.visit_count_goal</td> -<td>The monthly visit count goal.</td> -<td>0</td> -<td>2.18.0</td> -</tr> -</tbody> -</table>Apps: Creating Additional Docs from App Formshttps://docs.communityhealthtoolkit.org/apps/guides/forms/additional-docs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/additional-docs/ -<p>In version 2.13.0 and higher, you can configure app forms to generate additional docs upon submission. You can create one or more docs using variations on the configuration described below. One case where this can be used is to register a newborn from a delivery report, as shown below. First, here is an overview of what you can do and how the configuration should look in XML:</p> -<h2 id="extra-docs">Extra Docs</h2> -<p>Extra docs can be added by defining structures in the model with the attribute</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>db-doc<span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;true&#34;</span> -</span></span></code></pre></div> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -You must have lower-case <code>true</code> in your XLSform, even though Excel will default to <code>TRUE</code>. -</div> -<h3 id="example-form-model">Example Form Model</h3> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">&lt;data&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;root_prop_1&gt;</span>val A<span style="color:#204a87;font-weight:bold">&lt;/root_prop_1&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;other_doc</span> <span style="color:#c4a000">db-doc=</span><span style="color:#4e9a06">&#34;true&#34;</span><span style="color:#204a87;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;type&gt;</span>whatever<span style="color:#204a87;font-weight:bold">&lt;/type&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;other_prop&gt;</span>val B<span style="color:#204a87;font-weight:bold">&lt;/other_prop&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/other_doc&gt;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">&lt;/data&gt;</span> -</span></span></code></pre></div><h3 id="resulting-docs">Resulting Docs</h3> -<p>Report (as before):</p> -<pre tabindex="0"><code>{ -_id: &#39;...&#39;, -_rev: &#39;...&#39;, -type: &#39;report&#39;, -_attachments: { xml: ... ], -fields: { -root_prop_1: &#39;val A&#39;, -} -} -</code></pre><p>Other doc:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;...&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_rev&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;...&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;whatever&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;other_prop&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;val B&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h2 id="linked-docs">Linked Docs</h2> -<ul> -<li>Linked docs can be referred to using the doc-ref attribute, with an xpath. This can be done at any point in the model, e.g.:</li> -</ul> -<h3 id="example-form-model-1">Example Form Model</h3> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">&lt;sickness&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;sufferer</span> <span style="color:#c4a000">db-doc-ref=</span><span style="color:#4e9a06">&#34;/sickness/new&#34;</span><span style="color:#204a87;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;new</span> <span style="color:#c4a000">db-doc=</span><span style="color:#4e9a06">&#34;true&#34;</span><span style="color:#204a87;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;type&gt;</span>person<span style="color:#204a87;font-weight:bold">&lt;/type&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;name&gt;</span>Gómez<span style="color:#204a87;font-weight:bold">&lt;/name&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;original_report</span> <span style="color:#c4a000">db-doc-ref=</span><span style="color:#4e9a06">&#34;/sickness&#34;</span><span style="color:#204a87;font-weight:bold">/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/new&gt;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">&lt;/sickness&gt;</span> -</span></span></code></pre></div><h3 id="resulting-docs-1">Resulting Docs</h3> -<p>Report:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;abc-123&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_rev&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;...&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;report&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;fields&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;sufferer&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;def-456&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>Other doc:</p> -<pre tabindex="0"><code>{ -&#34;_id&#34;: &#34;def-456&#34;, -&#34;_rev&#34;: &#34;...&#34;, -&#34;type&#34;: &#34;person&#34;, -&#34;name&#34;: &#34;Gómez&#34;, -&#34;original_report&#34;: &#34;abc-123&#34; -} -</code></pre><h2 id="repeated-docs">Repeated Docs</h2> -<ul> -<li>Can have references to other docs, including the parent</li> -<li>These currently cannot be linked from other docs, as no provision is made for indexing these docs</li> -</ul> -<h3 id="example-form">Example Form</h3> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">&lt;thing&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;name&gt;</span>Ab<span style="color:#204a87;font-weight:bold">&lt;/name&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;related</span> <span style="color:#c4a000">db-doc=</span><span style="color:#4e9a06">&#34;true&#34;</span><span style="color:#204a87;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;type&gt;</span>relative<span style="color:#204a87;font-weight:bold">&lt;/type&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;name&gt;</span>Bo<span style="color:#204a87;font-weight:bold">&lt;/name&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;parent</span> <span style="color:#c4a000">db-doc-ref=</span><span style="color:#4e9a06">&#34;/thing&#34;</span><span style="color:#204a87;font-weight:bold">/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/related&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;related</span> <span style="color:#c4a000">db-doc=</span><span style="color:#4e9a06">&#34;true&#34;</span><span style="color:#204a87;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;type&gt;</span>relative<span style="color:#204a87;font-weight:bold">&lt;/type&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;name&gt;</span>Ca<span style="color:#204a87;font-weight:bold">&lt;/name&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;parent</span> <span style="color:#c4a000">db-doc-ref=</span><span style="color:#4e9a06">&#34;/thing&#34;</span><span style="color:#204a87;font-weight:bold">/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/related&gt;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">&lt;/artist&gt;</span> -</span></span></code></pre></div><h3 id="resulting-docs-2">Resulting Docs</h3> -<p>Report:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;abc-123&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_rev&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;...&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;report&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;fields&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Ab&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>Other docs:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;...&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_rev&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;...&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;relative&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Bo&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;abc-123&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;...&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_rev&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;...&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;relative&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Ch&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;abc-123&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h2 id="linked-docs-example">Linked Docs Example</h2> -<p>This example shows how you would register a single newborn from a delivery report.</p> -<p>First, the relevant section of the delivery report XLSForm file: -<img src="linked_docs_xlsform.png" alt="Delivery report"></p> -<p>Here is the corresponding portion of XML generated after converting the form:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">&lt;repeat&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;child_repeat</span> <span style="color:#c4a000">db-doc=</span><span style="color:#4e9a06">&#34;true&#34;</span> <span style="color:#c4a000">jr:template=</span><span style="color:#4e9a06">&#34;&#34;</span><span style="color:#204a87;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;child_name/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;child_gender/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;child_doc</span> <span style="color:#c4a000">db-doc-ref=</span><span style="color:#4e9a06">&#34; /delivery/repeat/child_repeat &#34;</span><span style="color:#204a87;font-weight:bold">/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;created_by_doc</span> <span style="color:#c4a000">db-doc-ref=</span><span style="color:#4e9a06">&#34;/delivery&#34;</span><span style="color:#204a87;font-weight:bold">/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;name/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;sex/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;date_of_birth/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;parent&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;_id/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;parent&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;_id/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;parent&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;_id/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/parent&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/parent&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/parent&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;type&gt;</span>person<span style="color:#204a87;font-weight:bold">&lt;/type&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/child_repeat&gt;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">&lt;/repeat&gt;</span> -</span></span></code></pre></div><p>If you&rsquo;ve done your configuration correctly, all you should see when you click on the submitted report from the Reports tab is the <code>child_doc</code> field with an <code>_id</code> that corresponds to the linked doc. In this case, you could look for that <code>_id</code> on the People tab or in the DB itself to confirm that the resulting doc looks correct.</p> -<h2 id="repeated-docs-example">Repeated Docs Example</h2> -<p>This example extends the above example to show how you would register one or multiple newborns from a delivery report. This allows you to handle multiple births.</p> -<p>First, the relevant section of the delivery report XLSForm file: -<img src="repeated_docs_xlsform.png" alt="Delivery report"></p> -<p>Here is the corresponding portion of XML generated after converting the form:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">&lt;repeat&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;child_repeat</span> <span style="color:#c4a000">db-doc=</span><span style="color:#4e9a06">&#34;true&#34;</span> <span style="color:#c4a000">jr:template=</span><span style="color:#4e9a06">&#34;&#34;</span><span style="color:#204a87;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;child_name/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;child_gender/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;created_by_doc</span> <span style="color:#c4a000">db-docs-ref=</span><span style="color:#4e9a06">&#34;/delivery&#34;</span><span style="color:#204a87;font-weight:bold">/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;name/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;sex/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;date_of_birth/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;parent&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;_id/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;parent&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;_id/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;parent&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;_id/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/parent&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/parent&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/parent&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;type&gt;</span>person<span style="color:#204a87;font-weight:bold">&lt;/type&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/child_repeat&gt;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">&lt;/repeat&gt;</span> -</span></span></code></pre></div><p>If you&rsquo;ve done your configuration correctly, all you should see when you click on the submitted report from the Reports tab is that there will be no information on the children created. You will find the created docs as siblings to the subject of the previously submitted report. Each of these child docs will have a field <code>created_by_doc</code> whose value is the <code>_id</code> of the report that created them. You can also look in the DB itself to confirm that the resulting docs look correct.</p>Apps: Making Calls and Sending SMS from App Formshttps://docs.communityhealthtoolkit.org/apps/guides/forms/app-form-sms/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/app-form-sms/ -<h2 id="triggering-calls-and-sms">Triggering Calls and SMS</h2> -<p>When an XForm is loaded on a phone you can start a phone call or trigger the sending of an SMS within the form itself. This can be useful if within a task or assessment, you want to tell the user to contact a patient, or perhaps a health worker at a facility.</p> -<p>To set up the call or SMS you&rsquo;ll need to create a link with <code>tel:</code> or <code>sms:</code> within a <code>note</code> field. To create the link, use the markdown link format, eg <code>[Call Patient](tel:+2547009875000)</code>. You can specify the content of the SMS by using the body parameter, eg <code>[Send SMS](sms://+25470098765000?body=Hello World!)</code>.</p> -<p>The phone number and message can be generated from fields within the XForm. For instance, if you have <code>patient_name</code>, <code>patient_phone</code>, and <code>message</code> fields, you can generate the SMS as follows:</p> -<ul> -<li> -<p><strong>XLSForm</strong> -<code>[Send SMS to ${patient_name}](sms://${patient_phone}?body=${message})</code></p> -</li> -<li> -<p><strong>XForm</strong> -<code>[Send SMS to &lt;output value=&quot; /data/patient_name &quot;/&gt;](sms://&lt;output value=&quot; /data/patient_phone &quot;/&gt;?body=&lt;output value=&quot; /data/message &quot;/&gt;)</code></p> -</li> -</ul> -<p>If you want to use a button to make the action more obvious, this can be done using HTML and CSS within the note:</p> -<pre tabindex="0"><code>[&lt;span style=&#39;background-color: #CC0000; color:white; padding: 1em; text-decoration: none; &#39;&gt;Call the patient&lt;/span&gt;](tel:${patient_phone}) -</code></pre><p>Note that the SMS link notation can be interpreted differently from one phone to another. Some devices work well with <code>sms:${phone}?body=${message}</code>, others with <code>sms://${phone}?body=${message}</code>. You may find <a href="https://web.archive.org/web/20210125031111/https://bradorego.com/test/sms.html">these SMS link tests</a> helpful in determining what works on devices for your deployment.</p> -<h2 id="sending-reports-as-sms">Sending reports as SMS</h2> -<p>To define that an XForm should be converted to an SMS, add the field <code>xml2sms</code> to the form&rsquo;s CouchDB doc and assign it a truthy value (either a boolean or a string).<br> -When submitting such a form, along with creating the report document, the app will try to compact the report&rsquo;s content into an SMS that would be sent to the configured Gateway phone number.</p> -<p>There are two formats available - either using the <a href="https://getodk.github.io/xforms-spec/#compact-record-representation-(for-sms)">ODK&rsquo;s compact record representation for SMS</a>, or Medic&rsquo;s custom format. -When the form compaction fails or returns no content, no SMS will be sent.</p> -<h3 id="odk-compact-record-representation-for-sms">ODK compact record representation for SMS</h3> -<p>When <code>xml2sms</code> field value is Boolean <code>true</code>, the app will try to compact the form using this format. -To get forms sent in this format, follow the <a href="https://getodk.github.io/xforms-spec/#compact-record-representation-(for-sms)">ODK documentation</a>.</p> -<h3 id="medic-custom-sms-representation">Medic Custom SMS representation</h3> -<p>To configure a form to send using Medic&rsquo;s custom SMS definition, the value of <code>xml2sms</code> from the form&rsquo;s CouchDB doc should be a string containing an <a href="https://docs.angularjs.org/guide/expression">Angular expression</a>. -This allows access to the <code>fields</code> property of the <code>data_record</code> doc created when saving the form submission to the database. Extra functions are also provided to make compiling a form submission more simple.</p> -<h4 id="special-functions">Special Functions</h4> -<h5 id="concatargs"><code>concat(...args)</code></h5> -<ul> -<li> -<p><code>...args</code>: 0 or more values to be concatenated.</p> -<pre><code> concat('A', 'bee', 'Sea') =&gt; 'AbeeSea' -</code></pre> -</li> -</ul> -<h5 id="spacedargs"><code>spaced(...args)</code></h5> -<ul> -<li> -<p><code>...args</code>: 0 or more values to be concatenated with spaces between them.</p> -<pre><code> spaced('A', 'bee', 'Sea') =&gt; 'A bee Sea' -</code></pre> -</li> -</ul> -<h5 id="matchval-matchers"><code>match(val, matchers)</code></h5> -<ul> -<li> -<p><code>val</code>: the value to run matches against</p> -</li> -<li> -<p><code>matchers</code>: a string representing values to match and their corresponding outputs</p> -<pre><code> match('a', 'a:Hay,b:bzz,c:see') =&gt; 'Hay' -match('b', 'a:Hay,b:bzz,c:see') =&gt; 'bzz' -match('c', 'a:Hay,b:bzz,c:see') =&gt; 'c' -</code></pre> -</li> -</ul> -<h4 id="examples">Examples</h4> -<h5 id="form-submission-json">Form Submission JSON</h5> -<pre><code>doc.fields = { -s_acc_danger_signs: { -s_acc_danger_sign_seizure: 'no', -s_acc_danger_sign_loss_consiousness: 'yes', -s_acc_danger_sign_unable_drink: 'no', -s_acc_danger_sign_confusion: 'yes', -s_acc_danger_sign_vomit: 'no', -s_acc_danger_sign_chest_indrawing: 'yes', -s_acc_danger_sign_wheezing: 'no', -s_acc_danger_sign_bleeding: 'yes', -s_acc_danger_sign_lathargy: 'no', -has_danger_sign: 'true', -}, -}; -</code></pre> -<h5 id="formdocreport2sms"><code>formDoc.report2sms</code></h5> -<pre><code>concat( -&quot;U5 &quot;, -match(doc.s_acc_danger_signs.has_danger_sign, &quot;true:DANGER, false:NO_DANGER&quot;), -&quot; &quot;, -match(doc.s_acc_danger_signs.s_acc_danger_sign_seizure, &quot;yes:S&quot;), -match(doc.s_acc_danger_signs.s_acc_danger_sign_loss_consiousness, &quot;yes:L&quot;), -match(doc.s_acc_danger_signs.s_acc_danger_sign_unable_drink, &quot;yes:D&quot;), -match(doc.s_acc_danger_signs.s_acc_danger_sign_confusion, &quot;yes:C&quot;), -match(doc.s_acc_danger_signs.s_acc_danger_sign_vomit, &quot;yes:V&quot;), -match(doc.s_acc_danger_signs.s_acc_danger_sign_chest_indrawing, &quot;yes:I&quot;), -match(doc.s_acc_danger_signs.s_acc_danger_sign_wheezing, &quot;yes:W&quot;), -match(doc.s_acc_danger_signs.s_acc_danger_sign_bleeding, &quot;yes:B&quot;), -match(doc.s_acc_danger_signs.s_acc_danger_sign_lathargy, &quot;yes:Y&quot;) -) -</code></pre> -<h5 id="sms-content">SMS content</h5> -<pre><code>U5 DANGER LCIB -</code></pre>Apps: Fetching forms from Google Drivehttps://docs.communityhealthtoolkit.org/apps/guides/forms/google-drive/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/google-drive/ -<p>To work collaboratively on form design it can be helpful to keep XLSForms in Google Drive. The <code>fetch-forms-from-google-drive</code> action downloads these XLSForms so that they can be converted to XForms and uploaded to your CHT app.</p> -<p>This action requires the following files in the top-level folder of your CHT app config: <code>forms-on-google-drive.json</code> and <code>.gdrive.secrets.json</code>.</p> -<h2 id="forms-on-google-drivejson">forms-on-google-drive.json</h2> -<p>This JSON file contains key value pairs, where the key is the relative path and name for the downloaded file, and the value is the file&rsquo;s Google Drive File ID.</p> -<p>To get the File ID for the form open the form in Google Sheets. The URL will look something like <code>https://docs.google.com/spreadsheets/d/12345ABCDEF/edit#gid=555666888</code> . The ID is the middle portion <code>12345ABCDEF</code></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;app/sample_report.xlsx&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;12345ABCDEF&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;contact/person-create.xlsx&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;ABCDEF123456&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h2 id="gdrivesecretsjson">.gdrive.secrets.json</h2> -<p>This file contains authentication secrets that are generated by enabling access to your Google Drive using these <a href="https://developers.google.com/drive/api/v3/about-auth">instructions</a>.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;client_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;your_client_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;project_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;your_project_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;auth_uri&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;https://accounts.google.com/o/oauth2/auth&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;token_uri&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;https://accounts.google.com/o/oauth2/token&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;auth_provider_x509_cert_url&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;https://www.googleapis.com/oauth2/v1/certs&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;client_secret&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;your_client_secret&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;redirect_uris&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#34;your_redirect_uris&#34;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#34;http://localhost&#34;</span><span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h2 id="run">Run</h2> -<p>With the files in place, run cht-conf&rsquo;s <code>fetch-forms-from-google-drive</code> action in your CHT app&rsquo;s top level directory.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>cht fetch-forms-from-google-drive -</span></span></code></pre></div><p>You will be prompted for an access code from your authenticated user. Follow the prompts through Google&rsquo;s authentication page and copy the access code into your command line. The forms will then be downloaded to the locations set in the forms-on-google-drive.json file.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -If the browser does not launch own its own copy the URL and paste the authentication url into a browser and then follow the prompts. -</div>Apps: Input data available in formshttps://docs.communityhealthtoolkit.org/apps/guides/forms/form-inputs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/form-inputs/ -<p>CHT forms have access to varying amounts of input data depending on the type of form and its source.</p> -<h2 id="contact-forms"><code>contact</code> forms</h2> -<p>Available data:</p> -<ul> -<li><a href="#initial-contact-data-in-contact-forms">initial contact data</a></li> -<li><a href="#user-data"><code>inputs</code> data for user</a></li> -<li><a href="#contact-selector">Contact data via contact selector</a></li> -</ul> -<h3 id="initial-contact-data-in-contact-forms">Initial contact data in <code>contact</code> forms</h3> -<h4 id="create-forms">Create forms</h4> -<p>Forms for adding contacts have access to a small group of fields contained in a top-level group that is named for the contact_type id of the contact being added (so <code>person</code>, <code>clinic</code>, etc). (This is the same group used to create the contact document when the form is completed.)</p> -<table> -<thead> -<tr> -<th>type</th> -<th>name</th> -<th>label</th> -<th>hint</th> -</tr> -</thead> -<tbody> -<tr> -<td>begin group</td> -<td>person</td> -<td>NO_LABEL</td> -<td></td> -</tr> -<tr> -<td>hidden</td> -<td>parent</td> -<td>Parent Id</td> -<td>Contains the doc id for the new contact&rsquo;s parent contact doc.</td> -</tr> -<tr> -<td>hidden</td> -<td>contact_type</td> -<td>Contact Type</td> -<td>The contact_type id of the contact.</td> -</tr> -<tr> -<td>end group</td> -<td></td> -<td></td> -<td></td> -</tr> -</tbody> -</table> -<h4 id="edit-forms">Edit forms</h4> -<p>Forms for editing contacts have access to <em>all the contact&rsquo;s current data</em>. These fields are contained in a top-level group that is named for the contact_type id of the contact being added (so <code>person</code>, <code>clinic</code>, etc). If fields in the top-level group are <a href="https://docs.communityhealthtoolkit.org/apps/reference/forms/contact/">edited by the form</a>, these changes will be saved to the contact&rsquo;s doc.</p> -<p>In addition, the contact&rsquo;s <code>parent</code> data is hydrated so that the form has access to the data stored on the parent contact doc in the <code>parent</code> group.</p> -<table> -<thead> -<tr> -<th>type</th> -<th>name</th> -<th>label</th> -<th>hint</th> -</tr> -</thead> -<tbody> -<tr> -<td>begin group</td> -<td>person</td> -<td>NO_LABEL</td> -<td></td> -</tr> -<tr> -<td>begin group</td> -<td>parent</td> -<td>Parent</td> -<td>Contains the data for the contact&rsquo;s parent contact doc.</td> -</tr> -<tr> -<td>hidden</td> -<td>name</td> -<td>Parent name</td> -<td>The name of the parent contact</td> -</tr> -<tr> -<td>end group</td> -<td>parent</td> -<td></td> -<td></td> -</tr> -<tr> -<td>end group</td> -<td></td> -<td></td> -<td></td> -</tr> -</tbody> -</table> -<hr> -<h2 id="app-forms"><code>app</code> forms</h2> -<p>Available data:</p> -<ul> -<li><a href="#form-source"><code>inputs</code> data for source</a></li> -<li><a href="#inputs-data-for-contact-in-app-forms"><code>inputs</code> data for contact</a></li> -<li><a href="#contact-summary-data"><code>contact-summary</code> data</a></li> -<li><a href="#inputs-data-from-task"><code>inputs</code> data from task</a></li> -<li><a href="#user-data"><code>inputs</code> data for user</a></li> -<li><a href="#contact-selector">Contact data via contact selector</a></li> -</ul> -<h3 id="form-source">Form source</h3> -<p>If a form is created from the &ldquo;People&rdquo; tab, <code>inputs/source</code> will be set to &ldquo;contact&rdquo;. If a form is created from a task, <code>inputs/source</code> will be set to &ldquo;task&rdquo;.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -The <code>source</code> field is also available at the top level (not nested in the <code>inputs</code> group). -</div> -<h3 id="inputs-data-for-contact-in-app-forms"><code>inputs</code> data for contact in <code>app</code> forms</h3> -<p><code>app</code> forms with a contact in context have access to that contact&rsquo;s data in the <code>inputs/contact</code> group.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -The <code>contact</code> group is also available at the top level (not nested in the <code>inputs</code> group). -</div> -<p>The <code>contact</code> group contains all the fields from the doc of the contact in context. If a <em>place</em> is in context, the primary person for that place will be hydrated in the <code>inputs/contact/contact</code> group. Alternatively, when a <em>person</em> is in context, the parent place for the person will be hydrated in <code>inputs/contact/parent</code>.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Contact data is not available in forms created from the &ldquo;Reports&rdquo; tab. -</div> -<h3 id="contact-summary-data"><code>contact-summary</code> data</h3> -<p><code>app</code> forms with a contact in context can access the contact-summary data associated with the contact. This is done by referencing an instance named <code>contact-summary</code>. E.g. <code>instance('contact-summary')/context/${variable}</code>. See <a href="https://docs.communityhealthtoolkit.org/apps/reference/contact-page/#care-guides">the reference documentation</a> for more information.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Contact summary data is not available in <code>contact</code> forms or in forms created from the &ldquo;Reports&rdquo; tab. -</div> -<h3 id="inputs-data-from-task"><code>inputs</code> data from task</h3> -<p><code>app</code> forms created via a task have access to any data <a href="https://docs.communityhealthtoolkit.org/apps/guides/tasks/pass-data-to-form/">supplied by the task</a> in the <code>inputs</code> group.</p> -<hr> -<h2 id="user-data"><code>user</code> data</h2> -<p>Both <code>app</code> and <code>contact</code> forms can access the current user&rsquo;s data at <code>inputs/user</code>. The data provided is simply the <a href="https://docs.communityhealthtoolkit.org/core/overview/db-schema/#users"><code>user-settings</code> doc for the user</a> (e.g. <code>org.couchdb.user:username</code>) plus an additional <code>language</code> field that contains the user&rsquo;s currently selected language code.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -The <code>user-settings</code> doc for the user is <em>NOT</em> the same as the CHT <em>contact</em> doc for the user. -</div> -<h3 id="example-of-saving-user-data-as-metadata-on-a-report">Example of saving <code>user</code> data as metadata on a report</h3> -<table> -<thead> -<tr> -<th>type</th> -<th>name</th> -<th>label</th> -<th>calculation</th> -</tr> -</thead> -<tbody> -<tr> -<td>begin group</td> -<td>inputs</td> -<td>NO_LABEL</td> -<td></td> -</tr> -<tr> -<td>begin group</td> -<td>user</td> -<td>NO_LABEL</td> -<td></td> -</tr> -<tr> -<td>string</td> -<td>contact_id</td> -<td>User&rsquo;s contact id</td> -<td></td> -</tr> -<tr> -<td>string</td> -<td>facility_id</td> -<td>Id for user&rsquo;s facility</td> -<td></td> -</tr> -<tr> -<td>name</td> -<td>name</td> -<td>Username</td> -<td></td> -</tr> -<tr> -<td>end group</td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>end group</td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>calculate</td> -<td>created_by</td> -<td>Username that created report</td> -<td>../inputs/user/name</td> -</tr> -<tr> -<td>calculate</td> -<td>created_by_person_uuid</td> -<td>UUID that created report</td> -<td>../inputs/user/contact_id</td> -</tr> -<tr> -<td>calculate</td> -<td>created_by_place_uuid</td> -<td>Facility of user that created report</td> -<td>../inputs/user/facility_id</td> -</tr> -</tbody> -</table> -<h3 id="loading-the-users-contact-data">Loading the user&rsquo;s contact data</h3> -<p>While the <code>inputs/user</code> group does not contain the user&rsquo;s contact data, it does contain the user&rsquo;s <code>contact_id</code> which can be used to load the contact doc via a <a href="#loading-contact-data-for-use-in-other-expressions">contact selector</a>.</p> -<h2 id="contact-selector">Contact selector</h2> -<p>Using a contact selector allows you to load data from the selected contact (person or place). The contact&rsquo;s id can be provided by the form or the user can search for an existing contact.</p> -<p>To select a contact in a form, create a field with the type <code>string</code> and set the appearance to <code>select-contact type-{{contact_type_1}} type-{{contact_type_2}} ...</code>. Setting multiple contact_type ids allows the user to search among multiple types of contacts. If no contact type appearance is specified then all contact types will be queried when searching.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -For CHT version <code>v3.9.x</code> and below, contacts must be selected by setting the field type to <code>db:{{contact_type}}</code> and the appearance to <code>db-object</code> (instead of using <code>string</code> and <code>select-contact</code>). -</div> -<h3 id="searching-for-a-contact">Searching for a contact</h3> -<p>When a contact selector question is visible in a form, the user can search for a contact by typing in the search box.</p> -<p>A value can be pre-selected for the search box via a <code>calculate</code> expression. By default, the user can change the selected value, but this can be prevented by setting <code>read_only = true</code>.</p> -<table> -<thead> -<tr> -<th>type</th> -<th>name</th> -<th>label</th> -<th>appearance</th> -<th>calculate</th> -</tr> -</thead> -<tbody> -<tr> -<td>string</td> -<td>person_id</td> -<td>Person ID</td> -<td>select-contact type-person</td> -<td></td> -</tr> -<tr> -<td>string</td> -<td>household_id</td> -<td>Household ID</td> -<td>select-contact type-clinic</td> -<td>../inputs/contact/parent/_id</td> -</tr> -</tbody> -</table> -<h3 id="loading-descendants-of-the-current-contact">Loading descendants of the current contact</h3> -<p>Use the appearance <code>descendant-of-current-contact</code> to load the current contact&rsquo;s descendants when opening an app form or contact form from the &ldquo;People&rdquo; tab.</p> -<table> -<thead> -<tr> -<th>type</th> -<th>name</th> -<th>label</th> -<th>appearance</th> -</tr> -</thead> -<tbody> -<tr> -<td>string</td> -<td>household_members</td> -<td>Household&rsquo;s members</td> -<td>select-contact type-person descendant-of-current-contact</td> -</tr> -</tbody> -</table> -<h3 id="loading-additional-contact-data">Loading additional contact data</h3> -<p>Additional data about the contact can be loaded by adding fields to the same group as the contact selector. The field name must match the name of a field on the contact doc. The data will be loaded when the contact is selected and will overwrite any existing data in the field.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Warning</h4> -The data loaded by the contact selector will overwrite any existing data in fields that are in the same group as the contact selector and share the same name as a field on the selected contact. Data will not be written into matching fields if <code>bind-id-only</code> is included in the appearance. -</div> -<table> -<thead> -<tr> -<th>type</th> -<th>name</th> -<th>label</th> -<th>appearance</th> -</tr> -</thead> -<tbody> -<tr> -<td>begin group</td> -<td>selected_person</td> -<td>NO_LABEL</td> -<td></td> -</tr> -<tr> -<td>string</td> -<td>person_id</td> -<td>Person ID</td> -<td>select-contact type-person</td> -</tr> -<tr> -<td>string</td> -<td>name</td> -<td>Name of selected person</td> -<td></td> -</tr> -<tr> -<td>end group</td> -<td>selected_person</td> -<td></td> -<td></td> -</tr> -</tbody> -</table> -<h4 id="loading-contact-data-for-use-in-other-expressions">Loading contact data for use in other expressions</h4> -<p>One powerful way to use the contact selector is to automatically load data about an existing contact for usage in various expressions within the form. This can be done with a combination of functionality we have already seen!</p> -<p>In the following example, we are loading the name of the current user from the associated contact doc:</p> -<table> -<thead> -<tr> -<th>type</th> -<th>name</th> -<th>label</th> -<th>appearance</th> -<th>calculate</th> -</tr> -</thead> -<tbody> -<tr> -<td>begin group</td> -<td>inputs</td> -<td>NO_LABEL</td> -<td></td> -<td></td> -</tr> -<tr> -<td>begin group</td> -<td>user</td> -<td>NO_LABEL</td> -<td></td> -<td></td> -</tr> -<tr> -<td>hidden</td> -<td>contact_id</td> -<td>User&rsquo;s Contact ID</td> -<td></td> -<td></td> -</tr> -<tr> -<td>end group</td> -<td>user</td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>end group</td> -<td>inputs</td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>begin group</td> -<td>intro</td> -<td>NO_LABEL</td> -<td>field-list</td> -<td></td> -</tr> -<tr> -<td>begin group</td> -<td>current_user</td> -<td>NO_LABEL</td> -<td></td> -<td></td> -</tr> -<tr> -<td>string</td> -<td>_id</td> -<td>User&rsquo;s Contact ID</td> -<td>hidden select-contact type-person</td> -<td>${contact_id}</td> -</tr> -<tr> -<td>string</td> -<td>name</td> -<td>User&rsquo;s name</td> -<td>hidden</td> -<td></td> -</tr> -<tr> -<td>end group</td> -<td>current_user</td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>calculate</td> -<td>user_name</td> -<td>Supervisor name</td> -<td></td> -<td>../current_user/name</td> -</tr> -<tr> -<td>note</td> -<td>welcome</td> -<td>Welcome ${user_name}!</td> -<td></td> -<td></td> -</tr> -<tr> -<td>end group</td> -<td>intro</td> -<td></td> -<td></td> -<td></td> -</tr> -</tbody> -</table> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -When loading &ldquo;hidden&rdquo; contact data, it is recommended to nest the group inside an existing page (created by a group with the <code>field-list</code> appearance). This is because there is <a href="https://github.com/medic/cht-core/issues/8226">a known issue</a> that results in an empty page being added to the form when a top-level group had no visible questions. -</div> -<h3 id="conditionally-selectingloading-contact-data-for-the-current-contact">Conditionally selecting/loading contact data for the current contact</h3> -<p><a href="#app-forms">As noted above</a>, when opening an <code>app</code> form with a contact in context (e.g. when opening the form from the &ldquo;People&rdquo; tab), the contact&rsquo;s data will be available in the <code>inputs/contact</code> group. However, this data <em>is not available</em> when opening the form from the &ldquo;Reports&rdquo; tab since there is no contact in context.</p> -<p>A common pattern for enabling consistent form behavior in this case is to use a contact selector in the <code>inputs/contact</code> group and make the <code>inputs</code> group only relevant when the default <code>source</code> value is not overridden (the <code>source</code> field is populated with specific values when the form is opened <a href="#form-source">from the &ldquo;People&rdquo; tab or from a task</a>). With this pattern, the contact selector will only be visible when the form is opened from the &ldquo;Reports&rdquo; tab and the user can select the contact they want to report on. This will ensure that the <code>inputs/contact</code> data is always available regardless of how the form is opened.</p> -<p>Then, this contact data can be used to link the report created from the form to the person or place in context (or the person selected when opening the form). Getting the values for <code>_id</code> or <code>patient_id</code> and setting them to <code>patient_id</code> or <code>patient_uuid</code> on the final report will link that report to the contact. Then the report will be displayed on the contact&rsquo;s summary page.</p> -<table> -<thead> -<tr> -<th>type</th> -<th>name</th> -<th>label</th> -<th>appearance</th> -<th>calculate</th> -<th>relevant</th> -<th>default</th> -</tr> -</thead> -<tbody> -<tr> -<td>begin group</td> -<td>inputs</td> -<td>NO_LABEL</td> -<td>field-list</td> -<td></td> -<td>./source = &lsquo;user&rsquo;</td> -<td></td> -</tr> -<tr> -<td>hidden</td> -<td>source</td> -<td>Source</td> -<td></td> -<td></td> -<td></td> -<td>user</td> -</tr> -<tr> -<td>begin group</td> -<td>contact</td> -<td>NO_LABEL</td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>string</td> -<td>_id</td> -<td>Patient ID</td> -<td>select-contact type-person</td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>string</td> -<td>patient_id</td> -<td>Medic ID</td> -<td>hidden</td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>end group</td> -<td>contact</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>end group</td> -<td>inputs</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>calculate</td> -<td>patient_uuid</td> -<td>Patient UUID</td> -<td></td> -<td>../contact/_id</td> -<td></td> -<td></td> -</tr> -<tr> -<td>calculate</td> -<td>patient_id</td> -<td>Patient ID</td> -<td></td> -<td>../contact/patient_id</td> -<td></td> -<td></td> -</tr> -</tbody> -</table>Apps: Including Multimedia in Formshttps://docs.communityhealthtoolkit.org/apps/guides/forms/multimedia/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/multimedia/ -<h2 id="multimedia-formats">Multimedia Formats</h2> -<p>There are many supported formats for video, audio, and images. We recommend using h.264(mpeg) for video, jpeg for images, and mp3 for audio. When creating videos or images keep in mind the dimensions and storage capabilities on phones that may be used. Lower end phones have smaller storage and screen sizes. When rendering images, video, and audio the CHT uses the browser&rsquo;s built in rendering tools. This means you can render any media format that is supported by the <a href="https://docs.communityhealthtoolkit.org/core/releases/#dependencies">minimum version of Chrome</a>.</p> -<p><strong>List of Supported formats</strong> <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats">video/audio</a> -<a href="https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types">images</a></p> -<h2 id="configuration">Configuration</h2> -<p>To play multimedia from forms you need to add elements to your xml and upload the corresponding multimedia to couchdb as an attachment to your form.</p> -<h3 id="form-config">Form Config</h3> -<p>Add an xml element of text and another element of value. Set form equal to the type of multimedia being used(video, audio, image). The value element must contain <code>jr://file_name.suffix</code> where <code>file_name.suffix</code> is the name of your multimedia file uploaded to couchdb.</p> -<p>Example:</p> -<pre tabindex="0"><code>&lt;text id=&#34;somevideo&#34;&gt; -&lt;value form=&#34;video&#34;&gt;jr://video.mp4&lt;/value&gt; -&lt;/text&gt; -</code></pre><p>Display Example:</p> -<pre tabindex="0"><code>&lt;input ref=&#34;q2&#34;&gt; -&lt;label ref=&#34;jr:itext(&#39;somevideo&#39;)&#34;/&gt; -&lt;/input&gt; -</code></pre><p>Here is a sample form that will display a video and/or image. When this form is opened a video player will be displayed so the user can watch the video. Forms support displaying of images and playing of audio files.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;h:html</span> <span style="color:#c4a000">xmlns=</span><span style="color:#4e9a06">&#34;http://www.w3.org/2002/xforms&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#c4a000">xmlns:h=</span><span style="color:#4e9a06">&#34;http://www.w3.org/1999/xhtml&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#c4a000">xmlns:ev=</span><span style="color:#4e9a06">&#34;http://www.w3.org/2001/xml-events&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#c4a000">xmlns:xsd=</span><span style="color:#4e9a06">&#34;http://www.w3.org/2001/XMLSchema&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#c4a000">xmlns:jr=</span><span style="color:#4e9a06">&#34;http://openrosa.org/javarosa&#34;</span><span style="color:#204a87;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;h:head&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;h:title&gt;</span>Multimedia - Demo Form<span style="color:#204a87;font-weight:bold">&lt;/h:title&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;model&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;itext&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;translation</span> <span style="color:#c4a000">lang=</span><span style="color:#4e9a06">&#34;en&#34;</span><span style="color:#204a87;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">&lt;!-- Attach sample media files to form doc --&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">&lt;!-- https://sample-videos.com --&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;text</span> <span style="color:#c4a000">id=</span><span style="color:#4e9a06">&#34;somevideo&#34;</span><span style="color:#204a87;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;value</span> <span style="color:#c4a000">form=</span><span style="color:#4e9a06">&#34;video&#34;</span><span style="color:#204a87;font-weight:bold">&gt;</span>jr://video.mp4<span style="color:#204a87;font-weight:bold">&lt;/value&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/text&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;text</span> <span style="color:#c4a000">id=</span><span style="color:#4e9a06">&#34;someimage&#34;</span><span style="color:#204a87;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;value</span> <span style="color:#c4a000">form=</span><span style="color:#4e9a06">&#34;image&#34;</span><span style="color:#204a87;font-weight:bold">&gt;</span>jr://image.jpg<span style="color:#204a87;font-weight:bold">&lt;/value&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/text&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;text</span> <span style="color:#c4a000">id=</span><span style="color:#4e9a06">&#34;someaudio&#34;</span><span style="color:#204a87;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;value</span> <span style="color:#c4a000">form=</span><span style="color:#4e9a06">&#34;audio&#34;</span><span style="color:#204a87;font-weight:bold">&gt;</span>jr://audio.mp3<span style="color:#204a87;font-weight:bold">&lt;/value&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/text&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/translation&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/itext&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;instance&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;media</span> <span style="color:#c4a000">id=</span><span style="color:#4e9a06">&#34;multimedia&#34;</span><span style="color:#204a87;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;meta&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;instanceID/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/meta&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/media&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/instance&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/model&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/h:head&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;h:body</span> <span style="color:#c4a000">class=</span><span style="color:#4e9a06">&#34;pages&#34;</span><span style="color:#204a87;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;group</span> <span style="color:#c4a000">appearance=</span><span style="color:#4e9a06">&#34;field-list&#34;</span> <span style="color:#c4a000">ref=</span><span style="color:#4e9a06">&#34;g&#34;</span><span style="color:#204a87;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;input</span> <span style="color:#c4a000">ref=</span><span style="color:#4e9a06">&#34;q2&#34;</span><span style="color:#204a87;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;label</span> <span style="color:#c4a000">ref=</span><span style="color:#4e9a06">&#34;jr:itext(&#39;somevideo&#39;)&#34;</span><span style="color:#204a87;font-weight:bold">/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/input&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;input</span> <span style="color:#c4a000">ref=</span><span style="color:#4e9a06">&#34;q3&#34;</span><span style="color:#204a87;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;label</span> <span style="color:#c4a000">ref=</span><span style="color:#4e9a06">&#34;jr:itext(&#39;someimage&#39;)&#34;</span><span style="color:#204a87;font-weight:bold">/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/input&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;input</span> <span style="color:#c4a000">ref=</span><span style="color:#4e9a06">&#34;q3&#34;</span><span style="color:#204a87;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;label</span> <span style="color:#c4a000">ref=</span><span style="color:#4e9a06">&#34;jr:itext(&#39;someaudio&#39;)&#34;</span><span style="color:#204a87;font-weight:bold">/&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/input&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/group&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&lt;/h:body&gt;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">&lt;/h:html&gt;</span> -</span></span></code></pre></div><h3 id="couchdb-config">Couchdb Config</h3> -<p>The file needs to be added as an attachment with a name that matches what is defined in the form. This can be added by using curl or fauxton. Here is the structure of the curl command.</p> -<p><code>curl -vX PUT https://user:pass@server_name/medic/&lt;form_doc_id&gt;/&lt;attachment_name.suffix&gt;?rev=&lt;latest_form_revision&gt; --data-binary @&lt;local_file_name&gt; -H &quot;Content-Type: &lt;expected_mime_type&gt;&quot;</code></p> -<p>Here is an example of how it would look uploading a sample video for the form above.</p> -<p><code>curl -vX PUT https://user:pass@localhost/medic/form:multimedia/video.mp4?rev=11-a2ebf09cb9678c031859cd2c1da4b603 -k --data-binary @sample.mp4 -H &quot;Content-Type: video/mp4&quot;</code></p> -<p>To use fauxton.</p> -<ol> -<li>Navigate to fauxton. <code>https://&lt;server_name&gt;/_utils</code></li> -<li>Click on the medic database.</li> -<li>Locate the form document.</li> -<li>Click add attachment</li> -<li>Upload the multimedia file and ensure the name matches what has been defined in the form.</li> -</ol> -<h3 id="uploading-with-cht-conf">Uploading with CHT-conf</h3> -<p>Multimedia files can be uploaded when running CHT-conf to upload your forms.</p> -<p>To include the files:</p> -<ol> -<li>Create a directory <code>{form_name}-media</code>. EX: <code>config/default/forms/app/delivery-media</code></li> -<li>Add multimedia files to <code>{form_name}-media\image.png</code>. EX: <code>config/default/forms/app/delivery-media/health_baby.png</code></li> -<li>Run <code>cht-conf upload-app-forms</code></li> -<li>Confirm image is uploaded in CHT app.</li> -</ol>Apps: Tracking Wealth Quintileshttps://docs.communityhealthtoolkit.org/apps/guides/forms/wealth-quintiles/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/wealth-quintiles/ -<p><em>Introduced in v2.16.0</em></p> -<p>Household surveys with questions about the home, possessions, and access to safe drinking water have been used to create equity score and improve the targeting of health services. This guide will cover how quintile information from a household survey can be used in customizing care for individual household members. For example, the equity quintile can be used to increase the number of pregnancy follow-ups for women in specific households, or to display specific notes or questions within patient forms.</p> -<p>For quintile information from a household survey to be used for people in that household, a form about a place must contain <code>NationalQuintile</code> and/or <code>UrbanQuintile</code> fields at the top level. When that form is submitted all the people belonging to that place will get the corresponding <code>wealth_quintile_national</code> and <code>wealth_quintile_urban</code> fields. Those fields can then be used by forms, tasks, and contact profiles accordingly.</p> -<p>Note that the <code>can_write_wealth_quintiles</code> permission is needed for the user that is submitting the form.</p> -<h3 id="sample-form">Sample form</h3> -<table> -<thead> -<tr> -<th>type</th> -<th>name</th> -<th>label</th> -<th>relevant</th> -<th>appearance</th> -<th>calculate</th> -<th>&hellip;</th> -</tr> -</thead> -<tbody> -<tr> -<td>string</td> -<td>place_id</td> -<td></td> -<td></td> -<td>select-contact type-household</td> -<td></td> -<td></td> -</tr> -<tr> -<td>integer</td> -<td>NationalQuintile</td> -<td>National Quintile</td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>integer</td> -<td>UrbanQuintile</td> -<td>Urban Quintile</td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -</tbody> -</table> -<h3 id="sample-report">Sample Report</h3> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;family_survey&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;data_record&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;content_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;xml&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;fields&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;place_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;23c7ab6e-6ea5-4f9f-9ffa-f00a1e30278d&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;NationalQuintile&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;5&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;UrbanQuintile&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;3&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="sample-contact">Sample Contact</h3> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;c148d0c7-b200-47f6-a891-3f55c78eda9b&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_rev&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;3-746a7c8ecb15dc1f43bfa13eba2afbbe&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;person&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Jane Smith&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;notes&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;sex&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;female&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;date_of_birth_method&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;approx&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;date_of_birth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;1987-12-10&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;phone&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;alternate_phone&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;external_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;reported_date&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1607530340715</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;23c7ab6e-6ea5-4f9f-9ffa-f00a1e30278d&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;8ed3600f-23e2-4446-8ff4-2528067085e7&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;wealth_quintile_national&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;5&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;wealth_quintile_urban&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;3&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div>Apps: Customizing Titles in the Reports Listhttps://docs.communityhealthtoolkit.org/apps/guides/forms/report-titles/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/report-titles/ -<p><em>Added in 3.9.0</em></p> -<p>By default the CHT shows the name of the subject of the report in the reports list. This can be overridden by configuring the <code>subject_key</code> property with a translation key in the form document.</p> -<p>The translation uses a summary of the report as the evaluation context so you can include report fields in your value, for example: <code>Case registration {{case_id}}</code>. Useful properties available in the summary include: <code>from</code> (the phone number of the sender), <code>phone</code> (the phone number of the report contact), <code>form</code> (the form code), <code>subject.name</code> (the name of the subject), and <code>case_id</code> (the generated case id).</p> -<h2 id="code-sample">Code Sample</h2> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;internalId&#34;</span><span style="color:#a40000">:</span> <span style="color:#4e9a06">&#34;signal&#34;</span><span style="color:#a40000">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;title&#34;</span><span style="color:#a40000">:</span> <span style="color:#4e9a06">&#34;Signal signoff&#34;</span><span style="color:#a40000">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;subject_key&#34;</span><span style="color:#a40000">:</span> <span style="color:#4e9a06">&#34;signal.list.subject&#34;</span><span style="color:#a40000">,</span> -</span></span></code></pre></div>Apps: Versioning formshttps://docs.communityhealthtoolkit.org/apps/guides/forms/versioning/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/versioning/ -<p><em>Added in cht-core 3.15.0 and cht-conf 3.10.0</em></p> -<p>When uploading app or contact xforms, cht-conf 3.10.0+ will automatically generate a version and include it in the form doc&rsquo;s -<code>xmlVersion</code> property. The version has two properties.</p> -<table> -<thead> -<tr> -<th>Property</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>time</code></td> -<td>The time that the form was uploaded to the server in millis since the epoch.</td> -</tr> -<tr> -<td><code>sha256</code></td> -<td>A hash of the xform content.</td> -</tr> -</tbody> -</table> -<p>For example:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;xmlVersion&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;time&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1658717177750</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;sha256&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;6f0bbfe5a9a9ebeb25784165879afec5e311b197cbd76ade5698c83c22dd9a8f&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>When a user fills in a form with an <code>xmlVersion</code> property, the version is copied in to the report doc as the <code>form_version</code> -property. For example, the snippet above would generate a property like this:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;form_version&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;time&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1658717177750</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;sha256&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;6f0bbfe5a9a9ebeb25784165879afec5e311b197cbd76ade5698c83c22dd9a8f&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>This can be used to determine the version of the form that was used to submit the report. If you upload multiple -versions of a form over time you can use logic to have the CHT and integrated systems to enable backwards compatibility so -they behave the same regardless of which version of the form was used.</p> -<p>For example, if a property was modified between versions then the logic may look something like this:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">let</span> <span style="color:#000">patientName</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">doc</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">form_version</span> <span style="color:#ce5c00;font-weight:bold">&lt;</span> <span style="color:#0000cf;font-weight:bold">1658717177750</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">patientName</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">doc</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">first_name</span> <span style="color:#ce5c00;font-weight:bold">+</span> <span style="color:#4e9a06">&#39; &#39;</span> <span style="color:#ce5c00;font-weight:bold">+</span> <span style="color:#000">doc</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">last_name</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> <span style="color:#204a87;font-weight:bold">else</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">patientName</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">doc</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">name</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div> \ No newline at end of file +Building and Maintaining Forms on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/guides/forms/Recent content in Building and Maintaining Forms on Community Health ToolkitHugo -- gohugo.ioenConfiguring UHC Modehttps://docs.communityhealthtoolkit.org/apps/guides/forms/uhc-mode/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/uhc-mode/Introduced in v2.18.0 +The CHT&rsquo;s UHC Mode empowers CHWs to provide equitable and timely care to families in their catchment area. The Community Health Toolkit supports this use-case by displaying the number of visits made to a household and highlighting households which haven&rsquo;t met their visit goal in red at the top of the contact list. +The date last visited is colored red whenever the date is 30 days or more in the past.Creating Additional Docs from App Formshttps://docs.communityhealthtoolkit.org/apps/guides/forms/additional-docs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/additional-docs/In version 2.13.0 and higher, you can configure app forms to generate additional docs upon submission. You can create one or more docs using variations on the configuration described below. One case where this can be used is to register a newborn from a delivery report, as shown below. First, here is an overview of what you can do and how the configuration should look in XML: +Extra Docs Extra docs can be added by defining structures in the model with the attributeMaking Calls and Sending SMS from App Formshttps://docs.communityhealthtoolkit.org/apps/guides/forms/app-form-sms/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/app-form-sms/Triggering Calls and SMS When an XForm is loaded on a phone you can start a phone call or trigger the sending of an SMS within the form itself. This can be useful if within a task or assessment, you want to tell the user to contact a patient, or perhaps a health worker at a facility. +To set up the call or SMS you&rsquo;ll need to create a link with tel: or sms: within a note field.Fetching forms from Google Drivehttps://docs.communityhealthtoolkit.org/apps/guides/forms/google-drive/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/google-drive/To work collaboratively on form design it can be helpful to keep XLSForms in Google Drive. The fetch-forms-from-google-drive action downloads these XLSForms so that they can be converted to XForms and uploaded to your CHT app. +This action requires the following files in the top-level folder of your CHT app config: forms-on-google-drive.json and .gdrive.secrets.json. +forms-on-google-drive.json This JSON file contains key value pairs, where the key is the relative path and name for the downloaded file, and the value is the file&rsquo;s Google Drive File ID.Input data available in formshttps://docs.communityhealthtoolkit.org/apps/guides/forms/form-inputs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/form-inputs/CHT forms have access to varying amounts of input data depending on the type of form and its source. +contact forms Available data: +initial contact data inputs data for user Contact data via contact selector Initial contact data in contact forms Create forms Forms for adding contacts have access to a small group of fields contained in a top-level group that is named for the contact_type id of the contact being added (so person, clinic, etc).Including Multimedia in Formshttps://docs.communityhealthtoolkit.org/apps/guides/forms/multimedia/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/multimedia/Multimedia Formats There are many supported formats for video, audio, and images. We recommend using h.264(mpeg) for video, jpeg for images, and mp3 for audio. When creating videos or images keep in mind the dimensions and storage capabilities on phones that may be used. Lower end phones have smaller storage and screen sizes. When rendering images, video, and audio the CHT uses the browser&rsquo;s built in rendering tools. This means you can render any media format that is supported by the minimum version of Chrome.Tracking Wealth Quintileshttps://docs.communityhealthtoolkit.org/apps/guides/forms/wealth-quintiles/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/wealth-quintiles/Introduced in v2.16.0 +Household surveys with questions about the home, possessions, and access to safe drinking water have been used to create equity score and improve the targeting of health services. This guide will cover how quintile information from a household survey can be used in customizing care for individual household members. For example, the equity quintile can be used to increase the number of pregnancy follow-ups for women in specific households, or to display specific notes or questions within patient forms.Customizing Titles in the Reports Listhttps://docs.communityhealthtoolkit.org/apps/guides/forms/report-titles/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/report-titles/Added in 3.9.0 +By default the CHT shows the name of the subject of the report in the reports list. This can be overridden by configuring the subject_key property with a translation key in the form document. +The translation uses a summary of the report as the evaluation context so you can include report fields in your value, for example: Case registration {{case_id}}. Useful properties available in the summary include: from (the phone number of the sender), phone (the phone number of the report contact), form (the form code), subject.Versioning formshttps://docs.communityhealthtoolkit.org/apps/guides/forms/versioning/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/versioning/Added in cht-core 3.15.0 and cht-conf 3.10.0 +When uploading app or contact xforms, cht-conf 3.10.0+ will automatically generate a version and include it in the form doc&rsquo;s xmlVersion property. The version has two properties. +Property Description time The time that the form was uploaded to the server in millis since the epoch. sha256 A hash of the xform content. For example: +&#34;xmlVersion&#34;: { &#34;time&#34;: 1658717177750, &#34;sha256&#34;: &#34;6f0bbfe5a9a9ebeb25784165879afec5e311b197cbd76ade5698c83c22dd9a8f&#34; } When a user fills in a form with an xmlVersion property, the version is copied in to the report doc as the form_version property. \ No newline at end of file diff --git a/apps/guides/forms/multimedia/index.html b/apps/guides/forms/multimedia/index.html index b8b230a810..c8cf7bd129 100644 --- a/apps/guides/forms/multimedia/index.html +++ b/apps/guides/forms/multimedia/index.html @@ -1,9 +1,9 @@ -Including Multimedia in Forms | Community Health Toolkit +Including Multimedia in Forms | Community Health Toolkit

    Including Multimedia in Forms

    How to include multimedia files in forms

    Multimedia Formats

    There are many supported formats for video, audio, and images. We recommend using h.264(mpeg) for video, jpeg for images, and mp3 for audio. When creating videos or images keep in mind the dimensions and storage capabilities on phones that may be used. Lower end phones have smaller storage and screen sizes. When rendering images, video, and audio the CHT uses the browser’s built in rendering tools. This means you can render any media format that is supported by the minimum version of Chrome.

    List of Supported formats video/audio + Create project issue

    Including Multimedia in Forms

    How to include multimedia files in forms

    Multimedia Formats

    There are many supported formats for video, audio, and images. We recommend using h.264(mpeg) for video, jpeg for images, and mp3 for audio. When creating videos or images keep in mind the dimensions and storage capabilities on phones that may be used. Lower end phones have smaller storage and screen sizes. When rendering images, video, and audio the CHT uses the browser’s built in rendering tools. This means you can render any media format that is supported by the minimum version of Chrome.

    List of Supported formats video/audio images

    Configuration

    To play multimedia from forms you need to add elements to your xml and upload the corresponding multimedia to couchdb as an attachment to your form.

    Form Config

    Add an xml element of text and another element of value. Set form equal to the type of multimedia being used(video, audio, image). The value element must contain jr://file_name.suffix where file_name.suffix is the name of your multimedia file uploaded to couchdb.

    Example:

    <text id="somevideo">
         <value form="video">jr://video.mp4</value>
     </text>
    @@ -363,7 +363,8 @@
     Reference >
     forms/ >
     contact

    Contact Forms: Used for creating and editing people and places

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/guides/forms/report-titles/index.html b/apps/guides/forms/report-titles/index.html index d0b63a1bf1..daffd9c3aa 100644 --- a/apps/guides/forms/report-titles/index.html +++ b/apps/guides/forms/report-titles/index.html @@ -1,9 +1,9 @@ -Customizing Titles in the Reports List | Community Health Toolkit +Customizing Titles in the Reports List | Community Health Toolkit

    Customizing Titles in the Reports List

    Customizing the title shown in the Reports list

    Added in 3.9.0

    By default the CHT shows the name of the subject of the report in the reports list. This can be overridden by configuring the subject_key property with a translation key in the form document.

    The translation uses a summary of the report as the evaluation context so you can include report fields in your value, for example: Case registration {{case_id}}. Useful properties available in the summary include: from (the phone number of the sender), phone (the phone number of the report contact), form (the form code), subject.name (the name of the subject), and case_id (the generated case id).

    Code Sample

      "internalId": "signal",
    + Create project issue

    Customizing Titles in the Reports List

    Customizing the title shown in the Reports list

    Added in 3.9.0

    By default the CHT shows the name of the subject of the report in the reports list. This can be overridden by configuring the subject_key property with a translation key in the form document.

    The translation uses a summary of the report as the evaluation context so you can include report fields in your value, for example: Case registration {{case_id}}. Useful properties available in the summary include: from (the phone number of the sender), phone (the phone number of the report contact), form (the form code), subject.name (the name of the subject), and case_id (the generated case id).

    Code Sample

      "internalId": "signal",
       "title": "Signal signoff",
       "subject_key": "signal.list.subject",
     

    CHT Applications > @@ -309,7 +309,8 @@ Reference > app_settings.json > .patient_reports

    Patient Reports: Defining SMS workflows with schedules, registration, and patient reports.

    -

    Last modified 05.06.2020: Categorized guides (d26e182e)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/forms/uhc-mode/index.html b/apps/guides/forms/uhc-mode/index.html index 6e758f53de..f5b8dae9f9 100644 --- a/apps/guides/forms/uhc-mode/index.html +++ b/apps/guides/forms/uhc-mode/index.html @@ -1,9 +1,9 @@ -Configuring UHC Mode | Community Health Toolkit +Configuring UHC Mode | Community Health Toolkit

    Configuring UHC Mode

    How to enable Universal Health Coverage monitoring with UHC Mode

    Introduced in v2.18.0

    UHC Mode screenshot

    The CHT’s UHC Mode empowers CHWs to provide equitable and timely care to families in their catchment area. The Community Health Toolkit supports this use-case by displaying the number of visits made to a household and highlighting households which haven’t met their visit goal in red at the top of the contact list.

    The date last visited is colored red whenever the date is 30 days or more in the past. The date last visited is displayed as a relative date. This period is described in terms of “days” up until two months, so “Visited 3 days ago” or “Visited 36 days ago”. After two months, we simply say “2 months” or “3 months”.

    Household Visits

    Contact sorting screenshot

    The date of the last visit is displayed and contacts can be sorted by last visit date to allow the CHW plan their work.

    “Date last visited” and “Visits this month” are different data points:

    • Date last visited is calculated based upon a rolling count of “how many days ago” and is not tied to the calendar at all.
    • Visits this month are tied directly to the calendar.

    Configuration

    Any patient- or household-level form or forms can be used to update the date last visited. You may run into performance issues if you configure this to look at forms submitted very frequently. For example, five forms submitted only once a month would work better than two forms submitted every day.

    UHC Permissions

    To see the date last visited, grant the relevant user role (usually CHW) the can_view_last_visited_date permission. Once that permission is enabled, you’ll be able to display the date the household was last visited in the list.

    To view UHC metrics, the can_view_uhc_stats permission needs to be granted to the CHW user role.

      "permissions": {
    + Create project issue

    Configuring UHC Mode

    How to enable Universal Health Coverage monitoring with UHC Mode

    Introduced in v2.18.0

    UHC Mode screenshot

    The CHT’s UHC Mode empowers CHWs to provide equitable and timely care to families in their catchment area. The Community Health Toolkit supports this use-case by displaying the number of visits made to a household and highlighting households which haven’t met their visit goal in red at the top of the contact list.

    The date last visited is colored red whenever the date is 30 days or more in the past. The date last visited is displayed as a relative date. This period is described in terms of “days” up until two months, so “Visited 3 days ago” or “Visited 36 days ago”. After two months, we simply say “2 months” or “3 months”.

    Household Visits

    Contact sorting screenshot

    The date of the last visit is displayed and contacts can be sorted by last visit date to allow the CHW plan their work.

    “Date last visited” and “Visits this month” are different data points:

    • Date last visited is calculated based upon a rolling count of “how many days ago” and is not tied to the calendar at all.
    • Visits this month are tied directly to the calendar.

    Configuration

    Any patient- or household-level form or forms can be used to update the date last visited. You may run into performance issues if you configure this to look at forms submitted very frequently. For example, five forms submitted only once a month would work better than two forms submitted every day.

    UHC Permissions

    To see the date last visited, grant the relevant user role (usually CHW) the can_view_last_visited_date permission. Once that permission is enabled, you’ll be able to display the date the household was last visited in the list.

    To view UHC metrics, the can_view_uhc_stats permission needs to be granted to the CHW user role.

      "permissions": {
         "can_view_last_visited_date": [ "chwUserRole" ]
       },
     

    Forms

    Indicate which forms should be included in the calculation of the date last visited. You can use forms at the person or household level. Add the visited_contact_uuid field to forms to track visits. This should be a calculate field that contains the place UUID of the visited contact. Ensure this is a top-level field and not in any group. When the form is submitted it would be seen as doc.fields.visited_contact_uuid.

    Settings

    Information on when to begin counting visits, household visit goals, and default contact sorting is defined in base_settings.json.

    SettingDescriptionDefaultVersion
    uhc.contacts_default_sort
    • “alpha”: Sort contacts alphanumerically
    • “last_visited_date”: sort contacts by the date they were most recently visited.
    “alpha”2.18.0
    uhc.visit_count.month_start_dateThe date of each month when the visit count is reset to 0.12.18.0
    uhc.visit_count.visit_count_goalThe monthly visit count goal.02.18.0

    CHT Applications > @@ -312,7 +312,8 @@ Reference > app_settings.json > .permissions

    User Permissions: Assigning fine grained settings for user roles

    -

    Last modified 26.07.2024: Update uhc-mode.md (#1471) (cad9d438)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/forms/versioning/index.html b/apps/guides/forms/versioning/index.html index 4849bf0b1e..38185f3269 100644 --- a/apps/guides/forms/versioning/index.html +++ b/apps/guides/forms/versioning/index.html @@ -1,9 +1,9 @@ -Versioning forms | Community Health Toolkit +Versioning forms | Community Health Toolkit

    Versioning forms

    Record the version of the form when creating reports

    Added in cht-core 3.15.0 and cht-conf 3.10.0

    When uploading app or contact xforms, cht-conf 3.10.0+ will automatically generate a version and include it in the form doc’s + Create project issue

    Versioning forms

    Record the version of the form when creating reports

    Added in cht-core 3.15.0 and cht-conf 3.10.0

    When uploading app or contact xforms, cht-conf 3.10.0+ will automatically generate a version and include it in the form doc’s xmlVersion property. The version has two properties.

    PropertyDescription
    timeThe time that the form was uploaded to the server in millis since the epoch.
    sha256A hash of the xform content.

    For example:

      "xmlVersion": {
         "time": 1658717177750,
         "sha256": "6f0bbfe5a9a9ebeb25784165879afec5e311b197cbd76ade5698c83c22dd9a8f"
    @@ -321,7 +321,8 @@
     

    CHT Applications > Features > Reports

    Reports for Data & Report Management

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/guides/forms/wealth-quintiles/index.html b/apps/guides/forms/wealth-quintiles/index.html index a3a573de98..3f910a4659 100644 --- a/apps/guides/forms/wealth-quintiles/index.html +++ b/apps/guides/forms/wealth-quintiles/index.html @@ -1,9 +1,9 @@ -Tracking Wealth Quintiles | Community Health Toolkit +Tracking Wealth Quintiles | Community Health Toolkit

    Tracking Wealth Quintiles

    How to track wealth quintiles on the profile of each family member in the household

    Introduced in v2.16.0

    Household surveys with questions about the home, possessions, and access to safe drinking water have been used to create equity score and improve the targeting of health services. This guide will cover how quintile information from a household survey can be used in customizing care for individual household members. For example, the equity quintile can be used to increase the number of pregnancy follow-ups for women in specific households, or to display specific notes or questions within patient forms.

    For quintile information from a household survey to be used for people in that household, a form about a place must contain NationalQuintile and/or UrbanQuintile fields at the top level. When that form is submitted all the people belonging to that place will get the corresponding wealth_quintile_national and wealth_quintile_urban fields. Those fields can then be used by forms, tasks, and contact profiles accordingly.

    Note that the can_write_wealth_quintiles permission is needed for the user that is submitting the form.

    Sample form

    typenamelabelrelevantappearancecalculate
    stringplace_idselect-contact type-household
    integerNationalQuintileNational Quintile
    integerUrbanQuintileUrban Quintile

    Sample Report

    Tracking Wealth Quintiles

    How to track wealth quintiles on the profile of each family member in the household

    Introduced in v2.16.0

    Household surveys with questions about the home, possessions, and access to safe drinking water have been used to create equity score and improve the targeting of health services. This guide will cover how quintile information from a household survey can be used in customizing care for individual household members. For example, the equity quintile can be used to increase the number of pregnancy follow-ups for women in specific households, or to display specific notes or questions within patient forms.

    For quintile information from a household survey to be used for people in that household, a form about a place must contain NationalQuintile and/or UrbanQuintile fields at the top level. When that form is submitted all the people belonging to that place will get the corresponding wealth_quintile_national and wealth_quintile_urban fields. Those fields can then be used by forms, tasks, and contact profiles accordingly.

    Note that the can_write_wealth_quintiles permission is needed for the user that is submitting the form.

    Sample form

    typenamelabelrelevantappearancecalculate
    stringplace_idselect-contact type-household
    integerNationalQuintileNational Quintile
    integerUrbanQuintileUrban Quintile

    Sample Report

    {
       "form": "family_survey",
       "type": "data_record",
       "content_type": "xml",
    @@ -333,7 +333,8 @@
       "wealth_quintile_urban": "3"
     }
     
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/index.html b/apps/guides/index.html index 4c7180247f..71bc462b97 100644 --- a/apps/guides/index.html +++ b/apps/guides/index.html @@ -1,9 +1,9 @@ -Quick Guides for Implementers | Community Health Toolkit +Quick Guides for Implementers | Community Health Toolkit

    Quick Guides for Implementers

    These quick guides address focused CHT app development topics

    Managing Data

    Creating and managing data in CHT applications

    Managing Databases

    Managing databases used by CHT applications

    Debugging Applications

    Guides for debugging common scenarios

    Building and Maintaining Forms

    Managing and using forms in the CHT

    Building Integrations

    How to build integrations with external software and data sources

    Messaging and SMS

    Building and troubleshooting messaging

    Tracking and Improving Performance

    Guides for tracking and improving the performance of CHT applications and servers

    Privacy

    Policies and templates for privacy and responsible data use

    Security

    Guides and best practices for securing CHT applications

    Tasks

    Building and managing Tasks and their data

    Training

    Building and managing training resources

    Managing Updates to Applications and Content

    Guides for managing updates to CHT applications, their content, and data

    Android app development

    Guides for developing a CHT Android application

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/index.xml b/apps/guides/index.xml index bf2a70e1ec..e861e83801 100644 --- a/apps/guides/index.xml +++ b/apps/guides/index.xml @@ -1,46 +1 @@ -Community Health Toolkit – Quick Guides for Implementershttps://docs.communityhealthtoolkit.org/apps/guides/Recent content in Quick Guides for Implementers on Community Health ToolkitHugo -- gohugo.ioenApps: Managing Datahttps://docs.communityhealthtoolkit.org/apps/guides/data/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/Apps: Managing Databaseshttps://docs.communityhealthtoolkit.org/apps/guides/database/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/database/ -<h2 id="couchdb">CouchDB</h2> -<p>The CHT has a range of CouchDB databases for storing different types of data. By default, databases all start with the prefix &ldquo;medic&rdquo;.</p> -<h3 id="medic">medic</h3> -<p>The main database, used to store all contact and report data. Data access is protected by API to provide protection on a per document basis.</p> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/core/overview/db-schema/">Database schema conventions</a></p> -<h3 id="medic-sentinel">medic-sentinel</h3> -<p>Stores the <code>_local/sentinel-meta-data</code> document which stores the sequence of the last processed change in the <code>medic</code> db. This is used so Sentinel can resume from where it left off and process all changes in order.</p> -<p>This database also stores metadata about the documents in the &ldquo;medic&rdquo; database, such as when it was received and which Sentinel transitions have executed on this doc. The UUID of the metadata doc is the same as the UUID of the &ldquo;medic&rdquo; doc with &ldquo;-info&rdquo; appended at the end.</p> -<p>For example:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;f8cc78d0-31a7-44e8-8073-176adcc0dc7b-info&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_rev&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2-6e08756f62fa0595d87a3f50777758dc&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;info&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;doc_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;f8cc78d0-31a7-44e8-8073-176adcc0dc7b&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;latest_replication_date&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2018-08-13T22:02:46.699Z&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;initial_replication_date&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2018-08-14T10:02:13.625Z&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="medic-logs">medic-logs</h3> -<p>Stores a record of when a user last attempted to replicate and how many docs they have access to. This can be useful when trying to diagnose issues with users getting too much access, or being unable to complete replication because their access is too broad.</p> -<h3 id="medic-vault">medic-vault</h3> -<p>Stores CHT credentials for authenticating with third party services. These credentials are encrypted for safety, and can only be updated using the <a href="https://docs.communityhealthtoolkit.org/apps/reference/api#put-apiv1credentials">Credentials API</a>.</p> -<h3 id="medic-user-username-meta">medic-user-{username}-meta</h3> -<p>Used for documents which are only relevant to a single user, including:</p> -<ul> -<li>&ldquo;feedback&rdquo;. Errors and exceptions caught in the browser, and user initiated feedback. Support staff must monitor these docs to detect any errors that are occurring on the client device.</li> -<li>&ldquo;read&rdquo;. Records that a document has been opened in the browser so it can be marked as read in the UI.</li> -<li>&ldquo;telemetry&rdquo;. Aggregate telemetry information for performance and usage metrics analysis. -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/guides/performance/telemetry/">User telemetry</a></p> -</li> -</ul> -<h3 id="medic-users-meta">medic-users-meta</h3> -<p>To make it easier to perform analysis of all the docs in each user&rsquo;s &ldquo;medic-user-{username}-meta&rdquo; database, Sentinel replicates all the &ldquo;feedback&rdquo; and &ldquo;telemetry&rdquo; docs into this single database . This is used for reporting, monitoring, and usage analytics. The &ldquo;feedback&rdquo; and &ldquo;telemetry&rdquo; docs are deleted from the user&rsquo;s &ldquo;medic-user-{username}-meta&rdquo; database after they have successfully been replicated to &ldquo;medic-users-meta&rdquo;.</p> -<p>Replication to this database can be enabled via configuration from 3.5.0 and works without configuration from 3.10.0.</p> -<h3 id="_users">_users</h3> -<p>This is the standard CouchDB database used to configure user authentication and authorization.</p> -<h3 id="medic-logs-1">medic-logs</h3> -<p>Stores meta data about the user including when they last connected to the server, and how many documents they are allowed to replicate. This can be useful for checking for connection issues and misconfigured users.</p> -<h2 id="pouchdb">PouchDB</h2> -<p>Used to store documents on the client device to allow for <a href="https://docs.communityhealthtoolkit.org/core/overview/offline-first/">Offline-First</a> access. Bidirectional replication is done on the &ldquo;medic&rdquo; and &ldquo;medic-user-{username}-meta&rdquo; databases. The &ldquo;medic&rdquo; database is only partially replicated so the user stores only a subset of the entire CouchDB database for performance and security reasons.</p> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/guides/performance/replication/">CouchDB replication</a></p> -<h2 id="postgresql">PostgreSQL</h2> -<p>Used to store data for performant analytical queries such as impact and monitoring dashboards. The CHT uses <a href="https://github.com/medic/medic-couch2pg">medic-couch2pg</a> to handle replication of docs from &ldquo;medic&rdquo;, &ldquo;medic-sentinel&rdquo;, and &ldquo;medic-users-meta&rdquo; databases into Postgres.</p> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/core/overview/data-flows-for-analytics/">Data Flows for Analytics</a></p>Apps: Debugging Applicationshttps://docs.communityhealthtoolkit.org/apps/guides/debugging/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/debugging/Apps: Building and Maintaining Formshttps://docs.communityhealthtoolkit.org/apps/guides/forms/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/Apps: Building Integrationshttps://docs.communityhealthtoolkit.org/apps/guides/integrations/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/integrations/Apps: Messaging and SMShttps://docs.communityhealthtoolkit.org/apps/guides/messaging/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/Apps: Tracking and Improving Performancehttps://docs.communityhealthtoolkit.org/apps/guides/performance/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/performance/Apps: Privacyhttps://docs.communityhealthtoolkit.org/apps/guides/privacy/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/privacy/Apps: Securityhttps://docs.communityhealthtoolkit.org/apps/guides/security/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/security/Apps: Taskshttps://docs.communityhealthtoolkit.org/apps/guides/tasks/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/tasks/Apps: Traininghttps://docs.communityhealthtoolkit.org/apps/guides/training/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/training/Apps: Managing Updates to Applications and Contenthttps://docs.communityhealthtoolkit.org/apps/guides/updates/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/updates/Apps: Android app developmenthttps://docs.communityhealthtoolkit.org/apps/guides/android/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/android/ -<p>The Community Health Toolkit includes 2 apps that are currently supported: the <strong><a href="https://github.com/medic/cht-android">CHT Android</a></strong> and the <strong><a href="https://github.com/medic/cht-gateway">CHT Gateway</a></strong>. The following guides will help you setup a development environment in your local computer to improve, release, and brand the apps (branding only applies to CHT Android).</p> -<p><strong><a href="https://github.com/medic/medic-collect">Medic Collect</a></strong> is currently in maintenance mode, although some of the guides here may apply if you need to work with it.</p> \ No newline at end of file +Quick Guides for Implementers on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/guides/Recent content in Quick Guides for Implementers on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file diff --git a/apps/guides/integrations/dhis2-aggregate/index.html b/apps/guides/integrations/dhis2-aggregate/index.html index 5fc199bf1c..65365f6a08 100644 --- a/apps/guides/integrations/dhis2-aggregate/index.html +++ b/apps/guides/integrations/dhis2-aggregate/index.html @@ -1,9 +1,9 @@ -DHIS2 Aggregate | Community Health Toolkit +DHIS2 Aggregate | Community Health Toolkit

    DHIS2 Aggregate

    Design considerations and how to configure

    One of the first things you’ll need to do is identify the specific DHIS2 data set that you plan to implement. You’ll need a list of all the data elements on that data set, a detailed understanding of how each is calculated, the frequency in which the data set is submitted (weekly, monthly, etc…), and for which organisation units the data set applies. You’ll also want to identify and engage the appropriate DHIS2 stakeholders to get access to DHIS2 metadata, test environments, and discuss workflows. The DHIS2 documentation site provides additional background and advice here.

    Design Considerations

    There are a few very important considerations related to how you design workflows and set up your CHT application to make sure you will be able to integrate with DHIS2. The main areas are:

    1. Hierarchies
    2. Fields and calculations
    3. Workflows and user access.

    These considerations are summarized below.

    Hierarchies

    The CHT relies on your Place hierarchy to determine how data should be aggregated for DHIS2. As such, it’s important that you consider how organization units are configured in the DHIS2 instance that you need to integrate data into. If your CHT Place hierarchy does not align with the DHIS2 organisation unit structure, the CHT will not be able to aggregate data in the way DHIS2 needs it.

    See Also: Hierarchies

    Fields and calculations

    It’s important to understand each data element on the DHIS2 data set you want to integrate data into and how each data element is calculated. When you are designing your forms in the CHT, you will need to make sure you are capturing information in your forms so that every data element on the chosen DHIS2 data set can be calculated within the CHT.

    See Also: Forms

    Workflows and User Access

    The aggregate data workflow is really designed around 3 key user personas. You’ll need to make sure that your context can support those assumptions and will be able to have access to the appropriate features in the CHT and DHIS2.

    Moving data from the CHT to DHIS2 can be done in three main ways.

    1. Manually downloading from the CHT
    2. Building an app in DHIS2 and calling an API in the CHT
    3. Orchestrating the steps using an interoperability layer such as OpenHIM, OpenFn, etc…

    Configuration

    Once you have designed your hierarchies, forms, calculations, and workflows, there are a few key configurations that need to be made.

    Data sets

    For the CHT to be able to export the file for DHIS2, it needs to know the specific name and ID of the data set in DHIS2. You will need to obtain the ID from the test or production DHIS2 environment.

    Configure the data set in app_settings.json.

    See Also: DHIS2 in App Settings

    Organisation units

    Aggregation in the CHT is based on your Place hierarchy. As mentioned in the Hierarchies Design Considerations above, your Places must align with DHIS2 organisation units. You will need to specify the DHIS2 organisation unit’s ID on the Place document in the CHT.

    See Also: Contact Forms

    DHIS2 Aggregate

    Design considerations and how to configure

    One of the first things you’ll need to do is identify the specific DHIS2 data set that you plan to implement. You’ll need a list of all the data elements on that data set, a detailed understanding of how each is calculated, the frequency in which the data set is submitted (weekly, monthly, etc…), and for which organisation units the data set applies. You’ll also want to identify and engage the appropriate DHIS2 stakeholders to get access to DHIS2 metadata, test environments, and discuss workflows. The DHIS2 documentation site provides additional background and advice here.

    Design Considerations

    There are a few very important considerations related to how you design workflows and set up your CHT application to make sure you will be able to integrate with DHIS2. The main areas are:

    1. Hierarchies
    2. Fields and calculations
    3. Workflows and user access.

    These considerations are summarized below.

    Hierarchies

    The CHT relies on your Place hierarchy to determine how data should be aggregated for DHIS2. As such, it’s important that you consider how organization units are configured in the DHIS2 instance that you need to integrate data into. If your CHT Place hierarchy does not align with the DHIS2 organisation unit structure, the CHT will not be able to aggregate data in the way DHIS2 needs it.

    See Also: Hierarchies

    Fields and calculations

    It’s important to understand each data element on the DHIS2 data set you want to integrate data into and how each data element is calculated. When you are designing your forms in the CHT, you will need to make sure you are capturing information in your forms so that every data element on the chosen DHIS2 data set can be calculated within the CHT.

    See Also: Forms

    Workflows and User Access

    The aggregate data workflow is really designed around 3 key user personas. You’ll need to make sure that your context can support those assumptions and will be able to have access to the appropriate features in the CHT and DHIS2.

    Moving data from the CHT to DHIS2 can be done in three main ways.

    1. Manually downloading from the CHT
    2. Building an app in DHIS2 and calling an API in the CHT
    3. Orchestrating the steps using an interoperability layer such as OpenHIM, OpenFn, etc…

    Configuration

    Once you have designed your hierarchies, forms, calculations, and workflows, there are a few key configurations that need to be made.

    Data sets

    For the CHT to be able to export the file for DHIS2, it needs to know the specific name and ID of the data set in DHIS2. You will need to obtain the ID from the test or production DHIS2 environment.

    Configure the data set in app_settings.json.

    See Also: DHIS2 in App Settings

    Organisation units

    Aggregation in the CHT is based on your Place hierarchy. As mentioned in the Hierarchies Design Considerations above, your Places must align with DHIS2 organisation units. You will need to specify the DHIS2 organisation unit’s ID on the Place document in the CHT.

    See Also: Contact Forms

    {
       "_id": "my_place",
       "type": "health_center",
       "dhis": {
    @@ -332,7 +332,8 @@
     Reference >
     app_settings.json >
     .dhis_data_sets

    DHIS2 Integration: Instructions and schema for defining DHIS2 integrations

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/integrations/index.html b/apps/guides/integrations/index.html index 417511e855..204b9c6c36 100644 --- a/apps/guides/integrations/index.html +++ b/apps/guides/integrations/index.html @@ -1,9 +1,9 @@ -Building Integrations | Community Health Toolkit +Building Integrations | Community Health Toolkit

    Building Integrations

    How to build integrations with external software and data sources

    DHIS2 Aggregate

    Design considerations and how to configure

    OpenMRS

    Exchange patient-level data with systems based on the OpenMRS platform

    OppiaMobile

    Components & configuration

    RapidPro

    Key concepts, design considerations, how to configure, and best practices

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/integrations/index.xml b/apps/guides/integrations/index.xml index 71e7909879..5242652465 100644 --- a/apps/guides/integrations/index.xml +++ b/apps/guides/integrations/index.xml @@ -1,947 +1,2 @@ -Community Health Toolkit – Building Integrationshttps://docs.communityhealthtoolkit.org/apps/guides/integrations/Recent content in Building Integrations on Community Health ToolkitHugo -- gohugo.ioenApps: DHIS2 Aggregatehttps://docs.communityhealthtoolkit.org/apps/guides/integrations/dhis2-aggregate/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/integrations/dhis2-aggregate/ -<p>One of the first things you’ll need to do is identify the specific <a href="https://docs.dhis2.org/2.34/en/dhis2_implementation_guide/data-sets-and-forms.html#what-is-a-data-set">DHIS2 data set</a> that you plan to implement. You’ll need a list of all the <a href="https://docs.dhis2.org/2.34/en/dhis2_implementation_guide/data-elements-and-custom-dimensions.html#data-elements">data elements</a> on that data set, a detailed understanding of how each is calculated, the frequency in which the data set is submitted (weekly, monthly, etc…), and for which <a href="https://docs.dhis2.org/2.34/en/dhis2_implementation_guide/organisation-units.html">organisation units</a> the data set applies. You’ll also want to identify and engage the appropriate DHIS2 stakeholders to get access to DHIS2 metadata, test environments, and discuss workflows. The DHIS2 documentation site provides additional background and advice <a href="https://docs.dhis2.org/2.34/en/dhis2_implementation_guide/integration-concepts.html#integration-concepts">here</a>.</p> -<h2 id="design-considerations">Design Considerations</h2> -<p>There are a few very important considerations related to how you design workflows and set up your CHT application to make sure you will be able to integrate with DHIS2. The main areas are:</p> -<ol> -<li>Hierarchies</li> -<li>Fields and calculations</li> -<li>Workflows and user access.</li> -</ol> -<p>These considerations are summarized below.</p> -<h3 id="hierarchies">Hierarchies</h3> -<p>The CHT relies on your Place hierarchy to determine how data should be aggregated for DHIS2. As such, it’s important that you consider how organization units are configured in the DHIS2 instance that you need to integrate data into. If your CHT Place hierarchy does not align with the DHIS2 organisation unit structure, the CHT will not be able to aggregate data in the way DHIS2 needs it.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -There should be a one-to-one relationship between DHIS2 organisation units and CHT places. -</div> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/concepts/hierarchy/">Hierarchies</a></p> -<h3 id="fields-and-calculations">Fields and calculations</h3> -<p>It’s important to understand each data element on the DHIS2 data set you want to integrate data into and how each data element is calculated. When you are designing your forms in the CHT, you will need to make sure you are capturing information in your forms so that every data element on the chosen DHIS2 data set can be calculated within the CHT.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Once you obtain the list of data elements on the data set, be sure to go through them one by one and figure out how to calculate each one using information available in your CHT forms. -</div> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/concepts/forms/">Forms</a></p> -<h3 id="workflows-and-user-access">Workflows and User Access</h3> -<p>The aggregate data workflow is really designed around 3 key user personas. You’ll need to make sure that your context can support those assumptions and will be able to have access to the appropriate features in the CHT and DHIS2.</p> -<p>Moving data from the CHT to DHIS2 can be done in three main ways.</p> -<ol> -<li>Manually downloading from the CHT</li> -<li>Building an app in DHIS2 and calling an API in the CHT</li> -<li>Orchestrating the steps using an interoperability layer such as <a href="http://openhim.org/">OpenHIM</a>, <a href="https://www.openfn.org/">OpenFn</a>, etc&hellip;</li> -</ol> -<h2 id="configuration">Configuration</h2> -<p>Once you have designed your hierarchies, forms, calculations, and workflows, there are a few key configurations that need to be made.</p> -<h3 id="data-sets">Data sets</h3> -<p>For the CHT to be able to export the file for DHIS2, it needs to know the specific name and ID of the data set in DHIS2. You will need to obtain the ID from the test or production DHIS2 environment.</p> -<p>Configure the data set in <code>app_settings.json</code>.</p> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/dhis2/">DHIS2 in App Settings</a></p> -<h3 id="organisation-units">Organisation units</h3> -<p>Aggregation in the CHT is based on your Place hierarchy. As mentioned in the Hierarchies Design Considerations above, your Places must align with DHIS2 organisation units. You will need to specify the DHIS2 organisation unit&rsquo;s ID on the Place document in the CHT.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Update the contact document of each place you wish to map to an organisation unit. Add a <code>dhis.orgUnit</code> attribute. -</div> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/reference/forms/contact/">Contact Forms</a></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;my_place&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;health_center&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;dhis&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;orgUnit&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;HJiPOcmziQA&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="data-elements">Data elements</h3> -<p>Calculations for DHIS2 indicators are done using CHT Target functionality. For each DHIS2 data element, you will need to configure a corresponding CHT Target and specify the ID of the DHIS2 data set and data element. If you do not include the data set, this data element will be included in every data set.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -In <code>targets.js</code>, configure one or more data elements by setting the <code>dhis.dataSet</code> and <code>dhis.dataElement</code> attributes in the target schema. -</div> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/reference/targets/">Targets</a></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#000">module</span><span style="color:#000;font-weight:bold">.</span><span style="color:#204a87;font-weight:bold">export</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;births-this-month&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;count&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;icon-infant&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.births.title&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">subtitle_translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.this_month.subtitle&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">goal</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesTo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contacts&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;person&#39;</span><span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">contact</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#ce5c00;font-weight:bold">!!</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">date</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">date_of_birth</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">dhis</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">dataSet</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;VMuFODsyWaO&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">dataElement</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;kB0ZBFisE0e&#39;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">];</span> -</span></span></code></pre></div><h3 id="users">Users</h3> -<p>For the HRIO role, create a new user role and a new user with that role; or update an existing user role. To export DHIS2 metrics, users need to have the following permissions: <code>can_configure</code>, <code>can_export_all</code>, and <code>can_export_dhis</code>.</p> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/concepts/users/">Users</a></p> -<h2 id="user-experience">User experience</h2> -<p>Once your CHT project is configured to integrate with DHIS2, follow these steps to export the data from CHT and import it into DHIS:</p> -<h3 id="export-data">Export data</h3> -<ol> -<li>Login to the CHT instance using a user account with the required permissions. You should be automatically navigated to the Admin Console.</li> -<li>Select &ldquo;Import &amp; export data&rdquo; from the side</li> -<li>Select &ldquo;DHIS2&rdquo; from the header along the top</li> -<li>Select the data set, org unit, and time period that you&rsquo;d like to send to DHIS2. Click &ldquo;Export&rdquo;</li> -<li>A file should download in your browser. This file contains a <a href="https://docs.dhis2.org/master/en/developer/html/webapi_data_values.html">dataValueSet</a> with aggregated user&rsquo;s data.</li> -</ol> -<h3 id="import-data">Import data</h3> -<ol> -<li>Login to DHIS2 using a user account with permission to manage the relevant dataset and organisation units.</li> -<li>Select the &ldquo;import/export&rdquo; application inside DHIS2. Select &ldquo;import data&rdquo;.</li> -<li>Upload the file you downloaded in Step 5.</li> -<li>Click &ldquo;Import&rdquo;</li> -</ol> -<p>Check the UI for any errors. If you get errors you don&rsquo;t understand or are unable to resolve an error, you can always ask for assistance on the <a href="https://forum.communityhealthtoolkit.org/c/support/18">CHT Forum</a>. If there are no errors, you can review the numbers that resulted by reviewing the data set in DHIS2.</p> -<h2 id="known-limitations">Known limitations</h2> -<ul> -<li>Data for each user is aggregated based on the contents of the user&rsquo;s target document. Note that if your users don&rsquo;t login and synchronize, their data won&rsquo;t be represented in the exported data.</li> -<li>Integrations are limited to <em>monthly</em> DHIS2 data sets.</li> -<li>Integrations are limited to numeric data-types. To support other data types, consider making a <a href="https://docs.dhis2.org/master/en/developer/html/apps_creating_apps.html">DHIS2 App</a> for your project.</li> -</ul>Apps: OpenMRShttps://docs.communityhealthtoolkit.org/apps/guides/integrations/openmrs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/integrations/openmrs/ -<p><a href="https://openmrs.org">OpenMRS</a> is an open source electronic medical record system used in dozens of countries. Integrating CHT apps with OpenMRS can open up opportunities to improve health outcomes for patients by promoting better coordination of care. For example, referrals by CHWs in the community can be sent electronically to health facilities using OpenMRS so that nurses and clinicians can prepare for their visit and have full access to the assessment done by a CHW.</p> -<p>Integrating CHT apps with OpenMRS can be achieved using the <a href="https://rest.openmrs.org/">OpenMRS REST API</a> and following the guidance in the <a href="https://docs.communityhealthtoolkit.org/apps/features/integrations/custom/">Custom Integrations</a> documentation.</p> -<h2 id="overview">Overview</h2> -<p>The CHT Core Framework supports integrations with OpenMRS in a variety of ways:</p> -<ol> -<li>Sending patient and patient contacts data</li> -<li>Sending reports (encounters and observations) data</li> -<li>Exposing an API for OpenMRS developers to pull data from CHT Core</li> -<li>Receiving data from OpenMRS</li> -</ol> -<p>Sending patients, patient contacts, and reports data can be achieved using the <a href="(https://docs.communityhealthtoolkit.org/apps/reference/app-settings/outbound/)">Outbound push</a>. Receiving data from OpenMRS can be achieved using the CHT Core Web <a href="https://docs.communityhealthtoolkit.org/apps/reference/api/">API</a>.</p> -<p>Common OpenMRS use cases include:</p> -<ol> -<li><strong>Linkage to care</strong>: Completion of medical visits after diagnosis</li> -<li><strong>Contact tracing</strong>: OpenMRS generates a list of contacts to be followed up</li> -<li><strong>Care coordination</strong>: Reminding patients to self-report or health-care givers to complete follow ups on patients of interest</li> -</ol> -<h2 id="prerequisites">Prerequisites</h2> -<p>As you design your usecases, bear in mind that at the heart of OpenMRS is the <a href="https://wiki.openmrs.org/display/docs/Concept+Dictionary+Basics">Concept Dictionary</a>. Every contact, relationship, encounter or observation metadata exists first, which guides the definition of the forms in the CHT. Therefore, you need to have good understanding of what data maps to what concept.</p> -<h2 id="getting-started">Getting started</h2> -<p>The <a href="https://docs.communityhealthtoolkit.org/apps/reference/api/">CHT API</a> and <a href="https://rest.openmrs.org/#openmrs-rest-api">OpenMRS API</a> are used for integration. However, the APIs do not do data cleaning and formatting out-of-the-box. Therefore, both systems require custom solutions that ochestrate the functionality to transform exchanged data to be accepted. In the following sections, we focus more on the general procedure for setting up custom modules and services.</p> -<h3 id="cht-to-openmrs">CHT to OpenMRS</h3> -<p>This section focuses on a simple process and the best practices to send data to OpenMRS.</p> -<h4 id="mapping-forms">Mapping forms</h4> -<p>The first thing is to define forms to capture data. Forms can be contact or app, which translate to patient and encounter (for example, observation, lab request, and referral) respectively. Forms are defined using the XLS Form standard. -Some of the best practices here include adopting a convention that results in minimum disruption (or that would require minimal processing) of the concept dictionary.</p> -<ol> -<li><strong>Defining contact forms</strong></li> -</ol> -<p>Here, you need to capture the basic details required for registering a patient or a patient contact in OpenMRS. Below is a sample naming convention for demographic details such as a person&rsquo;s name (under field name):</p> -<p><code>patient_familyName</code> for family_name, -<code>patient_firstName</code> for first_name, -<code>patient_middleName</code> for middle_name</p> -<p>Another example of patient identifiers could take the form <code>_IdentifierType_humanReadableName_IdentifierTypeUuid</code>. For example, national Id identifier type definition would be, <code>patient_identifierType_nationalId_49af6cdc-7968-4abb-bf46-de10d7f4859f</code>.</p> -<p>A sample form definition could be as follows:</p> -<table> -<thead> -<tr> -<th>type</th> -<th>name</th> -<th>label</th> -<th>required</th> -<th>relevant</th> -<th>appearance</th> -<th>constraint</th> -<th>constraint_message</th> -<th>calculation</th> -<th>choice_filter</th> -<th>hint</th> -<th>default</th> -</tr> -</thead> -<tbody> -<tr> -<td>begin group</td> -<td>patient_demographics</td> -<td>Demographic details</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>string</td> -<td>patient_familyName</td> -<td>Family name</td> -<td>yes</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>string</td> -<td>patient_firstName</td> -<td>First name</td> -<td>yes</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>string</td> -<td>patient_middleName</td> -<td>Middle name</td> -<td>yes</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>string</td> -<td>patient_identifierType_nationalId_49af6cdc-7968-4abb-bf46-de10d7f4859f</td> -<td>National ID</td> -<td>yes</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>end group</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -</tbody> -</table> -<p>A sample payload would be as follows:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>{ -</span></span><span style="display:flex;"><span> ..., -</span></span><span style="display:flex;"><span> &#34;patient_demographics&#34;: { -</span></span><span style="display:flex;"><span> &#34;patient_familyName&#34;: &#34;Doe&#34;, -</span></span><span style="display:flex;"><span> &#34;patient_firstName&#34;: &#34;John&#34;, -</span></span><span style="display:flex;"><span> &#34;patient_middleName&#34;: &#34;Test&#34;, -</span></span><span style="display:flex;"><span> &#34;patient_identifierType_nationalId_49af6cdc-7968-4abb-bf46-de10d7f4859f&#34;: &#34;38839128&#34;, -</span></span><span style="display:flex;"><span> &#34;patient_identifierType_clinicNumber_000f85aa-a460-46d1-87be-daabe7bd9d99&#34;: &#34;1271891&#34; -</span></span><span style="display:flex;"><span> } -</span></span><span style="display:flex;"><span>} -</span></span></code></pre></div><p>Such convention makes it easier to process the payload for queueing handling.</p> -<ol start="2"> -<li><strong>Defining app forms</strong></li> -</ol> -<p>An encounter form consists of input and observation groups in the XForm. <code>form_uuid</code> and <code>encounter_type_uuid</code> define the uuids for the encounter form and encounter type respectively. Please note that these are defined within the general <code>input</code> group provided by CHT.</p> -<p>The <code>observation</code> group is used to define the clinical observation variables to be collected by the form. This group is equivalent to the group of <code>&lt;obs&gt;</code> in OpenMRS html form entry module. In OpenMRS, an observation construct has concept ID (with inherent concept type), label, and answer options if it requires. In Xforms, one needs to define the following:</p> -<ul> -<li> -<p>Observation type (field type of the xlsform)</p> -</li> -<li> -<p>Name - this will be used as <code>key</code> in the generated JSON payload for the form data. Using the adopted convention above, <code>_conceptID_humanReadableConceptName_99DCT</code>, we could have:</p> -<p><code>_5089_weight_99DC</code> for weight.</p> -<p>For multi-select (obs group in OpenMRS), we can easily append <code>MULTISELECT</code> to the <code>humanReadableConceptName</code> for example: -<code>_162558_disabilityTypeMULTISELECT_99DCT</code> for diability type with the options <code>blind, dumb, ...</code></p> -</li> -<li> -<p>A label which is displayed to the user during form entry</p> -</li> -</ul> -<p>Here is a sample form snippet followed by sample select list in the choices worksheet.</p> -<h5 id="data-fields">Data fields</h5> -<table> -<thead> -<tr> -<th>type</th> -<th>name</th> -<th>label</th> -<th>required</th> -<th>relevant</th> -<th>appearance</th> -<th>constraint</th> -<th>constraint_message</th> -<th>calculation</th> -<th>choice_filter</th> -<th>hint</th> -<th>default</th> -</tr> -</thead> -<tbody> -<tr> -<td>calculate</td> -<td>form_uuid</td> -<td>NO_LABEL</td> -<td>yes</td> -<td></td> -<td></td> -<td></td> -<td>_99DCT</td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>calculate</td> -<td>encounter_type_uuid</td> -<td>NO_LABEL</td> -<td>yes</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>begin group</td> -<td>group_assessment</td> -<td>Assessment</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>select_one client_consented</td> -<td>_1710_clientConsented_99DCT</td> -<td>Has ${patient_name} consented?</td> -<td>yes</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>select_multiple disability_type</td> -<td>_162558_disabilityTypeMULTISELECT_99DCT</td> -<td>Disability type</td> -<td>yes</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>text</td> -<td>_160632_Specify_99DCT</td> -<td>Specify (Other)</td> -<td>yes</td> -<td>${_162558_disabilityTypeMULTISELECT_99DCT} = &lsquo;other&rsquo;</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>end group</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -</tbody> -</table> -<h5 id="choices">Choices</h5> -<table> -<thead> -<tr> -<th>list_name</th> -<th>name</th> -<th>label</th> -</tr> -</thead> -<tbody> -<tr> -<td>client_consented</td> -<td>_1065_Yes_99DCT</td> -<td>Yes</td> -</tr> -<tr> -<td>client_consented</td> -<td>_1066_No_99DCT</td> -<td>No</td> -</tr> -<tr> -<td>disability_type</td> -<td>_1058_vision_99DCT</td> -<td>Vision</td> -</tr> -<tr> -<td>disability_type</td> -<td>_1059_hearing_99DCT</td> -<td>Hearing</td> -</tr> -<tr> -<td>disability_type</td> -<td>_1060_mental_99DCT</td> -<td>Mental</td> -</tr> -<tr> -<td>disability_type</td> -<td>_1061_other_99DCT</td> -<td>Other</td> -</tr> -</tbody> -</table> -<p>Remember to convert and upload your forms</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>cht --url<span style="color:#ce5c00;font-weight:bold">=</span>https://&lt;username&gt;:&lt;password&gt;@localhost --accept-self-signed-certs convert-contact-forms upload-contact-forms convert-app-forms upload-app-forms -</span></span></code></pre></div> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Remember to setup the <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/outbound/">Outbound push</a> modules to send data to OpenMRS. -</div> -<h4 id="handling-the-data">Handling the data</h4> -<p>After collecting data using the forms defined above, the next step is to process and persist it in OpenMRS. Processing includes:</p> -<ol> -<li><strong>Cleaning</strong></li> -</ol> -<p>First, the data has to be transformed to an OpenMRS-compatible format before it is queued. This means that you need to define custom RESTful endpoints if not already existing, that would be utilized by the Outbound push modules configured above. Transformation basically involves extracting form data into an object. Most importantly, <code>discriminators</code>, which are like flags appended to form data to inform the type of data being processed. For example, a <code>registration</code> discriminator implies that we&rsquo;re dealing with demographic details. A sample transformed payload is shown below:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>{ -</span></span><span style="display:flex;"><span> &#34;patient&#34;: { -</span></span><span style="display:flex;"><span> &#34;patient.family_name&#34;: &#34;Doe&#34;, -</span></span><span style="display:flex;"><span> &#34;patient.first_name&#34;: &#34;John&#34;, -</span></span><span style="display:flex;"><span> &#34;patient.middle_name&#34;: &#34;Test&#34;, -</span></span><span style="display:flex;"><span> &#34;patient.other_identifier&#34;: [ -</span></span><span style="display:flex;"><span> { -</span></span><span style="display:flex;"><span> &#34;identifier_type_uuid&#34;: &#34;49af6cdc-7968-4abb-bf46-de10d7f4859f&#34;, -</span></span><span style="display:flex;"><span> &#34;identifier_type&#34;: &#34;38839128&#34;, -</span></span><span style="display:flex;"><span> &#34;identifier_type_name&#34;: &#34;National ID&#34;, -</span></span><span style="display:flex;"><span> }, -</span></span><span style="display:flex;"><span> { -</span></span><span style="display:flex;"><span> &#34;identifier_type_uuid&#34;: &#34;000f85aa-a460-46d1-87be-daabe7bd9d99&#34;, -</span></span><span style="display:flex;"><span> &#34;identifier_type&#34;: &#34;1271891&#34;, -</span></span><span style="display:flex;"><span> &#34;identifier_type_name&#34;: &#34;Clinic Number&#34;, -</span></span><span style="display:flex;"><span> } -</span></span><span style="display:flex;"><span> ] -</span></span><span style="display:flex;"><span> }, -</span></span><span style="display:flex;"><span> &#34;observation&#34;: { -</span></span><span style="display:flex;"><span> &#34;1710^CLIENT CONSENTED^99DCT&#34;: &#34;1065^Yes^99DCT&#34;, -</span></span><span style="display:flex;"><span> &#34;1542^OCCUPATION^99DCT&#34;: &#34;1538^Farmer^99DCT&#34; -</span></span><span style="display:flex;"><span> }, -</span></span><span style="display:flex;"><span> &#34;discriminator&#34;: { -</span></span><span style="display:flex;"><span> &#34;discriminator&#34;: &#34;registration&#34; -</span></span><span style="display:flex;"><span> } -</span></span><span style="display:flex;"><span>} -</span></span></code></pre></div><ol start="2"> -<li><strong>Queueing</strong></li> -</ol> -<p>This step involves adding a transformed payload, now objects, to a queue. This is important because it helps to capture errors that occur during processing and the data can be corrected and re-queued. A queue will have both registration and encounter objects.</p> -<p>You need to define a queue processor that will be consuming the queue and push each object to the rightful handler.</p> -<ol start="3"> -<li><strong>Data handlers</strong></li> -</ol> -<p>Data handlers are responsible for persisting the data in OpenMRS. The handler can also be used to trigger feedback to submitters of the data (this can be a brief summary such as number of synced documents and success rate). The following steps apply:</p> -<ul> -<li>Define handlers for each object type (e.g. person / patient, trace report.</li> -<li>Create a scheduler to start the queue processor above. The queue processor shall get to the handler for processing.</li> -<li>Closely monitor the errors log for prompt action where necessary.</li> -</ul> -<p>You may want to further configure a service that relays feedback to the CHT. Feel free to utilize in-app text messages, which can be triggered via the <a href="https://docs.communityhealthtoolkit.org/apps/reference/api/#post-apisms">sms endpoint</a> of CHT. The health workers would receive these feedback messages on their phones as well as access via the Messages tab in-app.</p> -<h4 id="error-handling">Error Handling</h4> -<p>Exceptions thrown during processing can be added to a queue and presented on an interface for action. The erring data may then be re-queued. You need to consider what works best at this point. For convenience, it makes sense for a backend user to resolve such errors.</p> -<p>Sample OpenMRS handler scripts include:</p> -<ol> -<li><a href="https://github.com/PIH/openmrs-module-emr">PIH Malawi&rsquo;s EMR handler</a></li> -<li><a href="https://github.com/palladiumkenya/openmrs-module-afyastat">KenyaEMR handler</a></li> -</ol> -<h3 id="openmrs-to-cht">OpenMRS to CHT</h3> -<h4 id="scheduled-tasks">Scheduled tasks</h4> -<p>This includes defining a scheduler and a task that will be compiling the payload to be pushed to the CHT on specific intervals. Remember that the CHT expects data in JSON format. -The CHT API can be used to process incoming reports. For custom payloads, the <a href="https://docs.couchdb.org/en/stable/api/database/bulk-api.html#db-bulk-docs">{db}/bulk_docs</a> can be utilized to save multiple payloads concurrent</p> -<h4 id="listener-script-in-the-cht">Listener script in the CHT</h4> -<p>This is a service that would help shift information in the CHT <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/hierarchy/">hierarchy</a> to support the usecases of interest. Through {db}/bulk_docs, OpenMRS posts a data record that contains data objects such as observations and contact list (if available) details. The service obtains the record, and unpacks it into a contact in CHT and parented under the correct hierarchy level based on the metadata received from OpenMRS. The script should:</p> -<ol> -<li>Do patient matching to avoid duplicate details. Querying can be achieved via available endpoints such as <code>contacts_by_phone</code> <a href="https://docs.communityhealthtoolkit.org/apps/reference/api/#get-apiv1contacts-by-phone">endpoint</a> and <code>hydrate</code> <a href="https://docs.communityhealthtoolkit.org/apps/reference/api/#get-apiv1hydrate">endpoint</a> among others/</li> -<li>If the incoming data matches what exists, update the contact found in CHT.</li> -<li>Process all reports payloads and append them to the linked contact’s profile. Note that the <code>&lt;report type&gt;</code> xml or JSON form has to be defined in the CHT.</li> -<li>Delete the payload that is received from OpenMRS after it is processed since it will have been used to create CHT data structures.</li> -<li>Monitor and log failed transactions</li> -</ol> -<p>Once you have configured the above for data exchange, the data flow will be like this:</p> -<p><img src="cht-openmrs.jpg" alt="CHT - OpenMRS Data Flow"></p>Apps: OppiaMobilehttps://docs.communityhealthtoolkit.org/apps/guides/integrations/oppia/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/integrations/oppia/ -<p>The training modules configuration consists of five main components:</p> -<ul> -<li>App Forms - Content that the user will interact with;</li> -<li>Tasks - How forms are presented to the user: how and when the user accesses the forms for input;</li> -<li>Targets - Shows the progress of the user;</li> -<li>Contact Summary - Gives a highlight of the modules completed by the user;</li> -<li>Context - Defines what forms are available to fill from the user’s profile, or available as tasks.</li> -</ul> -<h3 id="app-forms">App Forms</h3> -<p>The CHT application uses <a href="https://docs.communityhealthtoolkit.org/apps/reference/forms/app/">XLSForms</a> (app forms), which are a simplified method of setting up form configurations using Excel (or Libre Office Calc, Google sheets, etc). The forms contain the questions/content that the user will interact with, including <a href="https://oppiamobile.readthedocs.io/en/latest/implementers/integration/launch_from_other_app.html">web links</a> that enable the users to navigate from the CHT application to a specific course in OppiaMobile. App forms are typically created in the <code>project-folder &gt; forms &gt; app</code> directory of a project. If the content requires a user to access any form of media, then a media folder for the specific form is created and named after the form. For example, to add video content for a form module_one.xlsx, save the video file to the following directory: <code>project-folder &gt; forms &gt; app &gt; module_one-media &gt; video</code>. Once the forms are set up with content, the forms are converted to XForms, which are in xml format. To limit access of App Forms to certain contacts, an App Forms must have a properties file, which defines when and for whom certain forms should be accessed. Once configured, the forms are uploaded to an instance using the CHT configurer with the following commands, which upload specific forms and all forms respectively:</p> -<pre tabindex="0"><code>cht --instance=&lt;instance&gt; convert-app-forms upload-app-forms -- &lt;form1&gt; &lt;form2&gt; -</code></pre><p>or</p> -<pre tabindex="0"><code>cht --instance=&lt;instance&gt; convert-app-forms upload-app-forms -</code></pre><p>The image below shows an example of an XLSForm configured for the Educational Modules:</p> -<figure class="left col-12 col-lg-9"><a href="xls-modules.png"> -<img src="xls-modules.png"/> </a> -</figure> -<h3 id="integration-with-oppiamobile-using-weblinks">Integration with OppiaMobile using weblinks</h3> -<p>The CHT application makes use of weblinks to direct the user to the OppiaMobile application. Each task in the CHT has a weblink configured to point to a specific course in OppiaMobile. The weblinks are configured in the forms as a button, which, when clicked or tapped, redirects the user accordingly, depending on the installation status of the OppiaMobile application and the respective course. The weblinks are configured in each of the XLSForms that are triggered by a selected task as illustrated in the image below:</p> -<figure class="left col-12 col-lg-9"><a href="weblink.png"> -<img src="weblink.png"/> </a> -</figure> -<br clear="all"> -<p><code>[&lt;span style='background-color: #648af7; color:white; padding: 1em; text-decoration: none;border-radius: 8px; '&gt;Open Oppia Mobile&lt;/span&gt;]</code> represents the button styling and label.</p> -<p><code>https://staging.cha.oppia-mobile.org/view?course=introduction-to-covid-19</code> represents the weblink, where <code>course=introduction-to-covid-19</code> specifies the name of the course to be launched in OppiaMobile as configured on Moodle.</p> -<p>This image shows the outcome of the button configuration:</p> -<figure class="left col-7 col-lg-4"><a href="weblink-config-outcome.png"> -<img src="weblink-config-outcome.png"/> </a> -</figure> -<h3 id="tasks">Tasks</h3> -<p><a href="https://docs.communityhealthtoolkit.org/apps/reference/tasks/">Tasks</a> are a set of actions available to users from the task tab. Selecting a task opens up a specific form that completes a workflow. Tasks are available within a given timeframe, after which they expire and the user is unable to view or do them. Tasks are defined as an array of objects in a tasks.js file under the project folder, with each task following the task schema. The required properties of a task include:</p> -<ul> -<li>Name - unique identifier of the task;</li> -<li>Title - displays the workflow to be completed for a contact;</li> -<li>AppliesTo - determines if the task is emitted per contact or report;</li> -<li>ResolvedIf - conditions to mark a task as resolved/completed;</li> -<li>Events - specifies the timeframe of a task;</li> -<li>Actions - specifies the forms accessed by the user and allows injecting content from previous submissions.</li> -</ul> -<p>On completing the task configuration, the following command is used to compile and upload the applications settings (tasks, targets, contact-summary, form properties):</p> -<p><code>cht --instance=&lt;instance&gt; compile-app-settings upload-app-settings</code></p> -<p>The code snippet below illustrates an example of a task configured for the educational modules:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">title</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;Community Health Academy&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">name</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;cha-module-one-oppia&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">contactLabel</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;Introduction To Covid-19&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#4e9a06">&#39;icon-cha&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesTo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contacts&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;person&#39;</span><span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">c</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000">c</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">role</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;chw&#39;</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">user</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">user</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">type</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;health_center&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">actions</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">form</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;cha_module_one&#39;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">events</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">dueDate</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">event</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">c</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">Utils</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">addDate</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">new</span> <span style="color:#204a87">Date</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">c</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reported_date</span><span style="color:#000;font-weight:bold">),</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">start</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">end</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">25550</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">resolvedIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">c</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">c</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reports</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">some</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000">report</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">form</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;cha_module_one&#39;</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">Utils</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getField</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;assessment_passed&#39;</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;yes&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="targets">Targets</h3> -<p>The users also have access to <a href="https://docs.communityhealthtoolkit.org/apps/reference/targets/">targets</a>. Targets are a visual representation of the progress and goals of the user. These are app analytics accessed through the targets tab, where the user is able to view how many modules they have completed. Similar to tasks, targets are defined as an array of objects in a targets.js file under the project folder, with each target following the targets schema. The required properties of a target object include:</p> -<ul> -<li>Id - unique identifier of the target;</li> -<li>Translation_key - title displayed for the widget;</li> -<li>Type - type of numeric representation i.e. count/percentage;</li> -<li>Goal - denotes how much the user should achieve;</li> -<li>appliesTo - Determines whether a contact or a report is counted.</li> -</ul> -<p>On completing the targets configuration, the following command is used to compile and upload the applications settings (tasks, targets, contact-summary, form properties):</p> -<pre tabindex="0"><code>cht --instance=&lt;instance&gt; compile-app-settings upload-app-settings -</code></pre><p>Below is a code snippet for a target configured for the educational modules:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;training-modules-completed&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;percent&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;icon-cha&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">goal</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">100</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">context</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;user.role === &#34;chw&#34;&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.training_completion.title&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">subtitle_translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.all_time.subtitle&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesTo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contacts&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;person&#39;</span><span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">getField</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;role&#39;</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;chw&#39;</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">idType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;report&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">passesIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reports</span> <span style="color:#ce5c00;font-weight:bold">||</span> <span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reports</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">aggregate</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">date</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;now&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">emitCustom</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">emit</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">original</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">assessmentModules</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;cha_module_one&#39;</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06">&#39;cha_module_two&#39;</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06">&#39;cha_module_three&#39;</span><span style="color:#000;font-weight:bold">];</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">for</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">let</span> <span style="color:#000">eligibleModule</span> <span style="color:#204a87;font-weight:bold">of</span> <span style="color:#000">assessmentModules</span><span style="color:#000;font-weight:bold">){</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">emit</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87">Object</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">assign</span><span style="color:#000;font-weight:bold">({},</span> <span style="color:#000">original</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">_id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">`</span><span style="color:#4e9a06">${</span><span style="color:#000">eligibleModule</span><span style="color:#4e9a06">}</span><span style="color:#4e9a06">`</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">pass</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}));</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">validReports</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reports</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">filter</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">((</span><span style="color:#000">assessmentModules</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">includes</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">form</span><span style="color:#000;font-weight:bold">))</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">report</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">fields</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">assessment_passed</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;yes&#39;</span><span style="color:#000;font-weight:bold">));</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">for</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">let</span> <span style="color:#000">report</span> <span style="color:#204a87;font-weight:bold">of</span> <span style="color:#000">validReports</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">instance</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87">Object</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">assign</span><span style="color:#000;font-weight:bold">({},</span> <span style="color:#000">original</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">_id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">`</span><span style="color:#4e9a06">${</span><span style="color:#000">report</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">form</span><span style="color:#4e9a06">}</span><span style="color:#4e9a06">`</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">pass</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">});</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">emit</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">instance</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="contact-summary">Contact Summary</h3> -<p>In addition to targets, the user is able to see which specific modules they have completed, and view upcoming tasks and other general information on their profile. The section containing this information is known as the <a href="https://docs.communityhealthtoolkit.org/apps/reference/contact-page/">contact summary</a>. The contact summary has 3 main outputs: cards, fields, and context. Contact summary is defined in the <code>contact-summary.templated.js</code> file under the project folder.</p> -<p>Cards are an array of objects which can be customized to group information viewed on a contact’s profile. The required properties of a card include:</p> -<ul> -<li>Label - title of the card;</li> -<li>Fields - content of the card; -<ul> -<li>Field name- title/label of a field;</li> -<li>Field value - value displayed for a field.</li> -</ul> -</li> -</ul> -<p>The code snippet below shows an example of a card configured for the educational modules:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">isCHW</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">getField</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;role&#39;</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;chw&#39;</span><span style="color:#000;font-weight:bold">;</span> <span style="color:#000;font-weight:bold">};</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">hasCompletedModuleTraining</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">form</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">reports</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">reports</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">some</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000">report</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">form</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#000">form</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">report</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">fields</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">assessment_passed</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;yes&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">};</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">hasCompletedModuleOne</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">hasCompletedModuleTraining</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;cha_module_one&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">hasCompletedModuleTwo</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">hasCompletedModuleTraining</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;cha_module_two&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">hasCompletedModuleThree</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">hasCompletedModuleTraining</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;cha_module_three&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">cards</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.profile.training&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;person&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">isCHW</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">fields</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">let</span> <span style="color:#000">fields</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">[];</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">completedModuleOne</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">hasCompletedModuleOne</span> <span style="color:#ce5c00;font-weight:bold">?</span> <span style="color:#4e9a06">&#39;Complete&#39;</span> <span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;Incomplete&#39;</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">completedModuleTwo</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">hasCompletedModuleTwo</span> <span style="color:#ce5c00;font-weight:bold">?</span> <span style="color:#4e9a06">&#39;Complete&#39;</span> <span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;Incomplete&#39;</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">completedModuleThree</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">hasCompletedModuleThree</span> <span style="color:#ce5c00;font-weight:bold">?</span> <span style="color:#4e9a06">&#39;Complete&#39;</span> <span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;Incomplete&#39;</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#000">fields</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">push</span><span style="color:#000;font-weight:bold">(</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;icon-cha&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;Introduction To COVID-19&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">completedModuleOne</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">width</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">6</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;icon-cha&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;COVID Care&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">completedModuleTwo</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">width</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">6</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;icon-cha&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;COVID Misinformation&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">completedModuleThree</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">width</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">6</span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">fields</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">]</span> -</span></span></code></pre></div><br clear="all"> -<p>This image shows the training card configured to show completion status of the educational modules:</p> -<figure class="left col-7 col-lg-4"><a href="status.png"> -<img src="status.png"/> </a> -</figure> -<br clear="all"> -<p>Context provides information to App Forms, which are initiated from the contact&rsquo;s profile page. To show an App Form on a contact’s profile, the form’s expression field in its properties file must evaluate to true for the contact. The context information from the profile is accessible as the variable <code>summary</code>.</p> -<p>The code snippet below shows the context variables that can be accessed in the app forms:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">context</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">hasCompletedModuleOne</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">hasCompletedModuleTwo</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">hasCompletedModuleThree</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">};</span> -</span></span></code></pre></div><br clear="all"> -<p>The three variables <code>hasCompletedModuleOne</code>, <code>hasCompletedModuleTwo</code> and <code>hasCompletedModuleThree</code> are used in the educational modules app forms to determine whether the user will access the forms through the care guide.</p> -<p>The code snippet below shows an example of App Form properties, where the user can only access the form as a care guide if they have completed module one task. This is defined by the phrase <code>summary.hasCompletedModuleOne</code> in the expression statement.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;icon&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;icon-person&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;context&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;person&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;place&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;expression&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;!contact || (contact.type === &#39;person&#39; &amp;&amp; user.parent.type === &#39;health_center&#39; &amp;&amp; user.role === &#39;chw&#39; &amp;&amp; contact.role === &#39;chw&#39; &amp;&amp; summary.hasCompletedModuleOne)&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div>Apps: RapidProhttps://docs.communityhealthtoolkit.org/apps/guides/integrations/rapidpro/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/integrations/rapidpro/ -<p><a href="https://app.rapidpro.io/">RapidPro</a> is the open-source platform that powers <a href="https://textit.com/">TextIt</a>, developed by UNICEF and Nyaruka. RapidPro allows you to visually build messaging workflows for mobile-based services. Review RapidPro’s documentation to familiarize yourself with various components that include the <a href="https://rapidpro.io/api/v2/">API</a>. Before you embark on designing an integrated RapidPro/CHT workflow, you should start by understanding the needs of your users, identifying a problem to solve, and establishing goals. While an integrated RapidPro/CHT workflow can open up many powerful and personalized messaging capabilities, introducing an additional technology solution does come with complexities and cost. A good way to mitigate some of the complexities of setting up and <a href="https://rapidpro.github.io/rapidpro/docs/hosting/">hosting</a> RapidPro yourself is to utilize a SaaS solution such as <a href="https://textit.in/">TextIt</a>. TextIt offers transparent per message <a href="https://textit.com/pricing">pricing</a> and free credits to start off.</p> -<p>Coming up with a design to accommodate user needs requires a detailed understanding of the capabilities of both systems and conceptually where it makes sense to introduce interactions between them. Below we introduce some of the key concepts in RapidPro, but to learn more you can take one of their <a href="https://community.rapidpro.io/online-courses/">online courses</a>, watch some of their <a href="https://community.rapidpro.io/videos/">videos</a>, and check out their <a href="https://community.rapidpro.io/deployment-toolkit/">deployment toolkit</a>.</p> -<h2 id="rapidpro-concepts">RapidPro Concepts</h2> -<p>Before designing your integrated RapidPro/CHT workflow, it&rsquo;s important to understand a couple key functional concepts in RapidPro: <em>Flows</em>, <em>Campaigns</em>, and <em>Triggers</em>.</p> -<h3 id="flows">Flows</h3> -<figure class="right col-7 col-lg-5"><a href="flow-concept.png"> -<img src="flow-concept.png"/> </a> -</figure> -<p><a href="https://help.nyaruka.com/en/article/introduction-to-flows-1vmh15z/">Flows</a> are a series of steps you link together visually to define the interactions users will have. At any point in the flow, you can trigger actions such as sending an SMS, email, or calling external APIs. Flows are the base for RapidPro&rsquo;s other features.</p> -<p><em>Use Case Example:</em> Send an SMS to a patient asking if they are experiencing any new symptoms today. If so, let the patient know that a CHW will contact them. If not, let the patient know that they will receive another check-in message in three days and to contact their CHW if any new symptoms develop before then.</p> -<p><em>Additional Resources:</em> <a href="https://app.rapidpro.io/video/">Mastering Flows</a> and <a href="https://youtu.be/WcFhpSFhFug">How to Build a RapidPro Flow</a>.</p> -<h3 id="campaigns">Campaigns</h3> -<figure class="right col-5"><a href="campaign-concept.png"> -<img src="campaign-concept.png"/> </a> -</figure> -<p><a href="https://help.nyaruka.com/en/article/introduction-to-campaigns-1d71057/">Campaigns</a> allow you to schedule messages and flows around a date unique to each contact in a group, like a delivery date or registration date. You can add any number of <a href="https://help.nyaruka.com/en/article/adding-a-campaign-event-1amovrz/">Events</a> to the Campaign.</p> -<p><em>Use Case Example:</em> Send a daily check-in message to quarantined patients for 14 days to see if they have developed any symptoms.</p> -<p><em>Additional Resources:</em> <a href="https://youtu.be/3tJPOoHxJXE">Creating a Campaign</a></p> -<h3 id="triggers">Triggers</h3> -<figure class="right col-5"><a href="trigger-concept.png"> -<img src="trigger-concept.png"/> </a> -</figure> -<p><a href="https://help.nyaruka.com/en/article/introduction-to-triggers-1pm94xb/">Triggers</a> allow you to control how or when a Flow begins. A Trigger can be a keyword received in a text, a point in time, a missed call, or even a follow to a Twitter handle.</p> -<p><em>Use Case Example:</em> Start a health assessment Flow whenever a person sends the text <code>assessment</code> to a specific short code.</p> -<p><em>Additional Resources:</em> <a href="https://help.nyaruka.com/en/article/creating-a-keyword-trigger-that-starts-a-flow-bpmhw0/">Creating a Keyword Trigger that starts a Flow</a></p> -<h2 id="workflow-design">Workflow Design</h2> -<p>Designing an integrated workflow between multiple systems is more of an art than a science. Drafting a sequence diagram (below) is a good first step. When drafting your sequence diagram, it is useful to consider a few key integration touchpoints that are common across many integrated workflows.</p> -<ol> -<li>One system <strong>initiates or triggers an action in</strong> the other system</li> -<li>One system needs to <strong>get information from</strong> the other system</li> -<li>One system wants to <strong>store or update data in</strong> the other system</li> -</ol> -<h3 id="sequence-diagrams">Sequence Diagrams</h3> -<p>A <em>sequence diagram</em> will help you identify the various actors in a given workflow and what the flow of data will look like. Below is an overview and example diagram for a patient initiated triage and feedback workflow.</p> -<ol> -<li>Patient triggers a RapidPro flow by sending a message to a short code</li> -<li>Using the phone number of the patient, RapidPro requests information from the CHT</li> -<li>The CHT responds with the patient&rsquo;s name and other details</li> -<li>RapidPro sends personalized messages to conduct triage for the patient</li> -<li>RapidPro determines the outcome</li> -<li>RapidPro sends the outcome to the patient via SMS and posts the results to the CHT</li> -<li>If the patient needs to be followed-up with, the CHT creates a task for the appropriate CHW</li> -<li>Once the CHW completes that task, the CHT initiates the Feedback Flow in RapidPro</li> -<li>RapidPro logic records feedback via SMS from the patient</li> -<li>The results of the feedback flow are saved in the CHT</li> -</ol> -<p><img src="sequence-diagram.png" alt="Sequence"></p> -<h2 id="configuration">Configuration</h2> -<p>The information below focuses on specific interactions between RapidPro and the CHT, it does not cover RapidPro specific configuration, consult RapidPro documentation and resources for that. Also, the examples and code snippets below are using TextIt, the hosted RapidPro service mentioned above.</p> -<h3 id="create-rapidpro-user-in-cht">Create RapidPro user in CHT</h3> -<p>For RapidPro to communicate with the CHT, you need to create a <a href="https://docs.communityhealthtoolkit.org/apps/concepts/users/">User</a> in the CHT that will be used by RapidPro when calling the CHT’s APIs. This can be done from the <a href="https://docs.communityhealthtoolkit.org/apps/features/admin/">App Management</a> page in the CHT. When adding the user in the CHT, be sure to select the <code>Gateway - Limited access user for Medic Gateway</code> <a href="https://docs.communityhealthtoolkit.org/apps/concepts/users/#roles">Role</a>.</p> -<h3 id="globals">Globals</h3> -<p><a href="https://help.nyaruka.com/en/article/global-variables-km8la6/">Globals</a> are shared values that can be referenced in flows, as well as broadcasts and campaigns, within your account referenced by <code>@globals.value_name</code>. They allow you to create a value once and use it repeatedly without having to reenter the value. Likewise, globals make updating a shared value much easier. Rather than manually changing a value everywhere it&rsquo;s used in your account, simply update the value found in your <code>Globals</code> page.</p> -<p><img src="globals.png" alt="Globals"></p> -<p>Once you have configured a Global value, you can easily use it in your flows like this:</p> -<p><img src="globals-usage.png" alt="Globals-Usage"></p> -<h3 id="start-rapidpro-flow-from-cht">Start RapidPro Flow from CHT</h3> -<p>One of the most common activities you&rsquo;ll want to do is trigger a Flow in RapidPro based on something that occurred in the CHT. For example&hellip; whenever a specific form is submitted in the CHT with some conditional value, start a flow in RapidPro. To do this, you will use the <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/outbound/">Outbound</a> feature in the CHT, invoking the <a href="https://rapidpro.io/api/v2/explorer/">Flow Starts Endpoint</a> in RapidPro.</p> -<p>Below is an example <code>outbound</code> config in the CHT called <code>textit-self-quarantine</code> that will trigger a flow in RapidPro whenever a <code>covid_trace_follow_up</code> form is submitted in the CHT where <code>symptom = no</code>. It will also pass an extra date value for <code>self_quarantine_enrollment</code>.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-JSON" data-lang="JSON"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;textit-self-quarantine&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;relevant_to&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;doc.type === &#39;data_record&#39; &amp;&amp; doc.form === &#39;covid_trace_follow_up&#39; &amp;&amp; doc.fields.trace.symptom === &#39;no&#39;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;destination&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;base_url&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;https://textit.in&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;auth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;header&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Authorization&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;value_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;textit.in&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;path&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;/api/v2/flow_starts.json&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;mapping&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;flow&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;expr&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&lt;UUID of Flow in RapidPro&gt;&#39;&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;urns&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;expr&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;[ &#39;tel:&#39; + doc.fields.inputs.contact.phone ]&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;optional&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;extra.self_quarantine_enrollment&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;expr&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;new Date(doc.reported_date).getDate()+&#39;-&#39;+ (new Date(doc.reported_date).getMonth()+1) +&#39;-&#39; + new Date(doc.reported_date).getFullYear()+ &#39; &#39; + new Date(doc.reported_date).getHours() + &#39;:&#39; + new Date(doc.reported_date).getMinutes()&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;optional&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;extra.name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;doc.fields.inputs.contact.surname&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="save-flow-data-to-the-cht">Save flow data to the CHT</h3> -<p>Once a user has completed a Flow in RapidPro, it is likely you will want to record some of the information collected in the RapidPro flow back in the CHT. This can be achieved by utilizing the <a href="https://help.nyaruka.com/en/article/calling-a-webhook-adicxq/">Call Webhook</a> action in RapidPro.</p> -<table> -<thead> -<tr> -<th>Step</th> -<th>Application</th> -<th>Config step</th> -</tr> -</thead> -<tbody> -<tr> -<td>1</td> -<td>CHT</td> -<td>Configure a <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/forms/">JSON Form</a> that includes the fields from RapidPro you want to send to the CHT.</td> -</tr> -<tr> -<td>2</td> -<td>RapidPro</td> -<td>Add a <em>Call a Webhook</em> node.</td> -</tr> -<tr> -<td>3</td> -<td>RapidPro</td> -<td><code>POST</code> to the <a href="https://docs.communityhealthtoolkit.org/apps/reference/api/#post-apiv2records">records endpoint</a> in the CHT. If you used the Global value mentioned above, the POST will look something like <code>@globals.api/v2/records</code>.</td> -</tr> -<tr> -<td>4</td> -<td>RapidPro</td> -<td>Set a <code>Result Name</code></td> -</tr> -<tr> -<td>5</td> -<td>RapidPro</td> -<td>Configure HTTP Headers to be <code>Content-Type</code> -&gt; <code>application/json</code></td> -</tr> -<tr> -<td>6</td> -<td>RapidPro</td> -<td>Configure the <code>POST Body</code> (see example below)</td> -</tr> -</tbody> -</table> -<figure><a href="post-to-cht.png"> -<img src="post-to-cht.png"/> </a> -</figure> -<h3 id="look-up-cht-data-from-rapidpro">Look up CHT data from RapidPro</h3> -<p>Another common action you will likely need to perform in RapidPro is getting information from the CHT about a user or patient based on their phone number. You can use the <a href="https://docs.communityhealthtoolkit.org/apps/reference/api/#contacts-by-phone">contacts-by-phone</a> API to get fully hydrated contacts associated to that phone number.</p> -<h2 id="best-practices">Best Practices</h2> -<h3 id="messages">Messages</h3> -<ol> -<li>If language options are included allow users to select their preference before beginning the flow</li> -<li>Make sure the language and terminology of your messages are appropriate for the audience</li> -<li>Use a personalized welcome message before asking users to take actions</li> -<li>Keep content brief and relevant to the topic as to not overload the user</li> -<li>Make your response requests and calls-to-action clear</li> -<li>Be cognizant of form collisions during assessments (ex. “0=no, 1=yes” if those numbers may also reference workflow codes, or “N=no” if “N” is the code to create a new person)</li> -<li>Use standards in logic where possible (ex. The non-zero value is true; 0=false, 1=true, 0=no, 1=yes)</li> -<li>Sign the first message with the partner’s name, or MOH for visibility</li> -<li>Consider providing an option to revisit information again (ex. Text 123 to this number to receive these messages again)</li> -</ol> -<h3 id="workspaces">Workspaces</h3> -<p>A workspace contains models for a set of RapidPro users, and it also identifies a company or project. Set up <code>staging</code> and <code>production</code> workspaces so that builds, tests and updates can proceed safely before and after deployment.</p> -<h3 id="sms-messaging-channels">SMS Messaging Channels</h3> -<p>RapidPro supports both Android channels and SMS aggregators.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Android channels have a messaging limit of 330 outgoing messages per hour. -</div> -<p>Ensure that you install the maximum number of SMS packs (available in the RapidPro SMS Channel Android app) and follow these best practices when using Android channels:</p> -<ol> -<li> -<p>Prevent the Android channel from going to sleep. -Some steps to achieve this are available on <a href="https://dontkillmyapp.com">dontkillmyapp</a>:</p> -<ul> -<li>Do not optimize battery usage.</li> -<li>Make sure that the app is on the list of Apps that will not be put to sleep.</li> -<li>Set <code>Background Limit</code> to Standard Limit and confirm that the RapidPro app has the toggle enabled in &ldquo;Background Check&rdquo; and all of the apps have &ldquo;App Standby state: ACTIVE&rdquo; in Standby apps.</li> -<li>Keep the phone plugged into a power source.</li> -</ul> -</li> -<li> -<p>Automatically wake up the phone.</p> -<ul> -<li>The RapidPro system uses Google Cloud Messaging which wakes up the phone whenever a message is sent. Install an APK that makes the channel relay messages as soon as RapidPro emits.</li> -</ul> -</li> -<li> -<p>Alerts for when things go wrong.</p> -<ul> -<li>Send monitoring emails and alert various parties when the Android Channel goes to sleep or becomes unavailable. This can be done from the channel&rsquo;s management page under <code>Alert Email</code></li> -</ul> -</li> -</ol> -<p>Android channels can be <a href="http://web.archive.org/web/20220126134411/https://help.nyaruka.com/en/article/using-a-bulk-sender-sk27hz/">used with a bulk sender</a> to get past the 330 outgoing messages per hour.</p> -<p>Medic recommends that you use shared or dedicated shortcodes for SMS messaging. Dedicated shortcodes are preferred because recipients do not have to include the keyword with each response submitted. Shortcode procurement can be a lengthy process, so make arrangements for the shortcode in advance. It is possible but inconvenient to migrate to a shortcode after deployment.</p> -<p>Medic recommends that SMS costs be zero-rated so that respondents do not incur charges. This motivates them to respond.</p> -<h3 id="flow-design">Flow design</h3> -<p>Tips and best practices are listed below:</p> -<ul> -<li>If possible, have less than 10 questions. Long surveys may either lead to low completion rates or rushed responses that affects the data quality.</li> -<li>Close ended questions are better and easier to respond and handle in RapidPro since respondents only have to send a number or letter such as 1 for “Yes”, 2 for “No”.</li> -<li>Avoid sensitive questions since privacy cannot be guaranteed over SMS and where it is common to share phones.</li> -<li>Include intro and outro messages. Intro messages serve the purpose of giving the survey details such as the background of the survey, the number of questions, data protection, whether there shall be follow up, SMS billing, etc. Outro messages are helpful to notify respondents that they survey is over and commonly include thank you notes.</li> -<li>Include questions that give the respondent an opportunity to opt-in or out of the survey. If they opt out, do not send a follow-up text.</li> -<li>Keep it short, to the size of one SMS (160 characters). Longer messages will be split and may not display well on the recipients’ devices since Mobile Network Operators (MNOs) cannot guarantee that the multi-parts shall be delivered in the desired order. Medic recommends that you retain the same message length as you localize to multiple languages. Truncate appropriately if long contact names included in the message push the length beyond the limit.</li> -</ul> -<h3 id="flow-programming">Flow programming</h3> -<ul> -<li>Use skip logic to ask relevant questions only. For example, number of children only if they have children.</li> -<li>Automate error messages to validate responses. For example, <code>You submitted an invalid response. Reply 1 for “Yes”, 2 for “No”</code>. These can be customized.</li> -<li>Reduce unnecessary messaging by using reminders and follow-up messages selectively on respondents by first differentiating those that completed a flow versus those that did not.</li> -<li>Customize messages as much as possible by pre-populating information already known such as name, to target respondents, especially when phones are shared among clients.</li> -<li>Translate all messages, especially when deploying in a multi-lingual environment. This ensures that respondents fully understand the survey in their language.</li> -<li>Beware of the timing of the surveys that directly affects response rates. From experience, sending questions when respondents are busy with their errands during the day ultimately leads to low response rates as opposed to evenings when they are done for the day.</li> -<li>Make sure you handle unsolicited responses by redirecting such to, for example, a flow that eventually alerts concerned individuals such as reports of an outbreak.</li> -<li>Medic recommends that you use <a href="https://help.nyaruka.com/en/article/adding-timeouts-to-a-flow-1e2oodi/">timeouts</a> or <a href="https://web.archive.org/web/20210927110029/https://blog.textit.com/feature-update-add-timeouts-pauses-to-flows">pauses</a> to send automatic messages after a period of inactivity during a survey. This helps nudge the respondents to complete their flows.</li> -</ul> -<h3 id="configuration-1">Configuration</h3> -<ul> -<li>Persist a unique identifier for each respondent entering the flows. This identifier shall be used to link flow runs to the recipients to make data analysis smooth.</li> -<li>Use consistent language and message design patterns to maintain a consistent experience and conversation. For example, if including a contact name at the beginning of a message, keep it that way for all messages including the localized messages.</li> -<li>Be mindful of when you save data back to the CHT. This should happen at major milestones in a flow, for example, end of a flow or before sending a payload to an API endpoint.</li> -<li>Use <code>globals</code>, shared values that can be referenced inflows, as well as broadcasts and campaigns, within your account referenced by <code>@globals.value_name</code>. This prevents re-entry of values in various components and allows flows to be shared easily in staging and production environments.</li> -<li>Beware of concurrency that is not supported in RapidPro. Concurrency refers to a situation whereby one contact participates in more than one flow at the same time. Whenever this happens, the former flow shall be interrupted in favor of the latter. This can result in respondents exiting flows before completion, which is a confusing user experience and results in poor survey data. A main flow that spins up individual flows may be useful to consider.</li> -</ul> -<h3 id="testing">Testing</h3> -<p>Testing includes manual and scripted.</p> -<p><em>Manual testing</em></p> -<ul> -<li>Telegram is an effective, free, convenient tool for testing that is great for developers and quick testing.</li> -<li>Prior to release, it is crucial that you test the workflow as close to production as possible. Medic recommends that you use the production messaging channels, to especially check messaging fidelity.</li> -<li>Medic recommends that you run a pilot prior to scaling a deployment. Remember, this shall expose the flows to real respondents and be helpful towards uncovering details such as text display among others.</li> -<li>In order to prevent breakages in flows, run full end-to-end tests as edits/changes can have unpredictable impacts. Check that the entire flow is not impacted by the change prior to releasing in production.</li> -</ul> -<p><em>Scripted testing</em></p> -<p>These tests cover the parts that are inaccessible via manual tests. They include units that test individual components and end-to-end tests that test a flow from start to end. As a best practice, the following test pattern using the <a href="http://docs.communityhealthtoolkit.org/cht-conf-test-harness/">cht-conf-test-harness</a> is recommended:</p> -<ul> -<li>Create contact doc via cht-conf-test-harness</li> -<li>PUT docs into localhost API</li> -<li>Trigger RapidPro flow</li> -<li>Wait for a RapidPro flow to complete</li> -<li>Fetch state of contact in RapidPro</li> -<li>Wait for document to be created in API</li> -<li>Inject document into cht-conf-test-harness</li> -<li>Mock passage of time to test task behavior</li> -<li>One e2e test per production scenario</li> -</ul> -<h3 id="deployment">Deployment</h3> -<ul> -<li>Make sure all your flows are in source control. For every change, no matter how small (fixing a typo, etc), at the very least, document and commit the JSON for the flows to Github. This makes flows restorable, auditable, and releasable across environments</li> -<li>Medic recommends that you use an automated deployment process when pushing changes to an instance - either staging or production. A CI/CD reduces manual errors and ensures your production state is tested and reproducible.</li> -<li>Remember to set up the <a href="https://github.com/medic/rapidpro2pg">rapidpro2pg</a> service to get your RapidPro workspace data over to the Postgres database.</li> -</ul> -<h3 id="monitoring">Monitoring</h3> -<ul> -<li>Make sure you are monitoring workflows. Are they finishing as expected? Are some workflows not being used? A useful feature is the “send email” action whenever an unexpected event occurs while the flow is in progress, for example, failure calling an API endpoint. -<ul> -<li>Include relevant parties in the monitoring notifications. For starters, a mailing group that includes all parties will do it without re-entry of each address of relevant recipients.</li> -<li>Include notifications to respondents too, appropriate to the messaging method.</li> -</ul> -</li> -<li>Check that your workspace has enough credits and ensure RapidPro email credit alerts are configured so that credits top ups are done promptly.</li> -</ul> \ No newline at end of file +Building Integrations on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/guides/integrations/Recent content in Building Integrations on Community Health ToolkitHugo -- gohugo.ioenDHIS2 Aggregatehttps://docs.communityhealthtoolkit.org/apps/guides/integrations/dhis2-aggregate/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/integrations/dhis2-aggregate/One of the first things you’ll need to do is identify the specific DHIS2 data set that you plan to implement. You’ll need a list of all the data elements on that data set, a detailed understanding of how each is calculated, the frequency in which the data set is submitted (weekly, monthly, etc…), and for which organisation units the data set applies. You’ll also want to identify and engage the appropriate DHIS2 stakeholders to get access to DHIS2 metadata, test environments, and discuss workflows.OpenMRShttps://docs.communityhealthtoolkit.org/apps/guides/integrations/openmrs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/integrations/openmrs/OpenMRS is an open source electronic medical record system used in dozens of countries. Integrating CHT apps with OpenMRS can open up opportunities to improve health outcomes for patients by promoting better coordination of care. For example, referrals by CHWs in the community can be sent electronically to health facilities using OpenMRS so that nurses and clinicians can prepare for their visit and have full access to the assessment done by a CHW.OppiaMobilehttps://docs.communityhealthtoolkit.org/apps/guides/integrations/oppia/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/integrations/oppia/The training modules configuration consists of five main components: +App Forms - Content that the user will interact with; Tasks - How forms are presented to the user: how and when the user accesses the forms for input; Targets - Shows the progress of the user; Contact Summary - Gives a highlight of the modules completed by the user; Context - Defines what forms are available to fill from the user’s profile, or available as tasks.RapidProhttps://docs.communityhealthtoolkit.org/apps/guides/integrations/rapidpro/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/integrations/rapidpro/RapidPro is the open-source platform that powers TextIt, developed by UNICEF and Nyaruka. RapidPro allows you to visually build messaging workflows for mobile-based services. Review RapidPro’s documentation to familiarize yourself with various components that include the API. Before you embark on designing an integrated RapidPro/CHT workflow, you should start by understanding the needs of your users, identifying a problem to solve, and establishing goals. While an integrated RapidPro/CHT workflow can open up many powerful and personalized messaging capabilities, introducing an additional technology solution does come with complexities and cost. \ No newline at end of file diff --git a/apps/guides/integrations/openmrs/index.html b/apps/guides/integrations/openmrs/index.html index eab60529b5..2b153af5a5 100644 --- a/apps/guides/integrations/openmrs/index.html +++ b/apps/guides/integrations/openmrs/index.html @@ -1,9 +1,9 @@ -OpenMRS | Community Health Toolkit +OpenMRS | Community Health Toolkit

    OpenMRS

    Exchange patient-level data with systems based on the OpenMRS platform

    OpenMRS is an open source electronic medical record system used in dozens of countries. Integrating CHT apps with OpenMRS can open up opportunities to improve health outcomes for patients by promoting better coordination of care. For example, referrals by CHWs in the community can be sent electronically to health facilities using OpenMRS so that nurses and clinicians can prepare for their visit and have full access to the assessment done by a CHW.

    Integrating CHT apps with OpenMRS can be achieved using the OpenMRS REST API and following the guidance in the Custom Integrations documentation.

    Overview

    The CHT Core Framework supports integrations with OpenMRS in a variety of ways:

    1. Sending patient and patient contacts data
    2. Sending reports (encounters and observations) data
    3. Exposing an API for OpenMRS developers to pull data from CHT Core
    4. Receiving data from OpenMRS

    Sending patients, patient contacts, and reports data can be achieved using the Outbound push. Receiving data from OpenMRS can be achieved using the CHT Core Web API.

    Common OpenMRS use cases include:

    1. Linkage to care: Completion of medical visits after diagnosis
    2. Contact tracing: OpenMRS generates a list of contacts to be followed up
    3. Care coordination: Reminding patients to self-report or health-care givers to complete follow ups on patients of interest

    Prerequisites

    As you design your usecases, bear in mind that at the heart of OpenMRS is the Concept Dictionary. Every contact, relationship, encounter or observation metadata exists first, which guides the definition of the forms in the CHT. Therefore, you need to have good understanding of what data maps to what concept.

    Getting started

    The CHT API and OpenMRS API are used for integration. However, the APIs do not do data cleaning and formatting out-of-the-box. Therefore, both systems require custom solutions that ochestrate the functionality to transform exchanged data to be accepted. In the following sections, we focus more on the general procedure for setting up custom modules and services.

    CHT to OpenMRS

    This section focuses on a simple process and the best practices to send data to OpenMRS.

    Mapping forms

    The first thing is to define forms to capture data. Forms can be contact or app, which translate to patient and encounter (for example, observation, lab request, and referral) respectively. Forms are defined using the XLS Form standard. + Create project issue

    OpenMRS

    Exchange patient-level data with systems based on the OpenMRS platform

    OpenMRS is an open source electronic medical record system used in dozens of countries. Integrating CHT apps with OpenMRS can open up opportunities to improve health outcomes for patients by promoting better coordination of care. For example, referrals by CHWs in the community can be sent electronically to health facilities using OpenMRS so that nurses and clinicians can prepare for their visit and have full access to the assessment done by a CHW.

    Integrating CHT apps with OpenMRS can be achieved using the OpenMRS REST API and following the guidance in the Custom Integrations documentation.

    Overview

    The CHT Core Framework supports integrations with OpenMRS in a variety of ways:

    1. Sending patient and patient contacts data
    2. Sending reports (encounters and observations) data
    3. Exposing an API for OpenMRS developers to pull data from CHT Core
    4. Receiving data from OpenMRS

    Sending patients, patient contacts, and reports data can be achieved using the Outbound push. Receiving data from OpenMRS can be achieved using the CHT Core Web API.

    Common OpenMRS use cases include:

    1. Linkage to care: Completion of medical visits after diagnosis
    2. Contact tracing: OpenMRS generates a list of contacts to be followed up
    3. Care coordination: Reminding patients to self-report or health-care givers to complete follow ups on patients of interest

    Prerequisites

    As you design your usecases, bear in mind that at the heart of OpenMRS is the Concept Dictionary. Every contact, relationship, encounter or observation metadata exists first, which guides the definition of the forms in the CHT. Therefore, you need to have good understanding of what data maps to what concept.

    Getting started

    The CHT API and OpenMRS API are used for integration. However, the APIs do not do data cleaning and formatting out-of-the-box. Therefore, both systems require custom solutions that ochestrate the functionality to transform exchanged data to be accepted. In the following sections, we focus more on the general procedure for setting up custom modules and services.

    CHT to OpenMRS

    This section focuses on a simple process and the best practices to send data to OpenMRS.

    Mapping forms

    The first thing is to define forms to capture data. Forms can be contact or app, which translate to patient and encounter (for example, observation, lab request, and referral) respectively. Forms are defined using the XLS Form standard. Some of the best practices here include adopting a convention that results in minimum disruption (or that would require minimal processing) of the concept dictionary.

    1. Defining contact forms

    Here, you need to capture the basic details required for registering a patient or a patient contact in OpenMRS. Below is a sample naming convention for demographic details such as a person’s name (under field name):

    patient_familyName for family_name, patient_firstName for first_name, patient_middleName for middle_name

    Another example of patient identifiers could take the form _IdentifierType_humanReadableName_IdentifierTypeUuid. For example, national Id identifier type definition would be, patient_identifierType_nationalId_49af6cdc-7968-4abb-bf46-de10d7f4859f.

    A sample form definition could be as follows:

    typenamelabelrequiredrelevantappearanceconstraintconstraint_messagecalculationchoice_filterhintdefault
    begin grouppatient_demographicsDemographic details
    stringpatient_familyNameFamily nameyes
    stringpatient_firstNameFirst nameyes
    stringpatient_middleNameMiddle nameyes
    stringpatient_identifierType_nationalId_49af6cdc-7968-4abb-bf46-de10d7f4859fNational IDyes
    end group

    A sample payload would be as follows:

    {
    @@ -349,7 +349,8 @@
     Features >
     Integrations >
     OpenMRS

    Exchange patient-level data with systems based on the OpenMRS platform

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/guides/integrations/oppia/index.html b/apps/guides/integrations/oppia/index.html index 462585d061..94ea6026d0 100644 --- a/apps/guides/integrations/oppia/index.html +++ b/apps/guides/integrations/oppia/index.html @@ -1,9 +1,9 @@ -OppiaMobile | Community Health Toolkit +OppiaMobile | Community Health Toolkit

    OppiaMobile

    Components & configuration

    The training modules configuration consists of five main components:

    • App Forms - Content that the user will interact with;
    • Tasks - How forms are presented to the user: how and when the user accesses the forms for input;
    • Targets - Shows the progress of the user;
    • Contact Summary - Gives a highlight of the modules completed by the user;
    • Context - Defines what forms are available to fill from the user’s profile, or available as tasks.

    App Forms

    The CHT application uses XLSForms (app forms), which are a simplified method of setting up form configurations using Excel (or Libre Office Calc, Google sheets, etc). The forms contain the questions/content that the user will interact with, including web links that enable the users to navigate from the CHT application to a specific course in OppiaMobile. App forms are typically created in the project-folder > forms > app directory of a project. If the content requires a user to access any form of media, then a media folder for the specific form is created and named after the form. For example, to add video content for a form module_one.xlsx, save the video file to the following directory: project-folder > forms > app > module_one-media > video. Once the forms are set up with content, the forms are converted to XForms, which are in xml format. To limit access of App Forms to certain contacts, an App Forms must have a properties file, which defines when and for whom certain forms should be accessed. Once configured, the forms are uploaded to an instance using the CHT configurer with the following commands, which upload specific forms and all forms respectively:

    cht --instance=<instance> convert-app-forms upload-app-forms -- <form1> <form2>
    + Create project issue

    OppiaMobile

    Components & configuration

    The training modules configuration consists of five main components:

    • App Forms - Content that the user will interact with;
    • Tasks - How forms are presented to the user: how and when the user accesses the forms for input;
    • Targets - Shows the progress of the user;
    • Contact Summary - Gives a highlight of the modules completed by the user;
    • Context - Defines what forms are available to fill from the user’s profile, or available as tasks.

    App Forms

    The CHT application uses XLSForms (app forms), which are a simplified method of setting up form configurations using Excel (or Libre Office Calc, Google sheets, etc). The forms contain the questions/content that the user will interact with, including web links that enable the users to navigate from the CHT application to a specific course in OppiaMobile. App forms are typically created in the project-folder > forms > app directory of a project. If the content requires a user to access any form of media, then a media folder for the specific form is created and named after the form. For example, to add video content for a form module_one.xlsx, save the video file to the following directory: project-folder > forms > app > module_one-media > video. Once the forms are set up with content, the forms are converted to XForms, which are in xml format. To limit access of App Forms to certain contacts, an App Forms must have a properties file, which defines when and for whom certain forms should be accessed. Once configured, the forms are uploaded to an instance using the CHT configurer with the following commands, which upload specific forms and all forms respectively:

    cht --instance=<instance> convert-app-forms upload-app-forms -- <form1> <form2>
     

    or

    cht --instance=<instance> convert-app-forms upload-app-forms
     

    The image below shows an example of an XLSForm configured for the Educational Modules:

    The CHT application makes use of weblinks to direct the user to the OppiaMobile application. Each task in the CHT has a weblink configured to point to a specific course in OppiaMobile. The weblinks are configured in the forms as a button, which, when clicked or tapped, redirects the user accordingly, depending on the installation status of the OppiaMobile application and the respective course. The weblinks are configured in each of the XLSForms that are triggered by a selected task as illustrated in the image below:


    [<span style='background-color: #648af7; color:white; padding: 1em; text-decoration: none;border-radius: 8px; '>Open Oppia Mobile</span>] represents the button styling and label.

    https://staging.cha.oppia-mobile.org/view?course=introduction-to-covid-19 represents the weblink, where course=introduction-to-covid-19 specifies the name of the course to be launched in OppiaMobile as configured on Moodle.

    This image shows the outcome of the button configuration:

    Tasks

    Tasks are a set of actions available to users from the task tab. Selecting a task opens up a specific form that completes a workflow. Tasks are available within a given timeframe, after which they expire and the user is unable to view or do them. Tasks are defined as an array of objects in a tasks.js file under the project folder, with each task following the task schema. The required properties of a task include:

    • Name - unique identifier of the task;
    • Title - displays the workflow to be completed for a contact;
    • AppliesTo - determines if the task is emitted per contact or report;
    • ResolvedIf - conditions to mark a task as resolved/completed;
    • Events - specifies the timeframe of a task;
    • Actions - specifies the forms accessed by the user and allows injecting content from previous submissions.

    On completing the task configuration, the following command is used to compile and upload the applications settings (tasks, targets, contact-summary, form properties):

    cht --instance=<instance> compile-app-settings upload-app-settings

    The code snippet below illustrates an example of a task configured for the educational modules:

    {
       title: 'Community Health Academy',
    @@ -414,7 +414,8 @@
     OppiaMobile

    Integrate CHT core with OppiaMobile’s learning management platform

    CHT Applications > Examples > Learning & Care

    An integration built to pilot the integrated workflows focused on CHW remote learning and care support for COVID-19.

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/guides/integrations/rapidpro/index.html b/apps/guides/integrations/rapidpro/index.html index 02be263c0c..3d574c97d7 100644 --- a/apps/guides/integrations/rapidpro/index.html +++ b/apps/guides/integrations/rapidpro/index.html @@ -1,9 +1,9 @@ -RapidPro | Community Health Toolkit +RapidPro | Community Health Toolkit

    RapidPro

    Key concepts, design considerations, how to configure, and best practices

    RapidPro is the open-source platform that powers TextIt, developed by UNICEF and Nyaruka. RapidPro allows you to visually build messaging workflows for mobile-based services. Review RapidPro’s documentation to familiarize yourself with various components that include the API. Before you embark on designing an integrated RapidPro/CHT workflow, you should start by understanding the needs of your users, identifying a problem to solve, and establishing goals. While an integrated RapidPro/CHT workflow can open up many powerful and personalized messaging capabilities, introducing an additional technology solution does come with complexities and cost. A good way to mitigate some of the complexities of setting up and hosting RapidPro yourself is to utilize a SaaS solution such as TextIt. TextIt offers transparent per message pricing and free credits to start off.

    Coming up with a design to accommodate user needs requires a detailed understanding of the capabilities of both systems and conceptually where it makes sense to introduce interactions between them. Below we introduce some of the key concepts in RapidPro, but to learn more you can take one of their online courses, watch some of their videos, and check out their deployment toolkit.

    RapidPro Concepts

    Before designing your integrated RapidPro/CHT workflow, it’s important to understand a couple key functional concepts in RapidPro: Flows, Campaigns, and Triggers.

    Flows

    Flows are a series of steps you link together visually to define the interactions users will have. At any point in the flow, you can trigger actions such as sending an SMS, email, or calling external APIs. Flows are the base for RapidPro’s other features.

    Use Case Example: Send an SMS to a patient asking if they are experiencing any new symptoms today. If so, let the patient know that a CHW will contact them. If not, let the patient know that they will receive another check-in message in three days and to contact their CHW if any new symptoms develop before then.

    Additional Resources: Mastering Flows and How to Build a RapidPro Flow.

    Campaigns

    Campaigns allow you to schedule messages and flows around a date unique to each contact in a group, like a delivery date or registration date. You can add any number of Events to the Campaign.

    Use Case Example: Send a daily check-in message to quarantined patients for 14 days to see if they have developed any symptoms.

    Additional Resources: Creating a Campaign

    Triggers

    Triggers allow you to control how or when a Flow begins. A Trigger can be a keyword received in a text, a point in time, a missed call, or even a follow to a Twitter handle.

    Use Case Example: Start a health assessment Flow whenever a person sends the text assessment to a specific short code.

    Additional Resources: Creating a Keyword Trigger that starts a Flow

    Workflow Design

    Designing an integrated workflow between multiple systems is more of an art than a science. Drafting a sequence diagram (below) is a good first step. When drafting your sequence diagram, it is useful to consider a few key integration touchpoints that are common across many integrated workflows.

    1. One system initiates or triggers an action in the other system
    2. One system needs to get information from the other system
    3. One system wants to store or update data in the other system

    Sequence Diagrams

    A sequence diagram will help you identify the various actors in a given workflow and what the flow of data will look like. Below is an overview and example diagram for a patient initiated triage and feedback workflow.

    1. Patient triggers a RapidPro flow by sending a message to a short code
    2. Using the phone number of the patient, RapidPro requests information from the CHT
    3. The CHT responds with the patient’s name and other details
    4. RapidPro sends personalized messages to conduct triage for the patient
    5. RapidPro determines the outcome
    6. RapidPro sends the outcome to the patient via SMS and posts the results to the CHT
    7. If the patient needs to be followed-up with, the CHT creates a task for the appropriate CHW
    8. Once the CHW completes that task, the CHT initiates the Feedback Flow in RapidPro
    9. RapidPro logic records feedback via SMS from the patient
    10. The results of the feedback flow are saved in the CHT

    Sequence

    Configuration

    The information below focuses on specific interactions between RapidPro and the CHT, it does not cover RapidPro specific configuration, consult RapidPro documentation and resources for that. Also, the examples and code snippets below are using TextIt, the hosted RapidPro service mentioned above.

    Create RapidPro user in CHT

    For RapidPro to communicate with the CHT, you need to create a User in the CHT that will be used by RapidPro when calling the CHT’s APIs. This can be done from the App Management page in the CHT. When adding the user in the CHT, be sure to select the Gateway - Limited access user for Medic Gateway Role.

    Globals

    Globals are shared values that can be referenced in flows, as well as broadcasts and campaigns, within your account referenced by @globals.value_name. They allow you to create a value once and use it repeatedly without having to reenter the value. Likewise, globals make updating a shared value much easier. Rather than manually changing a value everywhere it’s used in your account, simply update the value found in your Globals page.

    Globals

    Once you have configured a Global value, you can easily use it in your flows like this:

    Globals-Usage

    Start RapidPro Flow from CHT

    One of the most common activities you’ll want to do is trigger a Flow in RapidPro based on something that occurred in the CHT. For example… whenever a specific form is submitted in the CHT with some conditional value, start a flow in RapidPro. To do this, you will use the Outbound feature in the CHT, invoking the Flow Starts Endpoint in RapidPro.

    Below is an example outbound config in the CHT called textit-self-quarantine that will trigger a flow in RapidPro whenever a covid_trace_follow_up form is submitted in the CHT where symptom = no. It will also pass an extra date value for self_quarantine_enrollment.

    RapidPro

    Key concepts, design considerations, how to configure, and best practices

    RapidPro is the open-source platform that powers TextIt, developed by UNICEF and Nyaruka. RapidPro allows you to visually build messaging workflows for mobile-based services. Review RapidPro’s documentation to familiarize yourself with various components that include the API. Before you embark on designing an integrated RapidPro/CHT workflow, you should start by understanding the needs of your users, identifying a problem to solve, and establishing goals. While an integrated RapidPro/CHT workflow can open up many powerful and personalized messaging capabilities, introducing an additional technology solution does come with complexities and cost. A good way to mitigate some of the complexities of setting up and hosting RapidPro yourself is to utilize a SaaS solution such as TextIt. TextIt offers transparent per message pricing and free credits to start off.

    Coming up with a design to accommodate user needs requires a detailed understanding of the capabilities of both systems and conceptually where it makes sense to introduce interactions between them. Below we introduce some of the key concepts in RapidPro, but to learn more you can take one of their online courses, watch some of their videos, and check out their deployment toolkit.

    RapidPro Concepts

    Before designing your integrated RapidPro/CHT workflow, it’s important to understand a couple key functional concepts in RapidPro: Flows, Campaigns, and Triggers.

    Flows

    Flows are a series of steps you link together visually to define the interactions users will have. At any point in the flow, you can trigger actions such as sending an SMS, email, or calling external APIs. Flows are the base for RapidPro’s other features.

    Use Case Example: Send an SMS to a patient asking if they are experiencing any new symptoms today. If so, let the patient know that a CHW will contact them. If not, let the patient know that they will receive another check-in message in three days and to contact their CHW if any new symptoms develop before then.

    Additional Resources: Mastering Flows and How to Build a RapidPro Flow.

    Campaigns

    Campaigns allow you to schedule messages and flows around a date unique to each contact in a group, like a delivery date or registration date. You can add any number of Events to the Campaign.

    Use Case Example: Send a daily check-in message to quarantined patients for 14 days to see if they have developed any symptoms.

    Additional Resources: Creating a Campaign

    Triggers

    Triggers allow you to control how or when a Flow begins. A Trigger can be a keyword received in a text, a point in time, a missed call, or even a follow to a Twitter handle.

    Use Case Example: Start a health assessment Flow whenever a person sends the text assessment to a specific short code.

    Additional Resources: Creating a Keyword Trigger that starts a Flow

    Workflow Design

    Designing an integrated workflow between multiple systems is more of an art than a science. Drafting a sequence diagram (below) is a good first step. When drafting your sequence diagram, it is useful to consider a few key integration touchpoints that are common across many integrated workflows.

    1. One system initiates or triggers an action in the other system
    2. One system needs to get information from the other system
    3. One system wants to store or update data in the other system

    Sequence Diagrams

    A sequence diagram will help you identify the various actors in a given workflow and what the flow of data will look like. Below is an overview and example diagram for a patient initiated triage and feedback workflow.

    1. Patient triggers a RapidPro flow by sending a message to a short code
    2. Using the phone number of the patient, RapidPro requests information from the CHT
    3. The CHT responds with the patient’s name and other details
    4. RapidPro sends personalized messages to conduct triage for the patient
    5. RapidPro determines the outcome
    6. RapidPro sends the outcome to the patient via SMS and posts the results to the CHT
    7. If the patient needs to be followed-up with, the CHT creates a task for the appropriate CHW
    8. Once the CHW completes that task, the CHT initiates the Feedback Flow in RapidPro
    9. RapidPro logic records feedback via SMS from the patient
    10. The results of the feedback flow are saved in the CHT

    Sequence

    Configuration

    The information below focuses on specific interactions between RapidPro and the CHT, it does not cover RapidPro specific configuration, consult RapidPro documentation and resources for that. Also, the examples and code snippets below are using TextIt, the hosted RapidPro service mentioned above.

    Create RapidPro user in CHT

    For RapidPro to communicate with the CHT, you need to create a User in the CHT that will be used by RapidPro when calling the CHT’s APIs. This can be done from the App Management page in the CHT. When adding the user in the CHT, be sure to select the Gateway - Limited access user for Medic Gateway Role.

    Globals

    Globals are shared values that can be referenced in flows, as well as broadcasts and campaigns, within your account referenced by @globals.value_name. They allow you to create a value once and use it repeatedly without having to reenter the value. Likewise, globals make updating a shared value much easier. Rather than manually changing a value everywhere it’s used in your account, simply update the value found in your Globals page.

    Globals

    Once you have configured a Global value, you can easily use it in your flows like this:

    Globals-Usage

    Start RapidPro Flow from CHT

    One of the most common activities you’ll want to do is trigger a Flow in RapidPro based on something that occurred in the CHT. For example… whenever a specific form is submitted in the CHT with some conditional value, start a flow in RapidPro. To do this, you will use the Outbound feature in the CHT, invoking the Flow Starts Endpoint in RapidPro.

    Below is an example outbound config in the CHT called textit-self-quarantine that will trigger a flow in RapidPro whenever a covid_trace_follow_up form is submitted in the CHT where symptom = no. It will also pass an extra date value for self_quarantine_enrollment.

    {
       "textit-self-quarantine": {
         "relevant_to": "doc.type === 'data_record' && doc.form === 'covid_trace_follow_up' && doc.fields.trace.symptom === 'no'",
         "destination": {
    @@ -333,7 +333,8 @@
     Features >
     Integrations >
     RapidPro

    Integrate interactive messaging conversations into your workflows

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/messaging/gateways/africas-talking/index.html b/apps/guides/messaging/gateways/africas-talking/index.html index 3385bf63e7..08242e205d 100644 --- a/apps/guides/messaging/gateways/africas-talking/index.html +++ b/apps/guides/messaging/gateways/africas-talking/index.html @@ -1,9 +1,9 @@ -Africa’s Talking SMS Aggregator | Community Health Toolkit +Africa’s Talking SMS Aggregator | Community Health Toolkit

    Africa’s Talking SMS Aggregator

    Integration for sending and receiving SMS

    As of v3.6.0, SMS messages can be sent and received using the Africa’s Talking service.

    Africa’s Talking configuration

    First generate a long unique key to use as the cht-api-key.

    Log on to the Africa’s Talking Dashboard and configure your callback URLs as follows.

    • Delivery Reports: https://<hostname>/api/v1/sms/africastalking/delivery-reports?key=<cht-api-key>
    • Incoming Messages: https://<hostname>/api/v1/sms/africastalking/incoming-messages?key=<cht-api-key>

    Then generate an “API Key” (we’ll refer to this as the at-api-key) and save this in your CHT Core configuration covered below.

    CHT Core configuration

    API keys

    The Africa’s Talking integration uses the CHT Credentials service to retrieve the API keys using the IDs africastalking.com:incoming and africastalking.com:outgoing. Use the CHT credentials API to securely store the credentials.

    App settings

    Update your app settings as follows.

    Africa’s Talking SMS Aggregator

    Integration for sending and receiving SMS

    As of v3.6.0, SMS messages can be sent and received using the Africa’s Talking service.

    Africa’s Talking configuration

    First generate a long unique key to use as the cht-api-key.

    Log on to the Africa’s Talking Dashboard and configure your callback URLs as follows.

    • Delivery Reports: https://<hostname>/api/v1/sms/africastalking/delivery-reports?key=<cht-api-key>
    • Incoming Messages: https://<hostname>/api/v1/sms/africastalking/incoming-messages?key=<cht-api-key>

    Then generate an “API Key” (we’ll refer to this as the at-api-key) and save this in your CHT Core configuration covered below.

    CHT Core configuration

    API keys

    The Africa’s Talking integration uses the CHT Credentials service to retrieve the API keys using the IDs africastalking.com:incoming and africastalking.com:outgoing. Use the CHT credentials API to securely store the credentials.

    App settings

    Update your app settings as follows.

    {
       "sms": {
         "outgoing_service": "africas-talking",
         "reply_to": "<africa's talking shortcode>",
    @@ -325,7 +325,8 @@
     Quick Guides >
     Messaging >
     Contact IDs

    Using unique short codes to identify places and people via messaging

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/messaging/gateways/gateway/configuration/index.html b/apps/guides/messaging/gateways/gateway/configuration/index.html index b9b5d21651..3f38530c62 100644 --- a/apps/guides/messaging/gateways/gateway/configuration/index.html +++ b/apps/guides/messaging/gateways/gateway/configuration/index.html @@ -1,9 +1,9 @@ -Configuration | Community Health Toolkit +Configuration | Community Health Toolkit

    Configuration

    Configuring cht-gateway

    CHT gateway supports Android 4.1 and above. To have it up and fully working, follow the 3 steps below.

    1. Install the latest APK from the releases page in the cht-gateway repo. This APK is not in the Play Store, you will need to side-load it as is done with CHT Android.

    2. Open the app. + Create project issue

    Configuration

    Configuring cht-gateway

    CHT gateway supports Android 4.1 and above. To have it up and fully working, follow the 3 steps below.

    1. Install the latest APK from the releases page in the cht-gateway repo. This APK is not in the Play Store, you will need to side-load it as is done with CHT Android.

    2. Open the app. if you are installing the app for the first time or afresh, you will get a Warning:medic-gateway is not set as the default messaging app on this device . Select HELP ME CHANGE and agree to the follow-up system prompt about changing the default messaging app.

    3. Configure the app. If you’re configuring cht-gateway(v1.0.0 and above) for use with hosted medic, with a URL of e.g. https://myproject.dev.medicmobile.org and a username of gateway and a password of topSecret, fill in the settings as follows:

    Instance name: myproject [dev] (if https://myproject.app..., select ‘app’)
    Username: gateway(Since v1.2.1 medic gateway versions this field is not present in the app. The user gateway is assumed.)
    Password: topSecret(This should be the password for the gateway username as set up in the project web instance)

    configuration

    Power Saving

    Care should be taken to disable all power-saving modes on the phone, as these may affect cht-gateway’s ability to check in with the server regularly.

    On different versions of Android, power saving options may be found in different places. Sometimes they will be per-app, and sometimes phone-wide.

    Some places you might find the power savings settings:

    • WiFi > MORE > Keep WiFi on during sleep > ALWAYS (increases battery usage)
    • Smart Manager > Battery > App Power Saving > OFF, or
    • Smart Manager > Battery > App Power Saving > Detail > CHT Gateway > Disable
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/guides/messaging/gateways/gateway/index.html b/apps/guides/messaging/gateways/gateway/index.html index cb31bd8cc8..2c3682687b 100644 --- a/apps/guides/messaging/gateways/gateway/index.html +++ b/apps/guides/messaging/gateways/gateway/index.html @@ -1,9 +1,9 @@ -CHT Gateway | Community Health Toolkit +CHT Gateway | Community Health Toolkit

    CHT Gateway

    Setting up and maintaining CHT Gateway

    Configuration

    Configuring cht-gateway

    Phones

    List of Phones that work well with CHT Gateway

    Troubleshooting

    Guide to troubleshooting Gateway problems

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/messaging/gateways/gateway/index.xml b/apps/guides/messaging/gateways/gateway/index.xml index 81c6ef2780..ede3f753a7 100644 --- a/apps/guides/messaging/gateways/gateway/index.xml +++ b/apps/guides/messaging/gateways/gateway/index.xml @@ -1,165 +1,6 @@ -Community Health Toolkit – CHT Gatewayhttps://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/Recent content in CHT Gateway on Community Health ToolkitHugo -- gohugo.ioenApps: Configurationhttps://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/configuration/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/configuration/ -<p>CHT gateway supports Android 4.1 and above. To have it up and fully working, follow the 3 steps below.</p> -<ol> -<li> -<p>Install the latest APK from the <a href="https://github.com/medic/cht-gateway/releases">releases page</a> in the <code>cht-gateway</code> repo. This APK is not in the Play Store, you will need to side-load it <a href="https://docs.communityhealthtoolkit.org/apps/guides/android/publishing/#side-loading">as is done with CHT Android</a>.</p> -</li> -<li> -<p>Open the app. -if you are installing the app for the first time or afresh, you will get a <code>Warning:medic-gateway is not set as the default messaging app on this device</code> . Select <code>HELP ME CHANGE</code> and agree to the follow-up system prompt about changing the default messaging app.</p> -</li> -<li> -<p>Configure the app. If you&rsquo;re configuring cht-gateway(v1.0.0 and above) for use with hosted medic, with a URL of e.g. <code>https://myproject.dev.medicmobile.org</code> and a username of <code>gateway</code> and a password of <code>topSecret</code>, fill in the settings as follows:</p> -</li> -</ol> -<p>Instance name: <code>myproject [dev]</code> (if <code>https://myproject.app...</code>, select &lsquo;app&rsquo;)<br> -Username: <code>gateway</code>(<em>Since</em> <a href="https://github.com/medic/cht-gateway/releases/tag/v1.2.1">v1.2.1</a> <em>medic gateway versions this field is not present in the app. The user <code>gateway</code> is assumed</em>.)<br> -Password: <code>topSecret</code>(<em>This should be the password for the <code>gateway</code> username as set up in the project web instance</em>)</p> -<p><img src="gateway-config.png" alt="configuration"></p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -If you&rsquo;re configuring cht-gateway (v0.6.2 and below - recommended if you have a non-<em>Medic</em> hosted instance) you will need to use the generic build of cht-gateway - links to download are <a href="https://github.com/medic/cht-gateway/releases">here</a>. Find out the value for webapp URL from your tech support then configure as below -<strong>WebappUrl</strong>: <code>https://gateway:topSecret@myproject.some-subdomain.mydomain.org</code> -</div> -<h1 id="power-saving">Power Saving</h1> -<p>Care should be taken to disable all power-saving modes on the phone, as these may affect <code>cht-gateway</code>&rsquo;s ability to check in with the server regularly.</p> -<p>On different versions of Android, power saving options may be found in different places. Sometimes they will be per-app, and sometimes phone-wide.</p> -<p>Some places you might find the power savings settings:</p> -<ul> -<li><code>WiFi &gt; MORE &gt; Keep WiFi on during sleep &gt; ALWAYS (increases battery usage)</code></li> -<li><code>Smart Manager &gt; Battery &gt; App Power Saving &gt; OFF</code>, or</li> -<li><code>Smart Manager &gt; Battery &gt; App Power Saving &gt; Detail &gt; CHT Gateway &gt; Disable</code></li> -</ul>Apps: Phoneshttps://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/phones/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/phones/ -<p>In order to use SMS workflows with the CHT you will need an SMS gateway. For reliability an SMS Aggregator, such as <a href="https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/africas-talking/">Africa&rsquo;s Talking</a> or <a href="https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/rapidpro/">RapidPro</a>, is recommended. When an SMS Aggregator is not available, an Android device running <a href="https://github.com/medic/cht-gateway">CHT Gateway</a> can be used to send and receive SMS in your CHT application. You may use an existing Android device and are not required to purchase a new one. However, for more reliable sending and receiving of SMS, the Android device should be in your organization’s office or facility with a consistent internet connection.</p> -<p>Below is a list of recommended Android devices across a range of prices. You may find these in local mobile equipment stores as well as online through Amazon and other e-commerce sites.</p> -<h2 id="low-cost-devices">Low-cost devices:</h2> -<ul> -<li>LG L60 *</li> -<li>Samsung Duos *</li> -<li>Lenovo A1000</li> -</ul> -<h2 id="medium-cost-devices">Medium-cost devices:</h2> -<ul> -<li>Samsung Galaxy J5 *</li> -<li>Huawei Honor 4x *</li> -<li>Huawei G Play mini *</li> -<li>Motorola Moto X play</li> -</ul> -<h2 id="high-cost-devices">High-cost devices:</h2> -<ul> -<li>OnePlus 3T</li> -<li>Nexus 6</li> -</ul> -<p>*Devices marked with an asterisk have been tested and used by Medic and our partners.</p> -<p>Android OS: We also support Android version 4.1 (Jelly Beans) and above. Details of the Android history can be found <a href="https://en.wikipedia.org/wiki/Android_version_history">here</a>.</p> -<p>We highly recommend investing in devices that are medium to high-cost, and in models that are marked with an asterisk in the list above to ensure optimal efficiency.</p>Apps: Troubleshootinghttps://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/troubleshooting/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/troubleshooting/ -<p>In a techlead heaven, we would have immediate physical access to gateway phones, but alas, most of the time we have to hand them over to the partner. Fortunately, comes this guide on debugging gateway problems.</p> -<p>Follow the steps as below (if you don&rsquo;t have physical access to the phone, start with step 2 i.e debug from the server side first)</p> -<ol> -<li> -<p>Make sure that the device:</p> -<ol> -<li> -<p>Is connected to the internet. You can check this by opening any Browser app in the phone and going to <code>https://google.com</code>.</p> -</li> -<li> -<p>Check if the instance name, instance type and <code>gateway</code> user password are set correctly in the Gateway <code>Settings</code> app screen.</p> -<p><img src="settings_screen.png" alt="Settings Screen"></p> -</li> -<li> -<p>Has the latest Gateway version. Get the latest version by opening the Google Play Store, searching for <code>Medic Gateway</code> and updating the existing app</p> -</li> -<li> -<p>Medic Gateway should be set as the default app for SMS. To check, go to <code>Settings</code> in the Gateway app. If Medic Gateway is not the default, you will be met with the below app screen. In that case, click <code>Help me change</code>.</p> -<p><img src="not_default.png" alt="Not Default"></p> -</li> -<li> -<p>If messages are not going through to the server, in the <code>To Webapp</code> tab, select some of them and press <code>Retry</code> at the bottom and wait for about 5-10 minutes.</p> -</li> -<li> -<p>If messages are not going through to the users, in the <code>From Webapp</code> tab, select some of them and press <code>Retry</code> at the bottom and wait for about 5-10 minutes.</p> -</li> -<li> -<p>In CDMA networks, there are operator issues where messages get to the users in chunks of around 160 characters when the message is too long. If this is the case, go to <code>Settings</code> in the gateway screen and make sure you tick <code>CDMA compatibility mode</code></p> -</li> -<li> -<p>Check if the phone has adequate free space</p> -</li> -<li> -<p>Restart the phone if no solution seems to work. Resetting the Android state sometimes resolves some persistent problems</p> -</li> -</ol> -</li> -<li> -<p>When attacking the problem from the phones end does not work, move on to the server and try to solve it from there. Maybe the server has a configuration issue which hinders the processing or the acceptance of the messages by <code>Medic-Api</code> -Follow the steps:</p> -<ol> -<li>Navigate to <code>/srv/storage/gardener/logs/ </code></li> -<li>Perform <code>ls -lt</code> to arrange the log files in order of modified date. Note the concerned file which was last modified on the date you are investigating</li> -<li>Pipe it to grep to get only SMS logs e.g <code>grep api/sms medic_medic_medic-api4.log</code>. As below, note the errors and work from there.</li> -</ol> -</li> -</ol> -<pre tabindex="0"><code>cd /srv/storage/gardener/logs/ -ls -lt -grep api/sms medic_medic_medic-api4.log -</code></pre><p>If the above steps don&rsquo;t yield the problem, read the <a href="https://docs.communityhealthtoolkit.org/apps/guides/debugging/obtaining-logs/#android-logs">Obtaining Logs</a> page, note the make/model/android version their gateway handset is and escalate with those details to the PM/project techlead.</p> -<p><strong>Verifying the SMS Gateway via a Test Message</strong></p> -<p>We can verify whether the SMS Gateway app is responding by sending a test message via the web app. For example, let us try this in a deployed project.</p> -<ol> -<li>Log in via the web app with a user with administrative rights.</li> -</ol> -<p><img src="log-in-page.png" alt="Log in page"></p> -<ol start="2"> -<li>Go to App Management.</li> -</ol> -<p><img src="app-management-menu-item.png" alt="App Management menu item"></p> -<ol start="3"> -<li>Click on SMS option, then click the Test Message tab.</li> -</ol> -<p><img src="sms-menu-item.png" alt="SMS menu item"></p> -<ol start="4"> -<li> -<p>Test message creation and delivery to Gateway phone verification:</p> -<ol> -<li> -<p>Input the test message which will be sent to the Gateway and the phone number where we wish to see the Gateway&rsquo;s reply. Country code if not specified will be the default country code specified in SMS -&gt; Default country code. -<img src="default-country-code.png" alt="Default country code setting"></p> -</li> -<li> -<p>The test message text doesn&rsquo;t matter here as we are just verifying if the Gateway phone receives messages from our server and responds correctly to it by sending us a message back in the phone number we input in step (i). The easiest way would be to input our own phone number in (i) and validate that the Gateway phone sends the response message to it.</p> -</li> -<li> -<p>After inputting the number where you expect to view the response message, press the &ldquo;Send message&rdquo; button.</p> -</li> -<li> -<p>We should see the Report Submitted message if the SMS was successfully received by the Gateway phone. -<img src="report-submitted.png" alt="Report submitted"></p> -</li> -</ol> -</li> -<li> -<p>If the message was received by the Gateway we should see the message in the &ldquo;Messages&rdquo; tab in the app and get an auto-reply on the number we provided in step 4(i). We should be able to see the following acknowledgments in the web app and phone respectively.</p> -<ol> -<li> -<p>Message Acknowledgement in the web app in the messages tab. -<img src="messages-tab.png" alt="Message shown in Messages Tab"></p> -</li> -<li> -<p>Response sent acknowledgment in the Outgoing Messages -&gt; Due tab. The recipient should be the number we input in 4(i) and the status should be delivered. -<img src="outgoing-messages.png" alt="Outgoing Messages in App Management"></p> -</li> -<li> -<p>Message reply as seen on the phone of 4(i) from Gateway phone. -<img src="sms-reply.png" alt="SMS reply shown on device"></p> -</li> -</ol> -</li> -<li> -<p>If you see the message acknowledgment it means the Gateway is working as expected.</p> -</li> -</ol> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Insist on screenshots even for the most trivial things that partners insist they have performed as you asked. They are also good for giving you a mental image of what is happening on the phone remotely. -</div> \ No newline at end of file +CHT Gateway on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/Recent content in CHT Gateway on Community Health ToolkitHugo -- gohugo.ioenConfigurationhttps://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/configuration/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/configuration/CHT gateway supports Android 4.1 and above. To have it up and fully working, follow the 3 steps below. +Install the latest APK from the releases page in the cht-gateway repo. This APK is not in the Play Store, you will need to side-load it as is done with CHT Android. +Open the app. if you are installing the app for the first time or afresh, you will get a Warning:medic-gateway is not set as the default messaging app on this device .Phoneshttps://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/phones/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/phones/In order to use SMS workflows with the CHT you will need an SMS gateway. For reliability an SMS Aggregator, such as Africa&rsquo;s Talking or RapidPro, is recommended. When an SMS Aggregator is not available, an Android device running CHT Gateway can be used to send and receive SMS in your CHT application. You may use an existing Android device and are not required to purchase a new one. However, for more reliable sending and receiving of SMS, the Android device should be in your organization’s office or facility with a consistent internet connection.Troubleshootinghttps://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/troubleshooting/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/troubleshooting/In a techlead heaven, we would have immediate physical access to gateway phones, but alas, most of the time we have to hand them over to the partner. Fortunately, comes this guide on debugging gateway problems. +Follow the steps as below (if you don&rsquo;t have physical access to the phone, start with step 2 i.e debug from the server side first) +Make sure that the device: +Is connected to the internet. \ No newline at end of file diff --git a/apps/guides/messaging/gateways/gateway/phones/index.html b/apps/guides/messaging/gateways/gateway/phones/index.html index 798bd875d7..017e0e73a6 100644 --- a/apps/guides/messaging/gateways/gateway/phones/index.html +++ b/apps/guides/messaging/gateways/gateway/phones/index.html @@ -1,9 +1,9 @@ -Phones | Community Health Toolkit +Phones | Community Health Toolkit

    Phones

    List of Phones that work well with CHT Gateway

    In order to use SMS workflows with the CHT you will need an SMS gateway. For reliability an SMS Aggregator, such as Africa’s Talking or RapidPro, is recommended. When an SMS Aggregator is not available, an Android device running CHT Gateway can be used to send and receive SMS in your CHT application. You may use an existing Android device and are not required to purchase a new one. However, for more reliable sending and receiving of SMS, the Android device should be in your organization’s office or facility with a consistent internet connection.

    Below is a list of recommended Android devices across a range of prices. You may find these in local mobile equipment stores as well as online through Amazon and other e-commerce sites.

    Low-cost devices:

    • LG L60 *
    • Samsung Duos *
    • Lenovo A1000

    Medium-cost devices:

    • Samsung Galaxy J5 *
    • Huawei Honor 4x *
    • Huawei G Play mini *
    • Motorola Moto X play

    High-cost devices:

    • OnePlus 3T
    • Nexus 6

    *Devices marked with an asterisk have been tested and used by Medic and our partners.

    Android OS: We also support Android version 4.1 (Jelly Beans) and above. Details of the Android history can be found here.

    We highly recommend investing in devices that are medium to high-cost, and in models that are marked with an asterisk in the list above to ensure optimal efficiency.


    CHT Applications > + Create project issue

    Phones

    List of Phones that work well with CHT Gateway

    In order to use SMS workflows with the CHT you will need an SMS gateway. For reliability an SMS Aggregator, such as Africa’s Talking or RapidPro, is recommended. When an SMS Aggregator is not available, an Android device running CHT Gateway can be used to send and receive SMS in your CHT application. You may use an existing Android device and are not required to purchase a new one. However, for more reliable sending and receiving of SMS, the Android device should be in your organization’s office or facility with a consistent internet connection.

    Below is a list of recommended Android devices across a range of prices. You may find these in local mobile equipment stores as well as online through Amazon and other e-commerce sites.

    Low-cost devices:

    • LG L60 *
    • Samsung Duos *
    • Lenovo A1000

    Medium-cost devices:

    • Samsung Galaxy J5 *
    • Huawei Honor 4x *
    • Huawei G Play mini *
    • Motorola Moto X play

    High-cost devices:

    • OnePlus 3T
    • Nexus 6

    *Devices marked with an asterisk have been tested and used by Medic and our partners.

    Android OS: We also support Android version 4.1 (Jelly Beans) and above. Details of the Android history can be found here.

    We highly recommend investing in devices that are medium to high-cost, and in models that are marked with an asterisk in the list above to ensure optimal efficiency.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/messaging/gateways/gateway/troubleshooting/index.html b/apps/guides/messaging/gateways/gateway/troubleshooting/index.html index b43659fcdb..5ef74a763f 100644 --- a/apps/guides/messaging/gateways/gateway/troubleshooting/index.html +++ b/apps/guides/messaging/gateways/gateway/troubleshooting/index.html @@ -1,9 +1,9 @@ -Troubleshooting | Community Health Toolkit +Troubleshooting | Community Health Toolkit

    Troubleshooting

    Guide to troubleshooting Gateway problems

    In a techlead heaven, we would have immediate physical access to gateway phones, but alas, most of the time we have to hand them over to the partner. Fortunately, comes this guide on debugging gateway problems.

    Follow the steps as below (if you don’t have physical access to the phone, start with step 2 i.e debug from the server side first)

    1. Make sure that the device:

      1. Is connected to the internet. You can check this by opening any Browser app in the phone and going to https://google.com.

      2. Check if the instance name, instance type and gateway user password are set correctly in the Gateway Settings app screen.

        Settings Screen

      3. Has the latest Gateway version. Get the latest version by opening the Google Play Store, searching for Medic Gateway and updating the existing app

      4. Medic Gateway should be set as the default app for SMS. To check, go to Settings in the Gateway app. If Medic Gateway is not the default, you will be met with the below app screen. In that case, click Help me change.

        Not Default

      5. If messages are not going through to the server, in the To Webapp tab, select some of them and press Retry at the bottom and wait for about 5-10 minutes.

      6. If messages are not going through to the users, in the From Webapp tab, select some of them and press Retry at the bottom and wait for about 5-10 minutes.

      7. In CDMA networks, there are operator issues where messages get to the users in chunks of around 160 characters when the message is too long. If this is the case, go to Settings in the gateway screen and make sure you tick CDMA compatibility mode

      8. Check if the phone has adequate free space

      9. Restart the phone if no solution seems to work. Resetting the Android state sometimes resolves some persistent problems

    2. When attacking the problem from the phones end does not work, move on to the server and try to solve it from there. Maybe the server has a configuration issue which hinders the processing or the acceptance of the messages by Medic-Api + Create project issue

    Troubleshooting

    Guide to troubleshooting Gateway problems

    In a techlead heaven, we would have immediate physical access to gateway phones, but alas, most of the time we have to hand them over to the partner. Fortunately, comes this guide on debugging gateway problems.

    Follow the steps as below (if you don’t have physical access to the phone, start with step 2 i.e debug from the server side first)

    1. Make sure that the device:

      1. Is connected to the internet. You can check this by opening any Browser app in the phone and going to https://google.com.

      2. Check if the instance name, instance type and gateway user password are set correctly in the Gateway Settings app screen.

        Settings Screen

      3. Has the latest Gateway version. Get the latest version by opening the Google Play Store, searching for Medic Gateway and updating the existing app

      4. Medic Gateway should be set as the default app for SMS. To check, go to Settings in the Gateway app. If Medic Gateway is not the default, you will be met with the below app screen. In that case, click Help me change.

        Not Default

      5. If messages are not going through to the server, in the To Webapp tab, select some of them and press Retry at the bottom and wait for about 5-10 minutes.

      6. If messages are not going through to the users, in the From Webapp tab, select some of them and press Retry at the bottom and wait for about 5-10 minutes.

      7. In CDMA networks, there are operator issues where messages get to the users in chunks of around 160 characters when the message is too long. If this is the case, go to Settings in the gateway screen and make sure you tick CDMA compatibility mode

      8. Check if the phone has adequate free space

      9. Restart the phone if no solution seems to work. Resetting the Android state sometimes resolves some persistent problems

    2. When attacking the problem from the phones end does not work, move on to the server and try to solve it from there. Maybe the server has a configuration issue which hinders the processing or the acceptance of the messages by Medic-Api Follow the steps:

      1. Navigate to /srv/storage/gardener/logs/
      2. Perform ls -lt to arrange the log files in order of modified date. Note the concerned file which was last modified on the date you are investigating
      3. Pipe it to grep to get only SMS logs e.g grep api/sms medic_medic_medic-api4.log. As below, note the errors and work from there.
    cd /srv/storage/gardener/logs/
     ls -lt
     grep api/sms medic_medic_medic-api4.log
    @@ -320,7 +320,8 @@
     Gateways >
     CHT Gateway >
     Configuration

    Configuring cht-gateway

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/guides/messaging/gateways/index.html b/apps/guides/messaging/gateways/index.html index 358afb86ca..2f9de6ece9 100644 --- a/apps/guides/messaging/gateways/index.html +++ b/apps/guides/messaging/gateways/index.html @@ -1,9 +1,9 @@ -Messaging Gateways | Community Health Toolkit +Messaging Gateways | Community Health Toolkit

    Messaging Gateways

    Guides for setting up and maintaining messaging gateways

    CHT Gateway

    Setting up and maintaining CHT Gateway

    Africa’s Talking SMS Aggregator

    Integration for sending and receiving SMS

    RapidPro Messaging Gateway

    Integration for sending and receiving messages

    Installing RapidPro - CHT Gateway Android APK

    Integration for sending and receiving messages

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/messaging/gateways/index.xml b/apps/guides/messaging/gateways/index.xml index 9575e14f99..c76bb009eb 100644 --- a/apps/guides/messaging/gateways/index.xml +++ b/apps/guides/messaging/gateways/index.xml @@ -1,111 +1,9 @@ -Community Health Toolkit – Messaging Gatewayshttps://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/Recent content in Messaging Gateways on Community Health ToolkitHugo -- gohugo.ioenApps: CHT Gatewayhttps://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/Apps: Africa’s Talking SMS Aggregatorhttps://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/africas-talking/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/africas-talking/ -<p>As of v3.6.0, SMS messages can be sent and received using the <a href="https://africastalking.com">Africa&rsquo;s Talking</a> service.</p> -<h2 id="africas-talking-configuration">Africa&rsquo;s Talking configuration</h2> -<p>First generate a long unique key to use as the <code>cht-api-key</code>.</p> -<p>Log on to the <a href="https://account.africastalking.com">Africa&rsquo;s Talking Dashboard</a> and configure your callback URLs as follows.</p> -<ul> -<li>Delivery Reports: <code>https://&lt;hostname&gt;/api/v1/sms/africastalking/delivery-reports?key=&lt;cht-api-key&gt;</code></li> -<li>Incoming Messages: <code>https://&lt;hostname&gt;/api/v1/sms/africastalking/incoming-messages?key=&lt;cht-api-key&gt;</code></li> -</ul> -<p>Then generate an &ldquo;API Key&rdquo; (we&rsquo;ll refer to this as the <code>at-api-key</code>) and save this in your CHT Core configuration covered below.</p> -<h2 id="cht-core-configuration">CHT Core configuration</h2> -<h3 id="api-keys">API keys</h3> -<p>The Africa&rsquo;s Talking integration uses the CHT Credentials service to retrieve the API keys using the IDs <code>africastalking.com:incoming</code> and <code>africastalking.com:outgoing</code>. Use the <a href="https://docs.communityhealthtoolkit.org/apps/reference/api#put-apiv1credentials">CHT credentials API</a> to securely store the credentials.</p> -<h3 id="app-settings">App settings</h3> -<p>Update your app settings as follows.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;sms&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;outgoing_service&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;africas-talking&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;reply_to&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&lt;africa&#39;s talking shortcode&gt;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;africas_talking&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;username&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&lt;africa&#39;s talking username&gt;&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h2 id="testing">Testing</h2> -<p>To test your integration, set your &ldquo;username&rdquo; to &ldquo;sandbox&rdquo;, log in to <a href="https://account.africastalking.com">Africa&rsquo;s Talking</a>, and go to the Sandbox app.</p>Apps: RapidPro Messaging Gatewayhttps://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/rapidpro/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/rapidpro/ -<p>As of v3.11.0, messages can be sent and received using <a href="https://docs.communityhealthtoolkit.org/apps/features/integrations/rapidpro/">RapidPro</a> as a messaging gateway.</p> -<h2 id="rapidpro-configuration">RapidPro configuration</h2> -<h3 id="store-globals">Store globals</h3> -<p>Generate a long unique key to use as the <code>cht_api_key</code>.</p> -<p>Log in to your RapidPro dashboard, go to the globals page (<code>/global/</code>) and create two globals with the following data:</p> -<ul> -<li>name: <code>cht_url</code>, value: <code>https://&lt;your-cht-instance-host&gt;/api/v2/sms/rapidpro/incoming-messages</code>. For security the instance host <strong>must not</strong> include basic authentication. (NB: This endpoint was added in CHT 4.1.0. If integrating with an earlier version you will need to use the earlier version with a typo in the URL: <code>https://&lt;your-cht-instance-host&gt;/api/v1/sms/radpidpro/incoming-messages</code>)</li> -<li>name: <code>cht_api_key</code>, value: <code>&lt;cht_api_key&gt;</code></li> -</ul> -<p>The names of these two global variables are arbitrary, but in this document we will keep referring to the names defined above.</p> -<p>Then visit the RapidPro workspace settings page (<code>/org/home/</code>) and check your RapidPro API token (we&rsquo;ll refer to this as the <code>rapidpro_api_key</code>).</p> -<h3 id="create-a-new-flow">Create a new flow</h3> -<p>In your RapidPro dashboard, visit the flows page (<code>/flow/</code>) and create a new flow. It only needs to contain a webhook, to relay the message to your CHT Core instance and handle possible errors.</p> -<p><img src="flow_overview.png" alt="flow_overview"></p> -<p>Configure the new webhook:</p> -<ul> -<li>to <code>POST</code> to the <code>cht_url</code> you configured earlier:</li> -</ul> -<p><img src="flow_webhook_host.png" alt="flow_webhook_host"></p> -<ul> -<li>set the authorization and content-type headers</li> -</ul> -<p><img src="flow_webhook_headers.png" alt="flow_webhook_headers"></p> -<ul> -<li>set the body to relay the message to CHT in the expected format:</li> -</ul> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#a40000">@(json(object(</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;id&#34;</span><span style="color:#a40000">,</span> <span style="color:#a40000">run.uuid,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;from&#34;</span><span style="color:#a40000">,</span> <span style="color:#a40000">replace(urns.tel,</span><span style="color:#4e9a06">&#34;tel:+&#34;</span><span style="color:#a40000">,</span> <span style="color:#4e9a06">&#34;+&#34;</span><span style="color:#a40000">),</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;content&#34;</span><span style="color:#a40000">,</span> <span style="color:#a40000">input.text</span> -</span></span><span style="display:flex;"><span><span style="color:#a40000">)))</span> -</span></span></code></pre></div><p><img src="flow_webhook_body.png" alt="flow_webhook_body"></p> -<h3 id="create-a-new-trigger">Create a new trigger</h3> -<p>Create a trigger (<code>/trigger/</code>) to start the new flow when a message is not handled elsewhere.</p> -<p><img src="trigger_select.png" alt="trigger_select"></p> -<p>For more details about RapidPro configuration, please consult the <a href="https://docs.communityhealthtoolkit.org/apps/guides/integrations/rapidpro/">RapidPro integration documentation</a>.</p> -<h2 id="cht-core-configuration">CHT Core configuration</h2> -<h3 id="api-keys">API keys</h3> -<p>The RapidPro integration uses the CHT Credentials service to retrieve the API keys using the IDs <code>rapidpro:incoming</code> and <code>rapidpro:outgoing</code>. Use the <a href="https://docs.communityhealthtoolkit.org/apps/reference/api#put-apiv1credentials">CHT credentials API</a> to securely store the credentials.<br> -<code>rapidpro:incoming</code> should contain the value of the long unique key generated earlier <code>&lt;cht_api_key&gt;</code> to verify incoming requests from RapidPro.<br> -<code>rapidpro:outgoing</code> should contain your RapidPro API token <code>&lt;rapidpro_api_key&gt;</code> to authenticate requests made against RapidPro&rsquo;s API.</p> -<h3 id="app-settings">App settings</h3> -<p>Update your app settings as follows.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;sms&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;outgoing_service&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;rapidpro&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rapidpro&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;url&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&lt;RapidPro instance url&gt;&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Rate limiting</h4> -<p>The RapidPro API endpoints are rate-limited, meaning that requests to send or check the status of messages beyond a certain number per hour will be blocked. The limit is currently 2500 actions per hour, and may change without notice. Check out the <a href="https://rapidpro.io/api/v2/#rate-limiting">RapidPro API reference</a> for more details. If sending a message or retrieving a status update fails, it will be retried automatically again later.</p> -<p>When the outgoing message service is set to RapidPro or the host/account are changed, CHT Core will request state updates for all messages that aren&rsquo;t in one of the final states: <code>delivered</code>, <code>failed</code>, <code>denied</code>, or <code>cleared</code>. Each of these request counts towards your quota. Since the messages are unlikely to exist on the new RapidPro service, these requests will fail on every retry and consume your request quota. It is therefore important that all outgoing messages have a final state before switching RapidPro accounts or hosts. The status can be set to <code>failed</code> for messages that should not be resent without user intervention, or to <code>scheduled</code> for those that should be automatically sent with the new RapidPro account.</p> -</div>Apps: Installing RapidPro - CHT Gateway Android APKhttps://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/rapidpro_cht_gateway/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/rapidpro_cht_gateway/ -<p>RapidPro - CHT uses your Android phone to send and receive messages on your behalf.</p> -<p>Due to restrictions that Google has placed on Android applications that send SMS messages, RapidPro - CHT applications can no longer be distributed through the Google Play Store. You will need to download the application file and install it using the steps below:</p> -<ol> -<li> -<p><strong>Download App</strong> - On your Android device, open your browser and download the app by entering the URL: <code><a href="https://rapidpro.app.medicmobile.org/android/">https://rapidpro.app.medicmobile.org/android/</a></code></p> -</li> -<li> -<p><strong>Launch the RapidPro - CHT installer</strong> - Open the notifications shade by dragging from the top and select the downloaded file. -<img src="apk_download.png" alt="download" title="image_tooltip"></p> -</li> -<li> -<p><strong>Tap Settings</strong> - You will be taken to a screen with &ldquo;allow from this source&rdquo; toggle -<img src="apk_download_settings.png" alt="settings" title="image_tooltip"></p> -</li> -<li> -<p><strong>Toggle allow from this source</strong> - Make sure &ldquo;allow from this source&rdquo; toggle is in the On position. The go back and click install</p> -<p><img src="apk_download_allow_install.png" alt="Toggle allow from this source" title="image_tooltip"> -<img src="apk_download_confirm_install.png" alt="Confirm Toggle allow from this source" title="image_tooltip"></p> -</li> -<li> -<p><strong>Open RapidPro - CHT</strong> - Once RapidPro - CHT has been installed, open the app drawer and tap on the RapidPro - CHT icon to start the app. -<img src="apk_download_start_app.png" alt="Open RapidPro - CHT" title="image_tooltip"></p> -</li> -<li> -<p><strong>Send Claim Code</strong> - When you first open RapidPro - CHT, the app will register your device with Google and our servers. Once that is complete, it will display a claim code. Copy that claim code in the form to the left and send it to your supporting engineers at Medic to connect your device. -<img src="apk_download_copy_claim.png" alt="Copy and Send Claim Code" title="image_tooltip"></p> -</li> -</ol> \ No newline at end of file +Messaging Gateways on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/Recent content in Messaging Gateways on Community Health ToolkitHugo -- gohugo.ioenAfrica’s Talking SMS Aggregatorhttps://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/africas-talking/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/africas-talking/As of v3.6.0, SMS messages can be sent and received using the Africa&rsquo;s Talking service. +Africa&rsquo;s Talking configuration First generate a long unique key to use as the cht-api-key. +Log on to the Africa&rsquo;s Talking Dashboard and configure your callback URLs as follows. +Delivery Reports: https://&lt;hostname&gt;/api/v1/sms/africastalking/delivery-reports?key=&lt;cht-api-key&gt; Incoming Messages: https://&lt;hostname&gt;/api/v1/sms/africastalking/incoming-messages?key=&lt;cht-api-key&gt; Then generate an &ldquo;API Key&rdquo; (we&rsquo;ll refer to this as the at-api-key) and save this in your CHT Core configuration covered below.RapidPro Messaging Gatewayhttps://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/rapidpro/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/rapidpro/As of v3.11.0, messages can be sent and received using RapidPro as a messaging gateway. +RapidPro configuration Store globals Generate a long unique key to use as the cht_api_key. +Log in to your RapidPro dashboard, go to the globals page (/global/) and create two globals with the following data: +name: cht_url, value: https://&lt;your-cht-instance-host&gt;/api/v2/sms/rapidpro/incoming-messages. For security the instance host must not include basic authentication. (NB: This endpoint was added in CHT 4.Installing RapidPro - CHT Gateway Android APKhttps://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/rapidpro_cht_gateway/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/rapidpro_cht_gateway/RapidPro - CHT uses your Android phone to send and receive messages on your behalf. +Due to restrictions that Google has placed on Android applications that send SMS messages, RapidPro - CHT applications can no longer be distributed through the Google Play Store. You will need to download the application file and install it using the steps below: +Download App - On your Android device, open your browser and download the app by entering the URL: https://rapidpro. \ No newline at end of file diff --git a/apps/guides/messaging/gateways/rapidpro/index.html b/apps/guides/messaging/gateways/rapidpro/index.html index 4f8446827b..fd88e20140 100644 --- a/apps/guides/messaging/gateways/rapidpro/index.html +++ b/apps/guides/messaging/gateways/rapidpro/index.html @@ -1,9 +1,9 @@ -RapidPro Messaging Gateway | Community Health Toolkit +RapidPro Messaging Gateway | Community Health Toolkit

    RapidPro Messaging Gateway

    Integration for sending and receiving messages

    As of v3.11.0, messages can be sent and received using RapidPro as a messaging gateway.

    RapidPro configuration

    Store globals

    Generate a long unique key to use as the cht_api_key.

    Log in to your RapidPro dashboard, go to the globals page (/global/) and create two globals with the following data:

    • name: cht_url, value: https://<your-cht-instance-host>/api/v2/sms/rapidpro/incoming-messages. For security the instance host must not include basic authentication. (NB: This endpoint was added in CHT 4.1.0. If integrating with an earlier version you will need to use the earlier version with a typo in the URL: https://<your-cht-instance-host>/api/v1/sms/radpidpro/incoming-messages)
    • name: cht_api_key, value: <cht_api_key>

    The names of these two global variables are arbitrary, but in this document we will keep referring to the names defined above.

    Then visit the RapidPro workspace settings page (/org/home/) and check your RapidPro API token (we’ll refer to this as the rapidpro_api_key).

    Create a new flow

    In your RapidPro dashboard, visit the flows page (/flow/) and create a new flow. It only needs to contain a webhook, to relay the message to your CHT Core instance and handle possible errors.

    flow_overview

    Configure the new webhook:

    • to POST to the cht_url you configured earlier:

    flow_webhook_host

    • set the authorization and content-type headers

    flow_webhook_headers

    • set the body to relay the message to CHT in the expected format:
    @(json(object(
    + Create project issue

    RapidPro Messaging Gateway

    Integration for sending and receiving messages

    As of v3.11.0, messages can be sent and received using RapidPro as a messaging gateway.

    RapidPro configuration

    Store globals

    Generate a long unique key to use as the cht_api_key.

    Log in to your RapidPro dashboard, go to the globals page (/global/) and create two globals with the following data:

    • name: cht_url, value: https://<your-cht-instance-host>/api/v2/sms/rapidpro/incoming-messages. For security the instance host must not include basic authentication. (NB: This endpoint was added in CHT 4.1.0. If integrating with an earlier version you will need to use the earlier version with a typo in the URL: https://<your-cht-instance-host>/api/v1/sms/radpidpro/incoming-messages)
    • name: cht_api_key, value: <cht_api_key>

    The names of these two global variables are arbitrary, but in this document we will keep referring to the names defined above.

    Then visit the RapidPro workspace settings page (/org/home/) and check your RapidPro API token (we’ll refer to this as the rapidpro_api_key).

    Create a new flow

    In your RapidPro dashboard, visit the flows page (/flow/) and create a new flow. It only needs to contain a webhook, to relay the message to your CHT Core instance and handle possible errors.

    flow_overview

    Configure the new webhook:

    • to POST to the cht_url you configured earlier:

    flow_webhook_host

    • set the authorization and content-type headers

    flow_webhook_headers

    • set the body to relay the message to CHT in the expected format:
    @(json(object(
       "id", run.uuid,
       "from", replace(urns.tel,"tel:+", "+"),
       "content", input.text
    @@ -329,7 +329,8 @@
     Quick Guides >
     Messaging >
     Contact IDs

    Using unique short codes to identify places and people via messaging

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/messaging/gateways/rapidpro_cht_gateway/index.html b/apps/guides/messaging/gateways/rapidpro_cht_gateway/index.html index ad695b044f..c9d8739fae 100644 --- a/apps/guides/messaging/gateways/rapidpro_cht_gateway/index.html +++ b/apps/guides/messaging/gateways/rapidpro_cht_gateway/index.html @@ -1,9 +1,9 @@ -Installing RapidPro - CHT Gateway Android APK | Community Health Toolkit +Installing RapidPro - CHT Gateway Android APK | Community Health Toolkit

    Installing RapidPro - CHT Gateway Android APK

    Integration for sending and receiving messages

    RapidPro - CHT uses your Android phone to send and receive messages on your behalf.

    Due to restrictions that Google has placed on Android applications that send SMS messages, RapidPro - CHT applications can no longer be distributed through the Google Play Store. You will need to download the application file and install it using the steps below:

    1. Download App - On your Android device, open your browser and download the app by entering the URL: https://rapidpro.app.medicmobile.org/android/

    2. Launch the RapidPro - CHT installer - Open the notifications shade by dragging from the top and select the downloaded file. + Create project issue

    Installing RapidPro - CHT Gateway Android APK

    Integration for sending and receiving messages

    RapidPro - CHT uses your Android phone to send and receive messages on your behalf.

    Due to restrictions that Google has placed on Android applications that send SMS messages, RapidPro - CHT applications can no longer be distributed through the Google Play Store. You will need to download the application file and install it using the steps below:

    1. Download App - On your Android device, open your browser and download the app by entering the URL: https://rapidpro.app.medicmobile.org/android/

    2. Launch the RapidPro - CHT installer - Open the notifications shade by dragging from the top and select the downloaded file. download

    3. Tap Settings - You will be taken to a screen with “allow from this source” toggle settings

    4. Toggle allow from this source - Make sure “allow from this source” toggle is in the On position. The go back and click install

      Toggle allow from this source Confirm Toggle allow from this source

    5. Open RapidPro - CHT - Once RapidPro - CHT has been installed, open the app drawer and tap on the RapidPro - CHT icon to start the app. @@ -310,7 +310,8 @@ Messaging > Gateways > RapidPro

      Integration for sending and receiving messages

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/guides/messaging/index.html b/apps/guides/messaging/index.html index 4515734339..77c036a826 100644 --- a/apps/guides/messaging/index.html +++ b/apps/guides/messaging/index.html @@ -1,9 +1,9 @@ -Messaging and SMS | Community Health Toolkit +Messaging and SMS | Community Health Toolkit

    Messaging and SMS

    Building and troubleshooting messaging

    Messaging Gateways

    Guides for setting up and maintaining messaging gateways

    SMS message states

    Overview of the possible states of SMS messages

    Short Contact Identifiers

    Using unique short codes to identify places and people via messaging

    Message Loops

    How to avoid messaging loops

    -

    Last modified 05.06.2020: Categorized guides (d26e182e)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/messaging/index.xml b/apps/guides/messaging/index.xml index cc05284c5c..7996f5b97f 100644 --- a/apps/guides/messaging/index.xml +++ b/apps/guides/messaging/index.xml @@ -1,182 +1,7 @@ -Community Health Toolkit – Messaging and SMShttps://docs.communityhealthtoolkit.org/apps/guides/messaging/Recent content in Messaging and SMS on Community Health ToolkitHugo -- gohugo.ioenApps: Messaging Gatewayshttps://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/Apps: SMS message stateshttps://docs.communityhealthtoolkit.org/apps/guides/messaging/sms-states/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/sms-states/ -<h2 id="interaction-with-sms-providers">Interaction with SMS providers</h2> -<p><a href="https://github.com/medic/cht-core">CHT Applications</a> can use <a href="https://github.com/medic/cht-gateway">CHT Gateway</a> and third party aggregators to send and receive SMS messages.</p> -<p>When an SMS report comes in from a user, <a href="https://github.com/medic/medic-sentinel">medic-sentinel</a> adds the appropriate list of scheduled messages (to be sent at a future date) to the report doc.</p> -<p>Periodically, sentinel checks for messages that need to be sent, and <a href="https://github.com/medic/medic-sentinel/blob/master/schedule/due_tasks.js">sets their state to <code>pending</code></a> if their scheduled sending time has been reached or passed.</p> -<p>Periodically, the aggregator checks for messages that need to be sent (i.e. that are in <code>pending</code> state).</p> -<p>The aggregator also reports on the status of the messages it&rsquo;s sending.</p> -<h2 id="message-statusesstates">Message statuses/states</h2> -<p>Both webapp and the aggregator store states/statuses of the messages to keep track of the exchange. They each have their set of statuses, which sometimes are called the same but do not mean the same thing. Watch out.</p> -<h3 id="message-statuses-in-cht-gateway">Message statuses in cht-gateway</h3> -<p>See <a href="https://github.com/medic/cht-gateway#user-content-content">https://github.com/medic/cht-gateway#content</a></p> -<h3 id="message-states-in-medic">Message states in medic</h3> -<table> -<thead> -<tr> -<th>State</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>scheduled</td> -<td>Not yet due. Messages as part of a configured schedule start in this state and are changed to <code>pending</code> when due.</td> -</tr> -<tr> -<td>pending</td> -<td>Due to be sent. The SMS gateway should pick this up for sending. Auto replies and instant messages start in this state.</td> -</tr> -<tr> -<td>forwarded-to-gateway</td> -<td>Message has been sent to gateway.</td> -</tr> -<tr> -<td>received-by-gateway</td> -<td>Has been received by the gateway.</td> -</tr> -<tr> -<td>forwarded-by-gateway</td> -<td>Gateway has tried sending the message.</td> -</tr> -<tr> -<td>sent</td> -<td>Successfully delivered to the sms network.</td> -</tr> -<tr> -<td>delivered</td> -<td>Successfully received by the recipient&rsquo;s device.</td> -</tr> -<tr> -<td>failed</td> -<td>The sending attempt failed. Sending will not be retried without user intervention.</td> -</tr> -<tr> -<td>denied</td> -<td>This will not be sent. The recipient phone number is configured to be denied via <code>outgoing_deny_list</code>, <code>outgoing_deny_with_alphas</code>, or <code>outgoing_deny_shorter_than</code>.</td> -</tr> -<tr> -<td>cleared</td> -<td>This will not be sent as a report has triggered an event to stop it. This can happen if a patient visit has occurred before the visit reminder is sent.</td> -</tr> -<tr> -<td>muted</td> -<td>This will not be sent as the task has been deliberately stopped. Messages in this state can be unmuted by user action.</td> -</tr> -</tbody> -</table> -<h2 id="timeline-of-the-cht-gateway-protocol-for-webapp-originating-message">Timeline of the cht-gateway protocol for webapp-originating message</h2> -<p>Read the table below like a vertical timeline : each time an event happens, the states/statuses corresponding to the message get updated.</p> -<p>Note 1 : Gateway only sends a status update for a message only if the &ldquo;needs forwarding&rdquo; flag for the message status is true, and then sets it back to false. So it only sends status updates once.</p> -<p>Note 2 : If api sends the same WO message again, then gateway sets its needs forwarding flag to true, and so sends the status at the next poll.</p> -<p>Note 3 : not all of the events below happen every time : this is assuming only one step of SMS-sending happens between each poll. If several steps happened, then some of the events below are skipped. If several status changes have happened between polls, Gateway will report the multiple new statuses at the next poll.</p> -<table> -<thead> -<tr> -<th>number</th> -<th>Event</th> -<th>webapp state</th> -<th>gateway status</th> -<th>gateway &ldquo;Needs forwarding&rdquo; flag</th> -</tr> -</thead> -<tbody> -<tr> -<td>1</td> -<td>Due date to send the message passes</td> -<td>pending</td> -<td>&mdash;</td> -<td></td> -</tr> -<tr> -<td>2</td> -<td>Gateway polls and gets a new WO message</td> -<td>forwarded-to-gateway</td> -<td>&mdash;</td> -<td>&mdash;</td> -</tr> -<tr> -<td>3</td> -<td>Gateway saves message in its DB</td> -<td>forwarded-to-gateway</td> -<td>UNSENT</td> -<td>true</td> -</tr> -<tr> -<td>4</td> -<td>Gateway reports UNSENT status for the message</td> -<td>received-by-gateway</td> -<td>UNSENT</td> -<td>false</td> -</tr> -<tr> -<td>5</td> -<td>Gateway sends the message</td> -<td>received-by-gateway</td> -<td>PENDING</td> -<td>true</td> -</tr> -<tr> -<td>6</td> -<td>Gateway reports PENDING status for the message</td> -<td>forwarded-by-gateway</td> -<td>PENDING</td> -<td>false</td> -</tr> -<tr> -<td>7</td> -<td>Gateway gets confirmation the message left</td> -<td>forwarded-by-gateway</td> -<td>SENT</td> -<td>true</td> -</tr> -<tr> -<td>8</td> -<td>Gateway reports SENT status for the message</td> -<td>sent</td> -<td>SENT</td> -<td>false</td> -</tr> -<tr> -<td>9</td> -<td>Gateway gets confirmation the message arrived</td> -<td>sent</td> -<td>DELIVERED</td> -<td>true</td> -</tr> -<tr> -<td>10</td> -<td>Gateway reports DELIVERED status for the message</td> -<td>delivered</td> -<td>DELIVERED</td> -<td>false</td> -</tr> -</tbody> -</table>Apps: Short Contact Identifiershttps://docs.communityhealthtoolkit.org/apps/guides/messaging/shortcodes/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/shortcodes/ -<p>Short unique identifiers for contacts are often used to identify contacts in messaging workflows. Unique short codes are generated on <code>doc.patient_id</code> against any document of a <code>person</code> <code>type</code>, and on every <code>doc.place_id</code> against any document of a <code>place</code> <code>type</code>. By default, these IDs start at 5 numeric digits long, and will increase in length as deemed necessary by the generation algorithm.</p> -<p>If the length is increased, this increase is stored in a CouchDB documented called <code>shortcode-id-length</code>:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;shortcode-id-length&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;current_length&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">6</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h2 id="configuring-a-minimum-length">Configuring a minimum length</h2> -<p>If you wish to change the minimum length of the generated identifiers, create or edit the <code>shortcode-id-length</code> document in CouchDB. For example, if you wish for the minimum length to be 7:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;shortcode-id-length&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;current_length&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">7</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>If this file already exists be sure to include the existing <code>_rev</code> property.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -If you are changing this document and want to make it relevant straight away, <strong>you must restart Sentinel.</strong> Otherwise there may be a collection of cached already accepted IDs of the previous length that Sentinel will work through first. -</div> -<h2 id="configuring-a-maximum-length-locking-down-an-exact-length-etc">Configuring a maximum length, locking down an exact length etc</h2> -<p>It is not possible to either alter the maximum length of IDs, to stop it automatically increasing, or to tweak when it decides to automatically increase. IDs automatically increasing in length when required is important to the continual correct functioning of Sentinel.</p> -<h2 id="valid-sizes">Valid sizes</h2> -<p>The shortcode identifiers can be between 5 and 13 digits long. Due to the last digit being a checksum digit, there are at most 10,000 5 digit ids, and at most 1,000,000,000,000 13 digit IDs.</p>Apps: Message Loopshttps://docs.communityhealthtoolkit.org/apps/guides/messaging/message-loops/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/message-loops/ -<p>Endless messaging loops can between the webapp and a mobile number via the gateway due to autoreplies from the webapp.</p> -<p>See the <a href="https://github.com/medic/cht-core/issues/750">Github Issue</a>.</p> -<p><strong>Solution:</strong> Add the offending number(e.g <code>800</code> or <code>SAFARICOM</code>) to the <code>Outgoing Deny List</code> in the webapp&rsquo;s <code>app_settings</code> configuration file.</p> -<pre tabindex="0"><code> &#34;multipart_sms_limit&#34;: 10, -&#34;outgoing_deny_list&#34;: &#34;800, SAFARICOM&#34;, -&#34;contact_summary&#34;: &#34;&#34; -</code></pre> \ No newline at end of file +Messaging and SMS on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/guides/messaging/Recent content in Messaging and SMS on Community Health ToolkitHugo -- gohugo.ioenSMS message stateshttps://docs.communityhealthtoolkit.org/apps/guides/messaging/sms-states/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/sms-states/Interaction with SMS providers CHT Applications can use CHT Gateway and third party aggregators to send and receive SMS messages. +When an SMS report comes in from a user, medic-sentinel adds the appropriate list of scheduled messages (to be sent at a future date) to the report doc. +Periodically, sentinel checks for messages that need to be sent, and sets their state to pending if their scheduled sending time has been reached or passed.Short Contact Identifiershttps://docs.communityhealthtoolkit.org/apps/guides/messaging/shortcodes/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/shortcodes/Short unique identifiers for contacts are often used to identify contacts in messaging workflows. Unique short codes are generated on doc.patient_id against any document of a person type, and on every doc.place_id against any document of a place type. By default, these IDs start at 5 numeric digits long, and will increase in length as deemed necessary by the generation algorithm. +If the length is increased, this increase is stored in a CouchDB documented called shortcode-id-length:Message Loopshttps://docs.communityhealthtoolkit.org/apps/guides/messaging/message-loops/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/message-loops/Endless messaging loops can between the webapp and a mobile number via the gateway due to autoreplies from the webapp. +See the Github Issue. +Solution: Add the offending number(e.g 800 or SAFARICOM) to the Outgoing Deny List in the webapp&rsquo;s app_settings configuration file. +&#34;multipart_sms_limit&#34;: 10, &#34;outgoing_deny_list&#34;: &#34;800, SAFARICOM&#34;, &#34;contact_summary&#34;: &#34;&#34; \ No newline at end of file diff --git a/apps/guides/messaging/message-loops/index.html b/apps/guides/messaging/message-loops/index.html index 647765e7f4..418ea5327d 100644 --- a/apps/guides/messaging/message-loops/index.html +++ b/apps/guides/messaging/message-loops/index.html @@ -1,9 +1,9 @@ -Message Loops | Community Health Toolkit +Message Loops | Community Health Toolkit

    Message Loops

    How to avoid messaging loops

    Endless messaging loops can between the webapp and a mobile number via the gateway due to autoreplies from the webapp.

    See the Github Issue.

    Solution: Add the offending number(e.g 800 or SAFARICOM) to the Outgoing Deny List in the webapp’s app_settings configuration file.

      "multipart_sms_limit": 10,
    + Create project issue

    Message Loops

    How to avoid messaging loops

    Endless messaging loops can between the webapp and a mobile number via the gateway due to autoreplies from the webapp.

    See the Github Issue.

    Solution: Add the offending number(e.g 800 or SAFARICOM) to the Outgoing Deny List in the webapp’s app_settings configuration file.

      "multipart_sms_limit": 10,
       "outgoing_deny_list": "800, SAFARICOM",
       "contact_summary": ""
     

    CHT Applications > @@ -313,7 +313,8 @@ Quick Guides > Messaging > Contact IDs

    Using unique short codes to identify places and people via messaging

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/guides/messaging/shortcodes/index.html b/apps/guides/messaging/shortcodes/index.html index a5c1df1bb7..ba30642bfd 100644 --- a/apps/guides/messaging/shortcodes/index.html +++ b/apps/guides/messaging/shortcodes/index.html @@ -1,9 +1,9 @@ -Short Contact Identifiers | Community Health Toolkit +Short Contact Identifiers | Community Health Toolkit

    Short Contact Identifiers

    Using unique short codes to identify places and people via messaging

    Short unique identifiers for contacts are often used to identify contacts in messaging workflows. Unique short codes are generated on doc.patient_id against any document of a person type, and on every doc.place_id against any document of a place type. By default, these IDs start at 5 numeric digits long, and will increase in length as deemed necessary by the generation algorithm.

    If the length is increased, this increase is stored in a CouchDB documented called shortcode-id-length:

    Short Contact Identifiers

    Using unique short codes to identify places and people via messaging

    Short unique identifiers for contacts are often used to identify contacts in messaging workflows. Unique short codes are generated on doc.patient_id against any document of a person type, and on every doc.place_id against any document of a place type. By default, these IDs start at 5 numeric digits long, and will increase in length as deemed necessary by the generation algorithm.

    If the length is increased, this increase is stored in a CouchDB documented called shortcode-id-length:

    {
         "_id": "shortcode-id-length",
         "current_length": 6
     }
    @@ -315,7 +315,8 @@
     Quick Guides >
     Messaging >
     SMS States

    Overview of the possible states of SMS messages

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/messaging/sms-states/index.html b/apps/guides/messaging/sms-states/index.html index 9985fbd7cf..40dcf9e03c 100644 --- a/apps/guides/messaging/sms-states/index.html +++ b/apps/guides/messaging/sms-states/index.html @@ -1,9 +1,9 @@ -SMS message states | Community Health Toolkit +SMS message states | Community Health Toolkit

    SMS message states

    Overview of the possible states of SMS messages

    Interaction with SMS providers

    CHT Applications can use CHT Gateway and third party aggregators to send and receive SMS messages.

    When an SMS report comes in from a user, medic-sentinel adds the appropriate list of scheduled messages (to be sent at a future date) to the report doc.

    Periodically, sentinel checks for messages that need to be sent, and sets their state to pending if their scheduled sending time has been reached or passed.

    Periodically, the aggregator checks for messages that need to be sent (i.e. that are in pending state).

    The aggregator also reports on the status of the messages it’s sending.

    Message statuses/states

    Both webapp and the aggregator store states/statuses of the messages to keep track of the exchange. They each have their set of statuses, which sometimes are called the same but do not mean the same thing. Watch out.

    Message statuses in cht-gateway

    See https://github.com/medic/cht-gateway#content

    Message states in medic

    StateDescription
    scheduledNot yet due. Messages as part of a configured schedule start in this state and are changed to pending when due.
    pendingDue to be sent. The SMS gateway should pick this up for sending. Auto replies and instant messages start in this state.
    forwarded-to-gatewayMessage has been sent to gateway.
    received-by-gatewayHas been received by the gateway.
    forwarded-by-gatewayGateway has tried sending the message.
    sentSuccessfully delivered to the sms network.
    deliveredSuccessfully received by the recipient’s device.
    failedThe sending attempt failed. Sending will not be retried without user intervention.
    deniedThis will not be sent. The recipient phone number is configured to be denied via outgoing_deny_list, outgoing_deny_with_alphas, or outgoing_deny_shorter_than.
    clearedThis will not be sent as a report has triggered an event to stop it. This can happen if a patient visit has occurred before the visit reminder is sent.
    mutedThis will not be sent as the task has been deliberately stopped. Messages in this state can be unmuted by user action.

    Timeline of the cht-gateway protocol for webapp-originating message

    Read the table below like a vertical timeline : each time an event happens, the states/statuses corresponding to the message get updated.

    Note 1 : Gateway only sends a status update for a message only if the “needs forwarding” flag for the message status is true, and then sets it back to false. So it only sends status updates once.

    Note 2 : If api sends the same WO message again, then gateway sets its needs forwarding flag to true, and so sends the status at the next poll.

    Note 3 : not all of the events below happen every time : this is assuming only one step of SMS-sending happens between each poll. If several steps happened, then some of the events below are skipped. If several status changes have happened between polls, Gateway will report the multiple new statuses at the next poll.

    numberEventwebapp stategateway statusgateway “Needs forwarding” flag
    1Due date to send the message passespending
    2Gateway polls and gets a new WO messageforwarded-to-gateway
    3Gateway saves message in its DBforwarded-to-gatewayUNSENTtrue
    4Gateway reports UNSENT status for the messagereceived-by-gatewayUNSENTfalse
    5Gateway sends the messagereceived-by-gatewayPENDINGtrue
    6Gateway reports PENDING status for the messageforwarded-by-gatewayPENDINGfalse
    7Gateway gets confirmation the message leftforwarded-by-gatewaySENTtrue
    8Gateway reports SENT status for the messagesentSENTfalse
    9Gateway gets confirmation the message arrivedsentDELIVEREDtrue
    10Gateway reports DELIVERED status for the messagedeliveredDELIVEREDfalse

    CHT Applications > + Create project issue

    SMS message states

    Overview of the possible states of SMS messages

    Interaction with SMS providers

    CHT Applications can use CHT Gateway and third party aggregators to send and receive SMS messages.

    When an SMS report comes in from a user, medic-sentinel adds the appropriate list of scheduled messages (to be sent at a future date) to the report doc.

    Periodically, sentinel checks for messages that need to be sent, and sets their state to pending if their scheduled sending time has been reached or passed.

    Periodically, the aggregator checks for messages that need to be sent (i.e. that are in pending state).

    The aggregator also reports on the status of the messages it’s sending.

    Message statuses/states

    Both webapp and the aggregator store states/statuses of the messages to keep track of the exchange. They each have their set of statuses, which sometimes are called the same but do not mean the same thing. Watch out.

    Message statuses in cht-gateway

    See https://github.com/medic/cht-gateway#content

    Message states in medic

    StateDescription
    scheduledNot yet due. Messages as part of a configured schedule start in this state and are changed to pending when due.
    pendingDue to be sent. The SMS gateway should pick this up for sending. Auto replies and instant messages start in this state.
    forwarded-to-gatewayMessage has been sent to gateway.
    received-by-gatewayHas been received by the gateway.
    forwarded-by-gatewayGateway has tried sending the message.
    sentSuccessfully delivered to the sms network.
    deliveredSuccessfully received by the recipient’s device.
    failedThe sending attempt failed. Sending will not be retried without user intervention.
    deniedThis will not be sent. The recipient phone number is configured to be denied via outgoing_deny_list, outgoing_deny_with_alphas, or outgoing_deny_shorter_than.
    clearedThis will not be sent as a report has triggered an event to stop it. This can happen if a patient visit has occurred before the visit reminder is sent.
    mutedThis will not be sent as the task has been deliberately stopped. Messages in this state can be unmuted by user action.

    Timeline of the cht-gateway protocol for webapp-originating message

    Read the table below like a vertical timeline : each time an event happens, the states/statuses corresponding to the message get updated.

    Note 1 : Gateway only sends a status update for a message only if the “needs forwarding” flag for the message status is true, and then sets it back to false. So it only sends status updates once.

    Note 2 : If api sends the same WO message again, then gateway sets its needs forwarding flag to true, and so sends the status at the next poll.

    Note 3 : not all of the events below happen every time : this is assuming only one step of SMS-sending happens between each poll. If several steps happened, then some of the events below are skipped. If several status changes have happened between polls, Gateway will report the multiple new statuses at the next poll.

    numberEventwebapp stategateway statusgateway “Needs forwarding” flag
    1Due date to send the message passespending
    2Gateway polls and gets a new WO messageforwarded-to-gateway
    3Gateway saves message in its DBforwarded-to-gatewayUNSENTtrue
    4Gateway reports UNSENT status for the messagereceived-by-gatewayUNSENTfalse
    5Gateway sends the messagereceived-by-gatewayPENDINGtrue
    6Gateway reports PENDING status for the messageforwarded-by-gatewayPENDINGfalse
    7Gateway gets confirmation the message leftforwarded-by-gatewaySENTtrue
    8Gateway reports SENT status for the messagesentSENTfalse
    9Gateway gets confirmation the message arrivedsentDELIVEREDtrue
    10Gateway reports DELIVERED status for the messagedeliveredDELIVEREDfalse

    CHT Applications > Features > Messaging

    Messaging for Care Coordination, Alerts, and Notifications

    CHT Applications > Quick Guides > @@ -309,7 +309,8 @@ Reference > app_settings.json > .sms

    SMS Settings: Instructions and schema for defining SMS settings

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/performance/index.html b/apps/guides/performance/index.html index f064615d00..dfa8e05f63 100644 --- a/apps/guides/performance/index.html +++ b/apps/guides/performance/index.html @@ -1,9 +1,9 @@ -Tracking and Improving Performance | Community Health Toolkit +Tracking and Improving Performance | Community Health Toolkit

    Tracking and Improving Performance

    Guides for tracking and improving the performance of CHT applications and servers

    Purging

    Remove unneeded documents from offline users devices.

    CouchDB replication

    Settings for downloading copies of data onto a user’s device.

    User telemetry

    Performance data of certain user actions

    -

    Last modified 05.06.2020: Categorized guides (d26e182e)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/performance/index.xml b/apps/guides/performance/index.xml index 95e23919a0..131af77624 100644 --- a/apps/guides/performance/index.xml +++ b/apps/guides/performance/index.xml @@ -1,1256 +1,7 @@ -Community Health Toolkit – Tracking and Improving Performancehttps://docs.communityhealthtoolkit.org/apps/guides/performance/Recent content in Tracking and Improving Performance on Community Health ToolkitHugo -- gohugo.ioenApps: Purginghttps://docs.communityhealthtoolkit.org/apps/guides/performance/purging/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/performance/purging/ -<p><em>Only available in 3.7.0 and above</em></p> -<p>Purging is a tool that allows you to increase performance and available disk space for offline users (eg CHWs) by removing unneeded documents from their device.</p> -<p>As users continually generate new reports their performance may naturally degrade as a result. You can use purging to remove older documents that are no longer relevant from their devices. Purging only removes documents from user&rsquo;s devices: these reports are still available for online analytics and impact metrics.</p> -<p>Purging is disabled by default, and is enabled if a purge function is specified in <code>app_settings.json</code>, along with a run schedule.</p> -<p>The following example would purge all reports that were created more than a year ago:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;//&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;other app_settings settings&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;purge&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;fn&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;function(userCtx, contact, reports, messages, chtScriptApi, permissions) { const old = Date.now() - (1000 * 60 * 60 * 24 * 365); return reports.filter(r =&gt; r.reported_date &lt; old).map(r =&gt; r._id);}&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;text_expression&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;at 12 am on Sunday&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p><strong>Purging is both very powerful and also very dangerous</strong>. Read the rest of this document carefully to make sure you completely understand how to purge and the ramifications of doing so, before using purging in your project.</p> -<h2 id="server-side">Server-side</h2> -<p>Purging runs on the server on a configurable schedule.</p> -<p>It will iterate over all users to generate a list of unique roles groups that represent every user. Each group will have their purged docs saved in an individual database.</p> -<p>Then, it will iterate over all existent contacts, collecting all reports about that contact along with all sms messages that the contact has sent or received. This is similar to the scoping you may have encountered when configuring <a href="https://docs.communityhealthtoolkit.org/apps/reference/tasks/">tasks</a> and <a href="https://docs.communityhealthtoolkit.org/apps/reference/targets/">targets</a>.</p> -<p>The configured purge function runs over all combinations of purge scope (contact + reports + messages) and user context (unique list of roles) to determine which docs should be purged.</p> -<p>The resulting list of docs to be purged is compared to the existent purged docs so that only the differences are saved (old purges are reverted and new purges are added).</p> -<p>A document is considered purged for a user if a document with the same id, prefixed by <code>purge</code>, exists in the corresponding purge database.</p> -<p>The following user:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;org.couchdb.user:&lt;your user&gt;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;roles&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;district_admin&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;supervisor&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>would get their purges from a <code>medic-purged-role-&lt;role_hash&gt;</code> where <code>role_hash</code> is an md5 hash of the user&rsquo;s roles.</p> -<p>When users sync (includes initial sync), they will only download documents that are not purged for their roles.</p> -<h2 id="client-side">Client-side</h2> -<p>Periodically a user&rsquo;s device calls an API endpoint that returns batches of doc ids that have been purged since last time the same device checked. This is done in the background so the user is not disrupted from their work.</p> -<p>The system is similar to CouchDB replication, in the sense that a <code>checkpointer</code> document is saved in the corresponding server-side database, that stores the <code>last_seq</code> that the device has downloaded and is used to get the next batch of ids. Purging many documents while the application is running is an expensive and potentially disruptive activity, so instead the ids are stored until the next time the app is started.</p> -<p>Then, when the user launches the app again during the bootstrap phase the app checks if there are any documents to purge, and if so deletes them all before starting up as usual. Doing the deletion prior to startup is relatively fast and designed to disrupt the user as little as possible.</p> -<h2 id="configuration">Configuration</h2> -<p>To enable purging, write your purge configuration to <code>purge.js</code> in your project root:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#000">module</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">exports</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">text_expression</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;at 9 am on Sunday&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">run_every_days</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">7</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">cron</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;0 1 * * SUN&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">fn</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">userCtx</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">reports</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">messages</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">chtScriptApi</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">permissions</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">old</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87">Date</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">now</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#ce5c00;font-weight:bold">-</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">1000</span> <span style="color:#ce5c00;font-weight:bold">*</span> <span style="color:#0000cf;font-weight:bold">60</span> <span style="color:#ce5c00;font-weight:bold">*</span> <span style="color:#0000cf;font-weight:bold">60</span> <span style="color:#ce5c00;font-weight:bold">*</span> <span style="color:#0000cf;font-weight:bold">24</span> <span style="color:#ce5c00;font-weight:bold">*</span> <span style="color:#0000cf;font-weight:bold">365</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">oldMessages</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87">Date</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">now</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#ce5c00;font-weight:bold">-</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">1000</span> <span style="color:#ce5c00;font-weight:bold">*</span> <span style="color:#0000cf;font-weight:bold">60</span> <span style="color:#ce5c00;font-weight:bold">*</span> <span style="color:#0000cf;font-weight:bold">60</span> <span style="color:#ce5c00;font-weight:bold">*</span> <span style="color:#0000cf;font-weight:bold">24</span> <span style="color:#ce5c00;font-weight:bold">*</span> <span style="color:#0000cf;font-weight:bold">90</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">reportsToPurge</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">reports</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">filter</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">r</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000">r</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reported_date</span> <span style="color:#ce5c00;font-weight:bold">&lt;</span> <span style="color:#000">old</span><span style="color:#000;font-weight:bold">)</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">map</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">r</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000">r</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">_id</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">messagesToPurge</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">messages</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">filter</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">m</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000">m</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reported_date</span> <span style="color:#ce5c00;font-weight:bold">&lt;</span> <span style="color:#000">oldMessages</span><span style="color:#000;font-weight:bold">)</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">map</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">m</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000">m</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">_id</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000;font-weight:bold">[...</span><span style="color:#000">reportsToPurge</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000;font-weight:bold">...</span><span style="color:#000">messagesToPurge</span><span style="color:#000;font-weight:bold">];</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">};</span> -</span></span></code></pre></div><h3 id="purge-configuration">Purge configuration</h3> -<p>As shown above, you should be exporting a property <code>fn</code> defining a self contained function: it should have no outside dependencies - like used variables, required modules or call outside functions.</p> -<p>This function takes six parameters:</p> -<ul> -<li><code>userCtx</code>, an object with the user&rsquo;s <code>roles</code> as fields. For more information read the <a href="https://docs.couchdb.org/en/stable/json-structure.html#userctx-object">documentation for the User Context Object</a>.</li> -<li><code>contact</code>, the contact document of a patient or other contact who has reports about them.</li> -<li><code>reports</code>, an array of all reports for that subject that are present on the server.</li> -<li><code>messages</code>, an array of sms messages that the contact has sent or received.</li> -<li><code>chtScriptApi</code> (Optional) the CHT API that provides CHT-Core Framework&rsquo;s functions to other parts of the app. More info on the API can be found <a href="https://docs.communityhealthtoolkit.org/apps/reference/_partial_cht_api/">here</a>. Added in 4.3.0.</li> -<li><code>permissions</code> (Optional) string or array of permissions that the user has. More info on permissions can be found <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-permissions/">here</a>. Added in 4.3.0.</li> -</ul> -<p>And should return an array of <code>_id</code> values for docs you would like to be purged (or <code>undefined</code> / nothing if you don&rsquo;t wish to purge anything). `Only ids of docs that were passed to the function are valid for purging: you are not allowed to purge other documents.</p> -<p>In the cases of reports that do not have subjects or their subjects are not found, the <code>purge</code> function will receive an empty object as <code>contact</code>. In the cases of reports about deleted contacts, the <code>purge</code> function will receive a <code>{ _deleted: true }</code> object as the <code>contact</code>.</p> -<p>As of <strong>3.9.0</strong>, <code>task</code> documents that are in a terminal state (<code>Cancelled</code>, <code>Completed</code>, <code>Failed</code>) are purged if their <code>end_date</code> is more than 60 days ago (relative to server date). -As of <strong>3.9.0</strong>, <code>target</code> documents are purged if their reporting period is more than 6 months ago (relative to server date). Purging <code>task</code> and <code>target</code> documents happens automatically on every purge run. The intervals and required states are not configurable.</p> -<h3 id="schedule-configuration">Schedule configuration</h3> -<p>You must set a schedule for purging to run server-side. -Depending on the size of the database and server capacity, purging could be a lengthy and resource intensive operation, so it is recommended you run purge on a schedule that your server can sustain (for example at nighttime in the weekends).</p> -<p>You can also change the frequency of local purge downloads (default being every 7 days).</p> -<table> -<thead> -<tr> -<th>property</th> -<th>description</th> -<th>required</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>fn</code></td> -<td>Self-contained purge function</td> -<td>yes</td> -</tr> -<tr> -<td><code>run_every_days</code></td> -<td>The interval (in days) at which purges will be downloaded client-side. <em>Default 7</em>.</td> -<td>no</td> -</tr> -<tr> -<td><code>text_expression</code></td> -<td>Any valid text expression to describe the interval of running purge server-side. For more information, see <a href="https://bunkat.github.io/later/parsers.html#text">LaterJS</a></td> -<td>no if <code>cron</code> provided</td> -</tr> -<tr> -<td><code>cron</code></td> -<td>Any valid Cron expression to describe the interval of running purge server-side. For more information, see <a href="https://bunkat.github.io/later/parsers.html#cron">LaterJS</a></td> -<td>no if <code>text_expression</code> provided</td> -</tr> -</tbody> -</table> -<p>Example of purge configured in your app_settings:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;//&#34;</span><span style="color:#a40000">:</span> <span style="color:#4e9a06">&#34;other app_settings settings&#34;</span><span style="color:#a40000">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;purge&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;fn&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;function(userCtx, contact, reports, messages) { return []; }&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;cron&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;0 1 * * SUN&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;text_expression&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;at 1:00 am on Sun&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;run_every_days&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">5</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h2 id="considerations">Considerations</h2> -<h3 id="purged-documents-server-side">Purged documents server-side</h3> -<p>Purging is run as a scheduled task in Sentinel.</p> -<p>Purging does not touch documents in the <code>medic</code> database, everything is done in separate purge databases(<code>medic-purged-roles-&lt;roles-hash&gt;</code>).</p> -<p>The purge databases names contain an md5 of the JSON representation of a list of unique roles. -They also contain a <code>_local/info</code> doc where the roles are listed in clear text.</p> -<p>As of <strong>3.14.0</strong>, contacts that have more than 20,000 associated reports + messages will be skipped, and none of their associated reports and messages will be purged. A single contact that has more than 20,000 associated records most likely points to a configuration issue. Skipped contacts&rsquo; ids are reported both in logs and in <code>purgelog</code> files (see below).</p> -<p>A <code>purgelog</code> document is saved in the <code>medic-sentinel</code> database after every purge. The purgelog has a meaningful id: <code>purgelog:&lt;timestamp&gt;</code>, where <code>timestamp</code> represents the moment when purging was completed. As of <strong>3.14.0</strong>, every time purging fails to complete due to an error, a <code>purgelog:error</code> document is saved in the <code>medic-sentinel</code> database. The ids of these documents are similarly meaningful: <code>purgelog:error:&lt;timestamp&gt;</code>.</p> -<p><code>purgelog</code> docs have the following properties:</p> -<table> -<thead> -<tr> -<th>property</th> -<th>value type</th> -<th>description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>date</code></td> -<td>ISO formatted date</td> -<td>The moment when purging was completed</td> -</tr> -<tr> -<td><code>roles</code></td> -<td>Map</td> -<td>A map of roles lists and their hashes that purging has run for</td> -</tr> -<tr> -<td><code>duration</code></td> -<td>Number</td> -<td>Time it took to run purging, in ms.</td> -</tr> -<tr> -<td><code>skipped_contacts</code></td> -<td>Array</td> -<td>Available since <strong>3.14.0</strong> List of contacts ids that were skipped, due to having too many associated records.</td> -</tr> -<tr> -<td><code>error</code></td> -<td>String</td> -<td>Available since <strong>3.14.0</strong> Describes the error that caused purging to fail. Only present in <code>purgelog:error</code> docs.</td> -</tr> -</tbody> -</table> -<p>You can retrieve a list of all your purge logs, descending from newest to oldest, with this request:</p> -<p><code>https(s)://&lt;host&gt;/medic-sentinel/_all_docs?end_key=&quot;purgelog:&quot;&amp;start_key=&quot;purgelog:\ufff0&quot;&amp;descending=true</code></p> -<p>You can retrieve a list of all your purge logs with errors, descending from newest to oldest, with this request:</p> -<p><code>https(s)://&lt;host&gt;/medic-sentinel/_all_docs?end_key=&quot;purgelog:error:&quot;&amp;start_key=&quot;purgelog:error:\ufff0&quot;&amp;descending=true</code></p> -<p>Purging is reversible. If you update your purge function, when running purge the old invalid purges will be deleted. This does not mean that devices will automatically re-download documents that become unpurged. In order for the user to re-download a previously purged document, the document either needs to be updated in the <code>medic</code> database on the server or the user has to download all data again.</p> -<p>Running purge will not remove old purge databases, even if they don&rsquo;t correspond to any existent users. Their removal is a manual process.</p> -<p>Purge does not run when adding new roles or adding said new roles to users. It also does not run when an existent user is updated to have a new unique list of roles (one that purge has not run over yet). This means that roles need to be planned carefully in order to take advantage of serverside purge. If purge has not run for the user&rsquo;s list of roles at the moment of initial replication, the user will download <strong>all</strong> documents - only to be purged later.</p> -<h3 id="purged-documents-client-side">Purged documents client-side</h3> -<p>The key thing to keep in mind while purging is that <strong>documents that you purge are deleted on user&rsquo;s device</strong>. This sounds obvious, but it&rsquo;s important to understand <em>how</em> this affects the running of the application:</p> -<ul> -<li>Any <strong>rules</strong> you have written that presume that the document exists may break. For example, if the document completes a task, purging it will reopen that task, unless you <em>also</em> purge the document that created the task in the first place (while making sure that purging <em>that</em> report doesn&rsquo;t break more things!)</li> -<li>Similarly <strong>targets</strong> won&rsquo;t be able to use the report to generate values, so counts may go down or become inaccurate</li> -<li>Additionally, the <strong>contact summary</strong> will also lose out on being able to use that report</li> -<li>Changing the user&rsquo;s roles list (adding/removing roles) will cause the user to download <strong>all purged docs ids</strong> from the purge database corresponding to their new roles list.</li> -</ul> -<p>More subtly, you may also confuse your users!</p> -<p>If you purge documents too quickly, they may get confused as to whether they created the report or not, and may create it again, causing data problems. Users are not told that purging is occurring in a very obvious way: the expectation is that purging will naturally occur as documents become irrelevant, and so users should never really notice.</p> -<p>Users may search for their own documents, and use data from them in novel ways you may not anticipate. It&rsquo;s important to work with your users to ensure documents are only removed once there are no uses for them.</p> -<p>It is key then, that you test your purge rules thoroughly!</p>Apps: CouchDB replicationhttps://docs.communityhealthtoolkit.org/apps/guides/performance/replication/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/performance/replication/ -<p>Replication is what we call it when users download a copy of the data on to their device.</p> -<h2 id="restricting-replication">Restricting replication</h2> -<p>If the user has an online role they can access all the data, otherwise they will get restricted access to the data.</p> -<h3 id="restriction-by-place">Restriction by place</h3> -<p>The most common restriction is by place. This is where we check the user&rsquo;s <code>facility_id</code> property, and allow access to all contacts that are descendants of that place, and all reports and messages that are about one of those descendants.</p> -<p>For example, if a CHP&rsquo;s <code>facility_id</code> property is set to the ID of the Maori Hill clinic, then they will be able to see all patients and all reports about patients at that clinic.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Starting in v4.9.0, users with the <code>can_have_multiple_places</code> permission can be assigned more than one <code>facility_id</code>. The primary use case for this is for Supervisors who manage multiple areas. A video demonstration of setting up a multi-facility user and what this looks like from a user’s perspective can be found <a href="https://forum.communityhealthtoolkit.org/t/support-for-supervisors-who-need-to-manage-multiple-areas/3497/2?u=michael">on the forum</a> and in the <a href="https://youtu.be/hrhdrzP41gE?si=_7wglk7Nm7CCSFbY&amp;t=606">June 2024 CHT Round-up</a>. -</div> -<h3 id="depth">Depth</h3> -<p>Sometimes though you want to only access contacts near the top of the hierarchy. This may be because returning all contacts would be too much data to be practical, or for patient privacy, or because it&rsquo;s just not part of your workflow. In this case you can configure a replication depth for a specific role under <code>replication_depth</code> in the app settings.</p> -<p>In 3.10, we add the ability to limit replication depth for reports, in conjunction with replication depth for contacts.</p> -<h5 id="code-sample">Code sample:</h5> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;replication_depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">&#34;role&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;district_manager&#34;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">&#34;depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">&#34;report_depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">&#34;role&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;national_manager&#34;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">&#34;depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">2</span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/replication_depth/">Replication Depth</a></p> -<h5 id="contact-depth">Contact Depth</h5> -<p>Contact depth is calculated relative to the user&rsquo;s home place, having the user&rsquo;s facility at depth = 0. Places and contacts that are direct children the facility are on depth = 1, and so on.</p> -<p>For example, considering the following contact hierarchy:</p> -<pre tabindex="0"><code>district &gt; health_center &gt; clinic &gt; area &gt; family &gt; patient -</code></pre><p>a <code>supervisor</code> at <code>health_center</code> level, and the following configuration:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">&#34;replication_depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[{</span> <span style="color:#204a87;font-weight:bold">&#34;role&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;supervisor_role&#34;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">&#34;depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#a40000">&lt;variable&gt;</span> <span style="color:#000;font-weight:bold">}]}</span> -</span></span></code></pre></div><p>The <code>supervisor</code> would download the following contacts and reports, depending on condigured depth:</p> -<table> -<thead> -<tr> -<th>Documents</th> -<th>depth=0</th> -<th>depth=1</th> -<th>depth=2</th> -<th>depth=3</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>health_center</code></td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -</tr> -<tr> -<td>all reports about <code>health_center</code></td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -</tr> -<tr> -<td>person children of <code>health_center</code></td> -<td>❌</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -</tr> -<tr> -<td>reports about person children of <code>health_center</code></td> -<td>❌</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -</tr> -<tr> -<td><code>clinic</code></td> -<td>❌</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -</tr> -<tr> -<td>all reports about <code>clinic</code></td> -<td>❌</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -</tr> -<tr> -<td>person children of <code>clinic</code></td> -<td>❌</td> -<td>❌</td> -<td>✔</td> -<td>✔</td> -</tr> -<tr> -<td>reports about person children of <code>clinic</code></td> -<td>❌</td> -<td>❌</td> -<td>✔</td> -<td>✔</td> -</tr> -<tr> -<td><code>family</code></td> -<td>❌</td> -<td>❌</td> -<td>✔</td> -<td>✔</td> -</tr> -<tr> -<td>all reports about <code>family</code></td> -<td>❌</td> -<td>❌</td> -<td>✔</td> -<td>✔</td> -</tr> -<tr> -<td>person children of <code>family</code></td> -<td>❌</td> -<td>❌</td> -<td>❌</td> -<td>✔</td> -</tr> -<tr> -<td>reports about person children of <code>family</code></td> -<td>❌</td> -<td>❌</td> -<td>❌</td> -<td>✔</td> -</tr> -</tbody> -</table> -<h5 id="report-depth">Report Depth</h5> -<p>As of 3.10, <code>report_depth</code> works similarly to <code>depth</code>, but it&rsquo;s applied to reports (documents with <code>&quot;type&quot;: &quot;data_record&quot;</code>) alone, excepting reports that are submitted by the authenticated user. -It only works in conjunction with <code>depth</code>.</p> -<p>Following the examples above:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">&#34;replication_depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[{</span> <span style="color:#204a87;font-weight:bold">&#34;role&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;supervisor_role&#34;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">&#34;depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#a40000">&lt;variable&gt;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">&#34;report_depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#a40000">&lt;variable&gt;</span> <span style="color:#000;font-weight:bold">}]}</span> -</span></span></code></pre></div><p>The <code>supervisor</code> would download the following contacts and reports:</p> -<table> -<thead> -<tr> -<th>Documents</th> -<th>depth=0</th> -<th>depth=1 report_depth=0</th> -<th>depth=2 report_depth=0</th> -<th>depth=2 report_depth=1</th> -<th>depth=2 report_depth=0</th> -<th>depth=3 report_depth=1</th> -<th>depth=3 report_depth=2</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>health_center</code></td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -</tr> -<tr> -<td>all reports about <code>health_center</code></td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -</tr> -<tr> -<td>children of <code>health_center</code></td> -<td>❌</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -</tr> -<tr> -<td>reports about children of <code>health_center</code> submitted by <code>supervisor</code></td> -<td>❌</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -</tr> -<tr> -<td>reports about children of <code>health_center</code> submitted by other users</td> -<td>❌</td> -<td>❌</td> -<td>❌</td> -<td>✔</td> -<td>❌</td> -<td>✔</td> -<td>✔</td> -</tr> -<tr> -<td><code>clinic</code></td> -<td>❌</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -</tr> -<tr> -<td>reports about <code>clinic</code> submitted by <code>supervisor</code></td> -<td>❌</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -</tr> -<tr> -<td>reports about <code>clinic</code> submitted by other users</td> -<td>❌</td> -<td>❌</td> -<td>❌</td> -<td>✔</td> -<td>❌</td> -<td>✔</td> -<td>✔</td> -</tr> -</tbody> -</table> -<table> -<thead> -<tr> -<th>Documents</th> -<th>depth=0</th> -<th>depth=1 report_depth=0</th> -<th>depth=2 report_depth=0</th> -<th>depth=2 report_depth=1</th> -<th>depth=2 report_depth=0</th> -<th>depth=3 report_depth=1</th> -<th>depth=3 report_depth=2</th> -</tr> -</thead> -<tbody> -<tr> -<td>children of <code>clinic</code></td> -<td>❌</td> -<td>❌</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -</tr> -<tr> -<td>reports about children of <code>clinic</code> submitted by <code>supervisor</code></td> -<td>❌</td> -<td>❌</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -</tr> -<tr> -<td>reports about children of <code>clinic</code> submitted by other users</td> -<td>❌</td> -<td>❌</td> -<td>❌</td> -<td>❌</td> -<td>❌</td> -<td>❌</td> -<td>✔</td> -</tr> -<tr> -<td><code>family</code></td> -<td>❌</td> -<td>❌</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -</tr> -<tr> -<td>reports about <code>family</code> submitted by <code>supervisor</code></td> -<td>❌</td> -<td>❌</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -<td>✔</td> -</tr> -<tr> -<td>reports about <code>family</code> submitted by other users</td> -<td>❌</td> -<td>❌</td> -<td>❌</td> -<td>❌</td> -<td>❌</td> -<td>❌</td> -<td>✔</td> -</tr> -<tr> -<td>children of <code>family</code></td> -<td>❌</td> -<td>❌</td> -<td>❌</td> -<td>❌</td> -<td>❌</td> -<td>✔</td> -<td>✔</td> -</tr> -<tr> -<td>reports about children of <code>family</code> submitted by <code>supervisor</code></td> -<td>❌</td> -<td>❌</td> -<td>❌</td> -<td>❌</td> -<td>❌</td> -<td>✔</td> -<td>✔</td> -</tr> -<tr> -<td>reports about children of <code>family</code> submitted by other users</td> -<td>❌</td> -<td>❌</td> -<td>❌</td> -<td>❌</td> -<td>❌</td> -<td>❌</td> -<td>❌</td> -</tr> -</tbody> -</table> -<p>If <code>report_depth</code> is omitted, the user will have access to all reports about contacts they see.</p> -<p>If <code>report_depth</code> is higher than <code>depth</code>, it has no effect.</p> -<p>If <code>depth</code> is omitted, the rule is invalid and will be ignored.</p> -<p>If a user&rsquo;s roles match multiple <code>replication_depth</code> entries, the one with the highest <code>depth</code> is selected.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;replication_depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">&#34;role&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;role1&#34;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">&#34;depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">&#34;role&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;role2&#34;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">&#34;depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">2</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">&#34;role&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;role3&#34;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">&#34;depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">3</span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>A user with <code>roles: [&quot;role1&quot;, &quot;role2&quot;, &quot;role3&quot;]</code> will have a replication depth of 3.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;replication_depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">&#34;role&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;role1&#34;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">&#34;depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">&#34;report_depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">&#34;role&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;role2&#34;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">&#34;depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">5</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">&#34;report_depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">2</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">&#34;role&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;role3&#34;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">&#34;depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">&#34;report_depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">3</span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>A user with <code>roles: [&quot;role1&quot;, &quot;role2&quot;, &quot;role3&quot;]</code> will have a replication depth of 5 and report replication depth of 2.</p> -<h3 id="supervisor-signoff">Supervisor signoff</h3> -<p>Some reports need to be signed off by a supervisor even though that supervisor doesn&rsquo;t have the depth to see the patient the report is about. To achieve this the report needs a field named <code>needs_signoff</code> with a value of <code>true</code>.</p> -<h3 id="sensitive-documents">Sensitive documents</h3> -<p>We won&rsquo;t replicate reports that are <code>private</code> and are about the user when the sender is someone the user can&rsquo;t access. A report is private when it has a property <code>doc.fields.private</code> with a value of <code>true</code>. For example, if a supervisor submits a private report about one of their CHPs, that CHP won&rsquo;t be able to see it.</p>Apps: User telemetryhttps://docs.communityhealthtoolkit.org/apps/guides/performance/telemetry/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/performance/telemetry/ -<p><em>Introduced in v3.4.0</em></p> -<p>The app collects performance data on certain user actions which is then aggregated each day and replicated to the server. This can be used to evaluate the performance of the code and configuration and to evaluate where improvements can be made.</p> -<p>The aggregate doc for the previous day is created when the first telemetry item is recorded each day. This is stored in the <code>medic-user-&lt;username&gt;-meta</code> database on the device and replicated to the server when an internet connection is available. This user specific server db is then replicated into the <code>medic-users-meta</code> database which holds all aggregate telemetry docs for all users.</p> -<p>The aggregate docs&rsquo; IDs follow the pattern <code>telemetry-&lt;year&gt;-&lt;month&gt;-&lt;day&gt;-&lt;username&gt;-&lt;uuid&gt;</code>.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Versions prior to v3.12.0 do aggregate in a monthly basis instead of daily, and the aggregate docs&rsquo; IDs follow the pattern <code>telemetry-&lt;year&gt;-&lt;month&gt;-&lt;username&gt;-&lt;uuid&gt;</code>. -</div> -<h2 id="performance-data">Performance data</h2> -<p>Each aggregate data point has the following fields:</p> -<table> -<thead> -<tr> -<th>Field</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>sum</code></td> -<td>A sum of all the recorded times in milliseconds.</td> -</tr> -<tr> -<td><code>min</code></td> -<td>The smallest time recorded in milliseconds.</td> -</tr> -<tr> -<td><code>max</code></td> -<td>The largest time recorded in milliseconds.</td> -</tr> -<tr> -<td><code>count</code></td> -<td>The number of times recorded.</td> -</tr> -<tr> -<td><code>sumsqr</code></td> -<td>The sum of squares of the times recorded in milliseconds. This is useful to see the variance, for example, a lower sumsqr can be indicative of having data closer together.</td> -</tr> -</tbody> -</table> -<p>The CHT records the Apdex (Application Performance Index) that is an open standard for measuring performance of software applications. Its purpose is to convert measurements into insights about user satisfaction, by specifying a uniform way to analyze and report on the degree to which measured performance meets user expectations.</p> -<p>The Apdex level is <code>satisfied</code> when the duration is less than or equal to 3s; <code>tolerable</code> when the duration is more than 3s but less than or equal to 12s; <code>frustrated</code> when duration is more than 12s. The Apdex is recorded as a telemetry entry using the format: <code>&lt;telemetry_field&gt;:apdex:&lt;satisfied/tolerable/frustrated&gt;</code>, for example, if the telemetry is about boot time and it had a tolerable performance, the Apdex is recorded as <code>boot_time:apdex:tolerable</code>.</p> -<p>Find below the list of telemetry data recorded by CHT:</p> -<table> -<thead> -<tr> -<th>Field</th> -<th>Description</th> -<th>Apdex</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>boot_time</code></td> -<td>The overall boot time including loading the code, purging, and accessing the database.</td> -<td>Yes. Added in 4.7</td> -</tr> -<tr> -<td><code>boot_time:1:to_first_code_execution</code></td> -<td>The time between the page loading and the JavaScript starting to run.</td> -<td></td> -</tr> -<tr> -<td><code>boot_time:2:to_bootstrap</code></td> -<td>The time between JavaScript starting and the bootstrapping (purging, initial replication, etc) to complete.</td> -<td></td> -</tr> -<tr> -<td><code>boot_time:2_1:to_replication</code></td> -<td>The time it takes to complete initial replication. If initial replication was interrupted and retried, this value will be incorrect. Added in 3.14.</td> -<td></td> -</tr> -<tr> -<td><code>boot_time:2_2:to_purge</code></td> -<td>The time it takes to complete the purge. Added in 3.14 and removed in 4.3.</td> -<td></td> -</tr> -<tr> -<td><code>boot_time:2_3:to_purge_meta</code></td> -<td>The time it takes to complete the purge of local meta database. Added in 3.14.</td> -<td></td> -</tr> -<tr> -<td><code>boot_time:3:to_angular_bootstrap</code></td> -<td>The time between bootstrapping completing and the webapp being ready to use.</td> -<td></td> -</tr> -<tr> -<td><code>boot_time:4:to_db_warmed</code></td> -<td>The time between the webapp being ready to use and the database being ready to use. Added in 3.6 and removed in 3.8.</td> -<td></td> -</tr> -<tr> -<td><code>boot_time:purging:&lt;boolean&gt;</code></td> -<td><code>boot_time:purging:true</code> when purging ran successfully on device startup, <code>boot_time:purging:false</code> when purging did not run. Added in 3.14 and removed in 4.3.</td> -<td></td> -</tr> -<tr> -<td><code>boot_time:purging_failed</code></td> -<td>The purging failed when running on device startup. Added in 3.14 and removed in 4.3.</td> -<td></td> -</tr> -<tr> -<td><code>boot_time:purging_meta:&lt;boolean&gt;</code></td> -<td><code>boot_time:purging_meta:true</code> when purging of the local meta database ran successfully, <code>boot_time:purging_meta:false</code> when it did not run. Added in 3.14.</td> -<td></td> -</tr> -<tr> -<td><code>boot_time:purging_meta_failed</code></td> -<td>The purging of the local meta database failed. Added in 3.14.</td> -<td></td> -</tr> -<tr> -<td><code>contact_list:load</code></td> -<td>The time taken to load the list of contacts on the left hand side of the Contacts tab. Added in 4.7.</td> -<td>Yes. Added in 4.7</td> -</tr> -<tr> -<td><code>contact_list:query</code></td> -<td>The time taken to query the People tab on initial load, when searching or sorting, this metric covers from fetching the data to preparing the data before display. Added in 4.7.</td> -<td>Yes. Added in 4.7</td> -</tr> -<tr> -<td><code>enketo:reports:&lt;form&gt;:&lt;action&gt;:&lt;component&gt;</code></td> -<td>The time taken to fill in app forms that are opened from Reports Tab. The <code>action</code> can either be &ldquo;add&rdquo; or &ldquo;edit&rdquo;. The <code>component</code> is one of: &ldquo;render&rdquo; covers getting the form and rendering it on screen; &ldquo;user_edit_time&rdquo; is the time the user took to fill in and submit the form; or &ldquo;save&rdquo; is about converting the form into a report and saving it.</td> -<td>Yes, added for <code>render</code> and <code>save</code> actions. Added in 4.7</td> -</tr> -<tr> -<td><code>enketo:contacts:&lt;form&gt;:&lt;action&gt;:&lt;component&gt;</code></td> -<td>The time taken to fill contact forms and app forms that are opened from People Tab. The <code>action</code> can either be &ldquo;add&rdquo; or &ldquo;edit&rdquo;. The <code>component</code> is one of: &ldquo;render&rdquo; covers getting the form and rendering it on screen; &ldquo;user_edit_time&rdquo; is the time the user took to fill in and submit the form; or &ldquo;save&rdquo; is about converting the form into a report and saving it.</td> -<td>Yes, added for <code>render</code> and <code>save</code> actions. Added in 4.7</td> -</tr> -<tr> -<td><code>enketo:tasks:&lt;form&gt;:&lt;action&gt;:&lt;component&gt;</code></td> -<td>As above but for forms on the Tasks tab.</td> -<td>Yes, added for <code>render</code> and <code>save</code> actions. Added in 4.7</td> -</tr> -<tr> -<td><code>message_list:load</code></td> -<td>The time taken to load the list of messages on the left hand side of the Messages tab. Added in 4.7.</td> -<td>Yes. Added in 4.7</td> -</tr> -<tr> -<td><code>search:contacts</code></td> -<td>The time taken to list all contacts.</td> -<td></td> -</tr> -<tr> -<td><code>search:contacts:&lt;filter[:filter]&gt;</code></td> -<td>The time taken to search all contacts using the given filters.</td> -<td></td> -</tr> -<tr> -<td><code>search:reports</code></td> -<td>The time taken to list all reports.</td> -<td></td> -</tr> -<tr> -<td><code>search:reports:&lt;filter[:filter]&gt;</code></td> -<td>The time taken to search all reports using the given filters.</td> -<td></td> -</tr> -<tr> -<td><code>contact_detail:&lt;contact_type&gt;:load</code></td> -<td>On the People tab, the time taken to load a contact, from the time the contact was selected to the time all content for that contact (contact summary, condition cards, reports, tasks, etc&hellip;) has fully loaded on the screen. Added in 4.7.</td> -<td>Yes. Added in 4.7</td> -</tr> -<tr> -<td><code>contact_detail:&lt;_form&gt;:load:contact_data</code></td> -<td>The time taken to load a contact&rsquo;s data. Added in 4.7.</td> -<td></td> -</tr> -<tr> -<td><code>contact_detail:&lt;_form&gt;:load:load_descendants</code></td> -<td>The time taken to load a contact&rsquo;s descendants, that is places and contacts under the contact hierarchy level. Added in 4.7.</td> -<td></td> -</tr> -<tr> -<td><code>contact_detail:&lt;_form&gt;:load:load_reports</code></td> -<td>The time taken to load a contact&rsquo;s associated reports. Added in 4.7.</td> -<td></td> -</tr> -<tr> -<td><code>contact_detail:&lt;_form&gt;:load:load_targets</code></td> -<td>The time taken to load a contact&rsquo;s targets. Added in 4.7.</td> -<td></td> -</tr> -<tr> -<td><code>contact_detail:&lt;_form&gt;:load:load_tasks</code></td> -<td>The time taken to load a contact&rsquo;s tasks. Added in 4.7.</td> -<td></td> -</tr> -<tr> -<td><code>contact_detail:&lt;_form&gt;:load:load_contact_summary</code></td> -<td>The time taken to load a contact&rsquo;s contact-summary. Added in 4.7.</td> -<td></td> -</tr> -<tr> -<td><code>report_detail:&lt;form&gt;:load</code></td> -<td>On the Reports tab, the time taken to load a report from the point it was selected on the left hand side to the time it was fully rendered. Added in 4.7.</td> -<td>Yes. Added in 4.7</td> -</tr> -<tr> -<td><code>messages_detail:load</code></td> -<td>On the Messages tab, the time taken to load the messages detail on the right hand side once has been selected from the list on the left hand side. Added in 4.7.</td> -<td>Yes. Added in 4.7</td> -</tr> -<tr> -<td><code>sidebar_filter:reports:open</code></td> -<td>Number of times the user opens the sidebar filter in Reports tab.</td> -<td></td> -</tr> -<tr> -<td><code>client-date-offset</code></td> -<td>The difference between the client datetime and the server datetime. Only recorded if the difference is large enough that it may cause issues.</td> -<td></td> -</tr> -<tr> -<td><code>analytics:targets:load</code></td> -<td>The time taken to load the targets page. Added in 3.9</td> -<td>Yes. Added in 4.7</td> -</tr> -<tr> -<td><code>analytics:target_aggregates:load</code></td> -<td>The time taken to load the target aggregates. Added in 4.7.</td> -<td>Yes. Added in 4.7</td> -</tr> -<tr> -<td><code>tasks:load</code></td> -<td>The time taken to load the tasks page. Added in 3.9</td> -<td>Yes. Added in 4.7</td> -</tr> -<tr> -<td><code>tasks:refresh</code></td> -<td>The time taken to refresh tasks on the tasks page. Added in 3.9</td> -<td>Yes. Added in 4.7</td> -</tr> -<tr> -<td><code>report_list:load</code></td> -<td>On the Reports tab, the time taken to load the list of reports on the left hand side. Added in 4.7.</td> -<td>Yes. Added in 4.7</td> -</tr> -<tr> -<td><code>report_list:query</code></td> -<td>The time taken to query the Reports tab on initial load, when searching or filtering, this metric covers from fetching the data to preparing the data before display. Added in 4.7.</td> -<td>Yes. Added in 4.7</td> -</tr> -<tr> -<td><code>rules-engine:initialize</code></td> -<td>The time taken to initialize the rules-engine . Added in 3.9</td> -<td></td> -</tr> -<tr> -<td><code>rules-engine:update-emissions</code></td> -<td>The time taken to update emissions in the rules-engine, when receiving a change. Added in 3.9</td> -<td></td> -</tr> -<tr> -<td><code>rules-engine:tasks:all-contacts</code></td> -<td>The time taken to fetch tasks for all contacts in rules-engine. Added in 3.9</td> -<td></td> -</tr> -<tr> -<td><code>rules-engine:tasks:some-contacts</code></td> -<td>The time taken to fetch tasks for some specific contacts in rules-engine. Added in 3.9</td> -<td></td> -</tr> -<tr> -<td><code>rules-engine:targets</code></td> -<td>Time taken for the rules-engine to fetch targets. Added in 3.9</td> -<td></td> -</tr> -<tr> -<td><code>rules-engine:targets:dirty-contacts</code></td> -<td>Number of &ldquo;dirty&rdquo; contacts[1] when fetching targets in the rules-engine. Added in 3.9</td> -<td></td> -</tr> -<tr> -<td><code>rules-engine:tasks:dirty-contacts</code></td> -<td>Number of &ldquo;dirty&rdquo; contacts[1] when fetching tasks in the rules-engine. Added in 3.9</td> -<td></td> -</tr> -<tr> -<td><code>rules-engine:ensureTaskFreshness:cancel</code></td> -<td>The time taken to cancel the automated task freshness thread in the rules-engine. This event is only recorded when the thread is cancelled before executing the refresh. Added in 3.9</td> -<td></td> -</tr> -<tr> -<td><code>rules-engine:ensureTargetFreshness:cancel</code></td> -<td>The time taken to cancel the automated target freshness thread in the rules-engine. This event is only recorded when the thread is cancelled before executing the refresh. Added in 3.9</td> -<td></td> -</tr> -<tr> -<td><code>rules-engine:tasks-breakdown:some-contacts</code></td> -<td>The time taken to fetch the tasks breakdown by status for some contacts. Added in 3.13.</td> -<td></td> -</tr> -<tr> -<td><code>replication:user-initiated</code></td> -<td>Number of times the user clicked &ldquo;Sync now&rdquo;. Added in 3.12.</td> -<td></td> -</tr> -<tr> -<td><code>replication:&lt;database&gt;:&lt;direction&gt;:success</code></td> -<td>Time taken to replicate, when replication was successful. Added in 3.12.</td> -<td></td> -</tr> -<tr> -<td><code>replication:&lt;database&gt;:&lt;direction&gt;:failure</code></td> -<td>Time taken to replicate, when replication failed. Added in 3.12.</td> -<td></td> -</tr> -<tr> -<td><code>replication:&lt;database&gt;:&lt;direction&gt;:failure:reason:offline:client</code></td> -<td>Number of times replication failed because of connection errors, and the app detects the client is offline. Added in 3.12.</td> -<td></td> -</tr> -<tr> -<td><code>replication:&lt;database&gt;:&lt;direction&gt;:failure:reason:offline:server</code></td> -<td>Number of times replication failed because of connection errors, and the app detects the client is online. Added in 3.12.</td> -<td></td> -</tr> -<tr> -<td><code>replication:&lt;database&gt;:&lt;direction&gt;:failure:reason:error</code></td> -<td>Number of times replication failed because of errors other than connection errors. Added in 3.12.</td> -<td></td> -</tr> -<tr> -<td><code>replication:&lt;database&gt;:&lt;direction&gt;:docs</code></td> -<td>Number of replicated docs. For <code>medic</code> replication, stores number of &ldquo;read&rdquo; docs, for <code>meta</code> replication, stores sum of read docs for every direction. Added in 3.12.</td> -<td></td> -</tr> -<tr> -<td><code>replication:medic:&lt;direction&gt;:ms-since-last-replicated-date</code></td> -<td>Time between a replication attempt and the last successful replication. Only recorded for medic database, every time replication is attempted. Added in 3.12.</td> -<td></td> -</tr> -<tr> -<td><code>replication:medic:&lt;direction&gt;:denied</code></td> -<td>Number of times replication was denied[2]. Added in 3.12.</td> -<td></td> -</tr> -<tr> -<td><code>tasks:group:all-tasks</code></td> -<td>Total number of tasks for the household (includes all statuses), when displaying household tasks page. Added in 3.13.</td> -<td></td> -</tr> -<tr> -<td><code>tasks:group:cancelled</code></td> -<td>Number of cancelled tasks for the household, when displaying household tasks page. Added in 3.13.</td> -<td></td> -</tr> -<tr> -<td><code>tasks:group:ready</code></td> -<td>Number of tasks in &ldquo;Ready&rdquo; state for the household, when displaying household tasks page. Added in 3.13.</td> -<td></td> -</tr> -<tr> -<td><code>tasks:group:ready:&lt;task_title&gt;</code></td> -<td>Breakdown of &ldquo;Ready&rdquo; tasks by <code>task_title</code> for the household, when displaying household tasks page. Added in 3.13.</td> -<td></td> -</tr> -<tr> -<td><code>tasks:group:modal:confirm</code></td> -<td>Number times the user confirms navigation away from the household tasks page. Added in 3.13.</td> -<td></td> -</tr> -<tr> -<td><code>tasks:group:modal:reject</code></td> -<td>Number times the user rejects navigation away from the household tasks page. Added in 3.13.</td> -<td></td> -</tr> -<tr> -<td><code>user_settings:language:&lt;language_code&gt;</code></td> -<td>The selected language by the user, example: <code>user_settings:language:en</code>. Added in 3.14.</td> -<td></td> -</tr> -<tr> -<td><code>enketo:&lt;training-card&gt;:add:render</code></td> -<td>The time it took to render the training card. Added in 4.2.0</td> -<td></td> -</tr> -<tr> -<td><code>enketo:&lt;training-card&gt;:add:user_edit_time</code></td> -<td>The time the user took to complete the training card. Added in 4.2.0</td> -<td></td> -</tr> -<tr> -<td><code>enketo:&lt;training-card&gt;:add:save</code></td> -<td>The time it took to save the training card. Added in 4.2.0</td> -<td></td> -</tr> -<tr> -<td><code>enketo:&lt;training-card&gt;:add:quit</code></td> -<td>The time from when the training card was rendered to when the user quits the training. Added in 4.2.0</td> -<td></td> -</tr> -<tr> -<td><code>geolocation:success</code></td> -<td>A successful GPS response with the value showing the accuracy.</td> -<td></td> -</tr> -<tr> -<td><code>geolocation:failure:&lt;x&gt;</code></td> -<td>An unsuccessful GPS response. <code>x</code> is a constant matching the <a href="https://developer.mozilla.org/en-US/docs/Web/API/GeolocationPositionError/code">GeolocationPositionError</a> or with one of the following values: <code>-1</code> unknown failure, <code>-2</code> timeout, or <code>-3</code> geolocation services unavailable.</td> -<td></td> -</tr> -</tbody> -</table> -<p>[1] &ldquo;Dirty&rdquo; indicates that the contact&rsquo;s task documents are not up to date. They will be refreshed before being used. <br> -[2] Replication can be denied when the user doesn&rsquo;t have permissions to create a doc (hierarchy permissions) or when a doc fails a <code>validate_doc_update</code> check.</p> -<p>Unless otherwise specified, <code>database</code> and <code>direction</code> placeholders stand for any combination of:</p> -<table> -<thead> -<tr> -<th>database</th> -<th>direction</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>medic</code></td> -<td><code>from</code> or <code>to</code></td> -</tr> -<tr> -<td><code>meta</code></td> -<td><code>sync</code></td> -</tr> -</tbody> -</table> -<h2 id="metadata">Metadata</h2> -<p>When the aggregate doc is created the Telemetry service also includes a snapshot of some metadata.</p> -<table> -<thead> -<tr> -<th>Field</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>year</code></td> -<td>The year the data was collected. Added in 3.4.0.</td> -</tr> -<tr> -<td><code>month</code></td> -<td>The month the data was collected. Initially the month was 0 indexed (eg: 0=Jan, 1=Feb, &hellip;), but from 3.8.0 <a href="https://github.com/medic/cht-core/issues/5949">this bug was fixed</a> so it changed to 1 indexed (eg: 1=Jan, 2=Feb, &hellip;). Added in 3.4.0.</td> -</tr> -<tr> -<td><code>day</code></td> -<td>The day of the month the data was collected. Added in 3.12.0.</td> -</tr> -<tr> -<td><code>user</code></td> -<td>The username of the logged in user. Added in 3.4.0.</td> -</tr> -<tr> -<td><code>deviceId</code></td> -<td>A unique key for this device. Added in 3.4.0.</td> -</tr> -<tr> -<td><code>versions.app</code></td> -<td>The version of the webapp. Added in 3.5.0.</td> -</tr> -<tr> -<td><code>versions.forms.&lt;form&gt;</code></td> -<td>The version of each form. Added in 3.5.0.</td> -</tr> -<tr> -<td><code>userAgent</code></td> -<td>The userAgent string from the user&rsquo;s browser. Added in 3.4.0.</td> -</tr> -<tr> -<td><code>hardwareConcurrency</code></td> -<td>The number of cores reported from the browser. Added in 3.4.0.</td> -</tr> -<tr> -<td><code>screen.width</code></td> -<td>The width of the screen in pixels. Added in 3.4.0.</td> -</tr> -<tr> -<td><code>screen.height</code></td> -<td>The height of the screen in pixels. Added in 3.4.0.</td> -</tr> -<tr> -<td><code>deviceInfo.app.version</code></td> -<td>The version name of the Android app. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.</td> -</tr> -<tr> -<td><code>deviceInfo.app.packageName</code></td> -<td>The package name of the Android app. Only captured when running in the Android wrapper v0.9.0+. Added in 3.12.0.</td> -</tr> -<tr> -<td><code>deviceInfo.app.versionCode</code></td> -<td>The Internal <a href="https://developer.android.com/reference/android/R.styleable#AndroidManifest_versionCode">version code</a> of the Android app. Only captured when running in the Android wrapper v0.9.0+. Added in 3.12.0.</td> -</tr> -<tr> -<td><code>deviceInfo.software.androidVersion</code></td> -<td>The version of Android OS. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.</td> -</tr> -<tr> -<td><code>deviceInfo.software.osApiLevel</code></td> -<td>The API of the Android OS. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.</td> -</tr> -<tr> -<td><code>deviceInfo.software.osVersion</code></td> -<td>The version of Android OS (detailed). Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.</td> -</tr> -<tr> -<td><code>deviceInfo.hardware.device</code></td> -<td>The Android device name. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.</td> -</tr> -<tr> -<td><code>deviceInfo.hardware.model</code></td> -<td>The Android model name. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.</td> -</tr> -<tr> -<td><code>deviceInfo.hardware.manufacturer</code></td> -<td>The Android device manufacturer. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.</td> -</tr> -<tr> -<td><code>deviceInfo.hardware.hardware</code></td> -<td>The Android device hardware. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.</td> -</tr> -<tr> -<td><code>deviceInfo.hardware.cpuInfo</code></td> -<td>The Android device CPU information. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.</td> -</tr> -<tr> -<td><code>deviceInfo.storage.free</code></td> -<td>The available storage on the device. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.</td> -</tr> -<tr> -<td><code>deviceInfo.storage.total</code></td> -<td>The total storage on the device. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.</td> -</tr> -<tr> -<td><code>deviceInfo.ram.free</code></td> -<td>The available RAM on the device. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.</td> -</tr> -<tr> -<td><code>deviceInfo.ram.total</code></td> -<td>The total RAM on the device. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.</td> -</tr> -<tr> -<td><code>deviceInfo.ram.threshold</code></td> -<td>The level of RAM at which certain services will be killed by Android. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.</td> -</tr> -<tr> -<td><code>deviceInfo.network.downSpeed</code></td> -<td>The reported download speed of the network. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.</td> -</tr> -<tr> -<td><code>deviceInfo.network.upSpeed</code></td> -<td>The reported upload speed of the network. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.</td> -</tr> -<tr> -<td><code>dbInfo.doc_count</code></td> -<td>The number of docs in the local database. Added in 3.4.0.</td> -</tr> -<tr> -<td><code>dbInfo.update_seq</code></td> -<td>The update sequence of the local database. Added in 3.4.0.</td> -</tr> -<tr> -<td><code>dbInfo.idb_attachment_format</code></td> -<td>The format of database attachments. Added in 3.4.0.</td> -</tr> -<tr> -<td><code>dbInfo.db_name</code></td> -<td>The name of the local database. Added in 3.4.0.</td> -</tr> -<tr> -<td><code>dbInfo.auto_compaction</code></td> -<td>Whether or not auto compaction is set. Added in 3.4.0.</td> -</tr> -<tr> -<td><code>dbInfo.adapter</code></td> -<td>The database adapter being used. Added in 3.4.0.</td> -</tr> -</tbody> -</table> -<h2 id="export-data-to-json">Export data to JSON</h2> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Telemetry data can be viewed directly in your browser with <a href="https://couchdb.apache.org/fauxton-visual-guide/">Fauxton</a> at <code>https://{{CHT_INSTANCE_URL}}/_utils</code>, and navigating the <code>medic-users-meta</code> database. -</div> -<p>To export all telemetry in JSON for further analysis or visualization, first meet these prerequisites:</p> -<ol> -<li>Ensure that both <a href="https://nodejs.org/en/"><code>node</code></a> and <a href="https://www.npmjs.com/get-npm"><code>npm</code></a> are installed and that the needed <code>node</code> libraries are installed: <code>npm install inquirer pouchdb-core fs path minimist pouchdb-adapter-http</code></li> -<li>Get a current copy of the export script: <code>curl -s -o get_users_meta_docs.js https://raw.githubusercontent.com/medic/cht-core/master/scripts/get_users_meta_docs.js</code></li> -</ol> -<p>To export the data open a terminal in the folder where you want to save the export, and run this command:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>node get_users_meta_docs.js --mode batch --type telemetry https://USERNAME:PASSWORD@COUCHDB_SERVER:COUCHDB_PORT/medic-users-meta &gt; telemetry.json -</span></span></code></pre></div><p>For example, if your username is <code>admin</code>, your password is <code>pass</code>, your CouchDB server is <code>localhost</code> and your CouchDB port is <code>5984</code>, you would run:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>node get_users_meta_docs.js --mode batch --type telemetry https://admin:pass@localhost:5984/medic-users-meta &gt; telemetry.json -</span></span></code></pre></div><p>This will save a file named <code>telemetry.json</code> containing all the telemetry data in the current directory.</p> -<h2 id="code-samples">Code Samples</h2> -<h3 id="logged-in-from-the-browser">Logged in from the browser</h3> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;_id&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;telemetry-2020-5-medic-016304ab-7167-4c97-93bb-a6626ef6128d&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;_rev&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;1-90a94d76eb30ac47e2f498b80cf54cd1&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;type&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;telemetry&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;metrics&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;boot_time&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;sum&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">3879.43500012625</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;min&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">308.0550000304356</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;max&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1110.6599999475293</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;count&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">7</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;sumsqr&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">2616858.529000253</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;boot_time:1:to_first_code_execution&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;sum&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">2096.309999935329</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;min&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">141.66500000283122</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;max&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">558.6599999805912</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;count&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">8</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;sumsqr&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">672987.1618553334</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;boot_time:2:to_bootstrap&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;sum&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">628.5150001058355</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;min&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">70.50499995239079</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;max&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">129.88000002223998</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;count&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">7</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;sumsqr&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">58704.48214660012</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;boot_time:3:to_angular_bootstrap&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;sum&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1415.5550000723451</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;min&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">45.25000008288771</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;max&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">787.9549999488518</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;count&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">7</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;sumsqr&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">732805.5498113176</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;search:contacts:types&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;sum&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">3728.9899999741465</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;min&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">96.60000004805624</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;max&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1134.1099999845028</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;count&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">8</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;sumsqr&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">2939824.639961114</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;search:reports:subjectIds&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;sum&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">3825.224999745842</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;min&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">27.304999995976686</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;max&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">982.2900000144728</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;count&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">25</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;sumsqr&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1461182.0137715642</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;metadata&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;year&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">2020</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;month&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">5</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;day&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">20</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;user&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;medic&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;deviceId&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;016304ab-7167-4c97-93bb-a6626ef6128d&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;versions&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;app&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;master&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;forms&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;client_review&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2-51ad4bd1c86c18ea768196da8264147e&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;contact:clinic:create&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;16-8dbb4dde6f8f9067e9cbed5b44ce5fd0&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;contact:clinic:edit&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2-c1c955042db963a95470bdf891bcef6f&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;contact:district_hospital:create&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2-42b8b579b38ca6c2978c238e7d9c78fe&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;contact:district_hospital:edit&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2-4360bb0eb6f6ff863849a07adac7e851&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;contact:health_center:create&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2-c9b86820479f211c8e505eba150c5203&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;contact:health_center:edit&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2-19b3da5f9f146be07ec1854a372e374b&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;contact:person:create&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2-230d39c41682da720b83e4909b138c9b&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;contact:person:edit&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;6-75781492122157beb7c2451861970ece&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;death_report&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2-97cfa3cc24ee743dfea03c019d644bec&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;delivery&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2-adf37c0da7d5e58c8e4d27fc940f70e6&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;enroll&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;10-13322d437e301f3c436e3b989a3fd384&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;no_contact&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;4-0901afd126ec6c87fe952ca3c3a652ed&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;pnc_danger_sign_follow_up_baby&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2-12e8989f6774828bb10aa4b675bc43ff&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;pnc_danger_sign_follow_up_mother&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2-62f58b6e0ade10d70f15df73ddb6a025&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;pregnancy&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2-4fa3db96cae638c4cb041125c9970496&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;pregnancy_danger_sign&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2-49758bfd894fb5db6e719bff864f9da9&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;pregnancy_danger_sign_follow_up&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2-a9a2b6800d6f93e151a3a63359620976&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;pregnancy_facility_visit_reminder&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2-0e3deaf22966166d05bcf803615499c8&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;pregnancy_home_visit&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2-283eb323e61a7fc756bda61f9d9be382&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;referral_for_care&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2-627c6c21bd54e482133acc8188a71f8d&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;undo_death_report&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2-3a4ca8d678f0338d7044cb1bc35d103d&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;device&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;userAgent&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.106 Safari/537.36&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;hardwareConcurrency&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;screen&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;width&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1920</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;height&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1080</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;deviceInfo&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;dbInfo&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;db_name&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;medic&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;purge_seq&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;0-g1AAAAEzeJzLYWBg4MhgTmHgzcvPy09JdcjLz8gvLskBCjPlsQBJhgNA6v____ezEhnwqnsAUfefkLoFEHX7CalrgKibj1tdkgKQTLLHa2dSAkhNPX41DiA18XjVJDIkyUMUZAEAuYBi9g&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;update_seq&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;1456-g1AAAAE2eJzLYWBg4MhgTmHgzcvPy09JdcjLz8gvLskBCjMlMiTJ____PytxAg4FSQpAMskerGYhLjUOIDXxYDVTcKlJAKmpB6tZhENNHguQZGgAUkBl87MSl-JVtwCibn9WYjtedQcg6u7jdj9E3QOIuv9ZSQwMjDVZAFZfZ6g&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;sizes&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;file&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">67196510</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;external&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">65800620</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;active&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">65357308</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;other&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;data_size&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">65800620</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;doc_del_count&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">41</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;doc_count&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">497</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;disk_size&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">67196510</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;disk_format_version&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">7</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;data_size&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">65357308</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;compact_running&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;cluster&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;q&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">8</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;n&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;w&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;r&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;instance_start_time&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;0&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;host&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;http://localhost:5988/medic/&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;auto_compaction&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;adapter&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;http&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div> \ No newline at end of file +Tracking and Improving Performance on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/guides/performance/Recent content in Tracking and Improving Performance on Community Health ToolkitHugo -- gohugo.ioenPurginghttps://docs.communityhealthtoolkit.org/apps/guides/performance/purging/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/performance/purging/Only available in 3.7.0 and above +Purging is a tool that allows you to increase performance and available disk space for offline users (eg CHWs) by removing unneeded documents from their device. +As users continually generate new reports their performance may naturally degrade as a result. You can use purging to remove older documents that are no longer relevant from their devices. Purging only removes documents from user&rsquo;s devices: these reports are still available for online analytics and impact metrics.CouchDB replicationhttps://docs.communityhealthtoolkit.org/apps/guides/performance/replication/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/performance/replication/Replication is what we call it when users download a copy of the data on to their device. +Restricting replication If the user has an online role they can access all the data, otherwise they will get restricted access to the data. +Restriction by place The most common restriction is by place. This is where we check the user&rsquo;s facility_id property, and allow access to all contacts that are descendants of that place, and all reports and messages that are about one of those descendants.User telemetryhttps://docs.communityhealthtoolkit.org/apps/guides/performance/telemetry/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/performance/telemetry/Introduced in v3.4.0 +The app collects performance data on certain user actions which is then aggregated each day and replicated to the server. This can be used to evaluate the performance of the code and configuration and to evaluate where improvements can be made. +The aggregate doc for the previous day is created when the first telemetry item is recorded each day. This is stored in the medic-user-&lt;username&gt;-meta database on the device and replicated to the server when an internet connection is available. \ No newline at end of file diff --git a/apps/guides/performance/purging/index.html b/apps/guides/performance/purging/index.html index b3b2d00917..0de90395af 100644 --- a/apps/guides/performance/purging/index.html +++ b/apps/guides/performance/purging/index.html @@ -1,9 +1,9 @@ -Purging | Community Health Toolkit +Purging | Community Health Toolkit

    Purging

    Remove unneeded documents from offline users devices.

    Only available in 3.7.0 and above

    Purging is a tool that allows you to increase performance and available disk space for offline users (eg CHWs) by removing unneeded documents from their device.

    As users continually generate new reports their performance may naturally degrade as a result. You can use purging to remove older documents that are no longer relevant from their devices. Purging only removes documents from user’s devices: these reports are still available for online analytics and impact metrics.

    Purging is disabled by default, and is enabled if a purge function is specified in app_settings.json, along with a run schedule.

    The following example would purge all reports that were created more than a year ago:

    Purging

    Remove unneeded documents from offline users devices.

    Only available in 3.7.0 and above

    Purging is a tool that allows you to increase performance and available disk space for offline users (eg CHWs) by removing unneeded documents from their device.

    As users continually generate new reports their performance may naturally degrade as a result. You can use purging to remove older documents that are no longer relevant from their devices. Purging only removes documents from user’s devices: these reports are still available for online analytics and impact metrics.

    Purging is disabled by default, and is enabled if a purge function is specified in app_settings.json, along with a run schedule.

    The following example would purge all reports that were created more than a year ago:

    {
       "//": "other app_settings settings",
       "purge": {
         "fn": "function(userCtx, contact, reports, messages, chtScriptApi, permissions) { const old = Date.now() - (1000 * 60 * 60 * 24 * 365); return reports.filter(r => r.reported_date < old).map(r => r._id);}",
    @@ -353,7 +353,8 @@
     Reference >
     app_settings.json >
     .patient_reports

    Patient Reports: Defining SMS workflows with schedules, registration, and patient reports.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/performance/replication/index.html b/apps/guides/performance/replication/index.html index 02d39c436a..919505d64f 100644 --- a/apps/guides/performance/replication/index.html +++ b/apps/guides/performance/replication/index.html @@ -1,9 +1,9 @@ -CouchDB replication | Community Health Toolkit +CouchDB replication | Community Health Toolkit

    CouchDB replication

    Settings for downloading copies of data onto a user’s device.

    Replication is what we call it when users download a copy of the data on to their device.

    Restricting replication

    If the user has an online role they can access all the data, otherwise they will get restricted access to the data.

    Restriction by place

    The most common restriction is by place. This is where we check the user’s facility_id property, and allow access to all contacts that are descendants of that place, and all reports and messages that are about one of those descendants.

    For example, if a CHP’s facility_id property is set to the ID of the Maori Hill clinic, then they will be able to see all patients and all reports about patients at that clinic.

    Depth

    Sometimes though you want to only access contacts near the top of the hierarchy. This may be because returning all contacts would be too much data to be practical, or for patient privacy, or because it’s just not part of your workflow. In this case you can configure a replication depth for a specific role under replication_depth in the app settings.

    In 3.10, we add the ability to limit replication depth for reports, in conjunction with replication depth for contacts.

    Code sample:

    CouchDB replication

    Settings for downloading copies of data onto a user’s device.

    Replication is what we call it when users download a copy of the data on to their device.

    Restricting replication

    If the user has an online role they can access all the data, otherwise they will get restricted access to the data.

    Restriction by place

    The most common restriction is by place. This is where we check the user’s facility_id property, and allow access to all contacts that are descendants of that place, and all reports and messages that are about one of those descendants.

    For example, if a CHP’s facility_id property is set to the ID of the Maori Hill clinic, then they will be able to see all patients and all reports about patients at that clinic.

    Depth

    Sometimes though you want to only access contacts near the top of the hierarchy. This may be because returning all contacts would be too much data to be practical, or for patient privacy, or because it’s just not part of your workflow. In this case you can configure a replication depth for a specific role under replication_depth in the app settings.

    In 3.10, we add the ability to limit replication depth for reports, in conjunction with replication depth for contacts.

    Code sample:
    {
       "replication_depth": [
         { "role": "district_manager", "depth": 1, "report_depth": 1 },
         { "role": "national_manager", "depth": 2 }
    @@ -331,7 +331,8 @@
     Quick Guides >
     Debugging >
     Production Data

    How to copy data from an instance to a local CouchDB database and app

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/performance/telemetry/index.html b/apps/guides/performance/telemetry/index.html index 102098024a..7417fb9332 100644 --- a/apps/guides/performance/telemetry/index.html +++ b/apps/guides/performance/telemetry/index.html @@ -1,9 +1,9 @@ -User telemetry | Community Health Toolkit +User telemetry | Community Health Toolkit

    User telemetry

    Performance data of certain user actions

    Introduced in v3.4.0

    The app collects performance data on certain user actions which is then aggregated each day and replicated to the server. This can be used to evaluate the performance of the code and configuration and to evaluate where improvements can be made.

    The aggregate doc for the previous day is created when the first telemetry item is recorded each day. This is stored in the medic-user-<username>-meta database on the device and replicated to the server when an internet connection is available. This user specific server db is then replicated into the medic-users-meta database which holds all aggregate telemetry docs for all users.

    The aggregate docs’ IDs follow the pattern telemetry-<year>-<month>-<day>-<username>-<uuid>.

    Performance data

    Each aggregate data point has the following fields:

    FieldDescription
    sumA sum of all the recorded times in milliseconds.
    minThe smallest time recorded in milliseconds.
    maxThe largest time recorded in milliseconds.
    countThe number of times recorded.
    sumsqrThe sum of squares of the times recorded in milliseconds. This is useful to see the variance, for example, a lower sumsqr can be indicative of having data closer together.

    The CHT records the Apdex (Application Performance Index) that is an open standard for measuring performance of software applications. Its purpose is to convert measurements into insights about user satisfaction, by specifying a uniform way to analyze and report on the degree to which measured performance meets user expectations.

    The Apdex level is satisfied when the duration is less than or equal to 3s; tolerable when the duration is more than 3s but less than or equal to 12s; frustrated when duration is more than 12s. The Apdex is recorded as a telemetry entry using the format: <telemetry_field>:apdex:<satisfied/tolerable/frustrated>, for example, if the telemetry is about boot time and it had a tolerable performance, the Apdex is recorded as boot_time:apdex:tolerable.

    Find below the list of telemetry data recorded by CHT:

    FieldDescriptionApdex
    boot_timeThe overall boot time including loading the code, purging, and accessing the database.Yes. Added in 4.7
    boot_time:1:to_first_code_executionThe time between the page loading and the JavaScript starting to run.
    boot_time:2:to_bootstrapThe time between JavaScript starting and the bootstrapping (purging, initial replication, etc) to complete.
    boot_time:2_1:to_replicationThe time it takes to complete initial replication. If initial replication was interrupted and retried, this value will be incorrect. Added in 3.14.
    boot_time:2_2:to_purgeThe time it takes to complete the purge. Added in 3.14 and removed in 4.3.
    boot_time:2_3:to_purge_metaThe time it takes to complete the purge of local meta database. Added in 3.14.
    boot_time:3:to_angular_bootstrapThe time between bootstrapping completing and the webapp being ready to use.
    boot_time:4:to_db_warmedThe time between the webapp being ready to use and the database being ready to use. Added in 3.6 and removed in 3.8.
    boot_time:purging:<boolean>boot_time:purging:true when purging ran successfully on device startup, boot_time:purging:false when purging did not run. Added in 3.14 and removed in 4.3.
    boot_time:purging_failedThe purging failed when running on device startup. Added in 3.14 and removed in 4.3.
    boot_time:purging_meta:<boolean>boot_time:purging_meta:true when purging of the local meta database ran successfully, boot_time:purging_meta:false when it did not run. Added in 3.14.
    boot_time:purging_meta_failedThe purging of the local meta database failed. Added in 3.14.
    contact_list:loadThe time taken to load the list of contacts on the left hand side of the Contacts tab. Added in 4.7.Yes. Added in 4.7
    contact_list:queryThe time taken to query the People tab on initial load, when searching or sorting, this metric covers from fetching the data to preparing the data before display. Added in 4.7.Yes. Added in 4.7
    enketo:reports:<form>:<action>:<component>The time taken to fill in app forms that are opened from Reports Tab. The action can either be “add” or “edit”. The component is one of: “render” covers getting the form and rendering it on screen; “user_edit_time” is the time the user took to fill in and submit the form; or “save” is about converting the form into a report and saving it.Yes, added for render and save actions. Added in 4.7
    enketo:contacts:<form>:<action>:<component>The time taken to fill contact forms and app forms that are opened from People Tab. The action can either be “add” or “edit”. The component is one of: “render” covers getting the form and rendering it on screen; “user_edit_time” is the time the user took to fill in and submit the form; or “save” is about converting the form into a report and saving it.Yes, added for render and save actions. Added in 4.7
    enketo:tasks:<form>:<action>:<component>As above but for forms on the Tasks tab.Yes, added for render and save actions. Added in 4.7
    message_list:loadThe time taken to load the list of messages on the left hand side of the Messages tab. Added in 4.7.Yes. Added in 4.7
    search:contactsThe time taken to list all contacts.
    search:contacts:<filter[:filter]>The time taken to search all contacts using the given filters.
    search:reportsThe time taken to list all reports.
    search:reports:<filter[:filter]>The time taken to search all reports using the given filters.
    contact_detail:<contact_type>:loadOn the People tab, the time taken to load a contact, from the time the contact was selected to the time all content for that contact (contact summary, condition cards, reports, tasks, etc…) has fully loaded on the screen. Added in 4.7.Yes. Added in 4.7
    contact_detail:<_form>:load:contact_dataThe time taken to load a contact’s data. Added in 4.7.
    contact_detail:<_form>:load:load_descendantsThe time taken to load a contact’s descendants, that is places and contacts under the contact hierarchy level. Added in 4.7.
    contact_detail:<_form>:load:load_reportsThe time taken to load a contact’s associated reports. Added in 4.7.
    contact_detail:<_form>:load:load_targetsThe time taken to load a contact’s targets. Added in 4.7.
    contact_detail:<_form>:load:load_tasksThe time taken to load a contact’s tasks. Added in 4.7.
    contact_detail:<_form>:load:load_contact_summaryThe time taken to load a contact’s contact-summary. Added in 4.7.
    report_detail:<form>:loadOn the Reports tab, the time taken to load a report from the point it was selected on the left hand side to the time it was fully rendered. Added in 4.7.Yes. Added in 4.7
    messages_detail:loadOn the Messages tab, the time taken to load the messages detail on the right hand side once has been selected from the list on the left hand side. Added in 4.7.Yes. Added in 4.7
    sidebar_filter:reports:openNumber of times the user opens the sidebar filter in Reports tab.
    client-date-offsetThe difference between the client datetime and the server datetime. Only recorded if the difference is large enough that it may cause issues.
    analytics:targets:loadThe time taken to load the targets page. Added in 3.9Yes. Added in 4.7
    analytics:target_aggregates:loadThe time taken to load the target aggregates. Added in 4.7.Yes. Added in 4.7
    tasks:loadThe time taken to load the tasks page. Added in 3.9Yes. Added in 4.7
    tasks:refreshThe time taken to refresh tasks on the tasks page. Added in 3.9Yes. Added in 4.7
    report_list:loadOn the Reports tab, the time taken to load the list of reports on the left hand side. Added in 4.7.Yes. Added in 4.7
    report_list:queryThe time taken to query the Reports tab on initial load, when searching or filtering, this metric covers from fetching the data to preparing the data before display. Added in 4.7.Yes. Added in 4.7
    rules-engine:initializeThe time taken to initialize the rules-engine . Added in 3.9
    rules-engine:update-emissionsThe time taken to update emissions in the rules-engine, when receiving a change. Added in 3.9
    rules-engine:tasks:all-contactsThe time taken to fetch tasks for all contacts in rules-engine. Added in 3.9
    rules-engine:tasks:some-contactsThe time taken to fetch tasks for some specific contacts in rules-engine. Added in 3.9
    rules-engine:targetsTime taken for the rules-engine to fetch targets. Added in 3.9
    rules-engine:targets:dirty-contactsNumber of “dirty” contacts[1] when fetching targets in the rules-engine. Added in 3.9
    rules-engine:tasks:dirty-contactsNumber of “dirty” contacts[1] when fetching tasks in the rules-engine. Added in 3.9
    rules-engine:ensureTaskFreshness:cancelThe time taken to cancel the automated task freshness thread in the rules-engine. This event is only recorded when the thread is cancelled before executing the refresh. Added in 3.9
    rules-engine:ensureTargetFreshness:cancelThe time taken to cancel the automated target freshness thread in the rules-engine. This event is only recorded when the thread is cancelled before executing the refresh. Added in 3.9
    rules-engine:tasks-breakdown:some-contactsThe time taken to fetch the tasks breakdown by status for some contacts. Added in 3.13.
    replication:user-initiatedNumber of times the user clicked “Sync now”. Added in 3.12.
    replication:<database>:<direction>:successTime taken to replicate, when replication was successful. Added in 3.12.
    replication:<database>:<direction>:failureTime taken to replicate, when replication failed. Added in 3.12.
    replication:<database>:<direction>:failure:reason:offline:clientNumber of times replication failed because of connection errors, and the app detects the client is offline. Added in 3.12.
    replication:<database>:<direction>:failure:reason:offline:serverNumber of times replication failed because of connection errors, and the app detects the client is online. Added in 3.12.
    replication:<database>:<direction>:failure:reason:errorNumber of times replication failed because of errors other than connection errors. Added in 3.12.
    replication:<database>:<direction>:docsNumber of replicated docs. For medic replication, stores number of “read” docs, for meta replication, stores sum of read docs for every direction. Added in 3.12.
    replication:medic:<direction>:ms-since-last-replicated-dateTime between a replication attempt and the last successful replication. Only recorded for medic database, every time replication is attempted. Added in 3.12.
    replication:medic:<direction>:deniedNumber of times replication was denied[2]. Added in 3.12.
    tasks:group:all-tasksTotal number of tasks for the household (includes all statuses), when displaying household tasks page. Added in 3.13.
    tasks:group:cancelledNumber of cancelled tasks for the household, when displaying household tasks page. Added in 3.13.
    tasks:group:readyNumber of tasks in “Ready” state for the household, when displaying household tasks page. Added in 3.13.
    tasks:group:ready:<task_title>Breakdown of “Ready” tasks by task_title for the household, when displaying household tasks page. Added in 3.13.
    tasks:group:modal:confirmNumber times the user confirms navigation away from the household tasks page. Added in 3.13.
    tasks:group:modal:rejectNumber times the user rejects navigation away from the household tasks page. Added in 3.13.
    user_settings:language:<language_code>The selected language by the user, example: user_settings:language:en. Added in 3.14.
    enketo:<training-card>:add:renderThe time it took to render the training card. Added in 4.2.0
    enketo:<training-card>:add:user_edit_timeThe time the user took to complete the training card. Added in 4.2.0
    enketo:<training-card>:add:saveThe time it took to save the training card. Added in 4.2.0
    enketo:<training-card>:add:quitThe time from when the training card was rendered to when the user quits the training. Added in 4.2.0
    geolocation:successA successful GPS response with the value showing the accuracy.
    geolocation:failure:<x>An unsuccessful GPS response. x is a constant matching the GeolocationPositionError or with one of the following values: -1 unknown failure, -2 timeout, or -3 geolocation services unavailable.

    [1] “Dirty” indicates that the contact’s task documents are not up to date. They will be refreshed before being used.
    [2] Replication can be denied when the user doesn’t have permissions to create a doc (hierarchy permissions) or when a doc fails a validate_doc_update check.

    Unless otherwise specified, database and direction placeholders stand for any combination of:

    databasedirection
    medicfrom or to
    metasync

    Metadata

    When the aggregate doc is created the Telemetry service also includes a snapshot of some metadata.

    FieldDescription
    yearThe year the data was collected. Added in 3.4.0.
    monthThe month the data was collected. Initially the month was 0 indexed (eg: 0=Jan, 1=Feb, …), but from 3.8.0 this bug was fixed so it changed to 1 indexed (eg: 1=Jan, 2=Feb, …). Added in 3.4.0.
    dayThe day of the month the data was collected. Added in 3.12.0.
    userThe username of the logged in user. Added in 3.4.0.
    deviceIdA unique key for this device. Added in 3.4.0.
    versions.appThe version of the webapp. Added in 3.5.0.
    versions.forms.<form>The version of each form. Added in 3.5.0.
    userAgentThe userAgent string from the user’s browser. Added in 3.4.0.
    hardwareConcurrencyThe number of cores reported from the browser. Added in 3.4.0.
    screen.widthThe width of the screen in pixels. Added in 3.4.0.
    screen.heightThe height of the screen in pixels. Added in 3.4.0.
    deviceInfo.app.versionThe version name of the Android app. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.app.packageNameThe package name of the Android app. Only captured when running in the Android wrapper v0.9.0+. Added in 3.12.0.
    deviceInfo.app.versionCodeThe Internal version code of the Android app. Only captured when running in the Android wrapper v0.9.0+. Added in 3.12.0.
    deviceInfo.software.androidVersionThe version of Android OS. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.software.osApiLevelThe API of the Android OS. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.software.osVersionThe version of Android OS (detailed). Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.hardware.deviceThe Android device name. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.hardware.modelThe Android model name. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.hardware.manufacturerThe Android device manufacturer. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.hardware.hardwareThe Android device hardware. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.hardware.cpuInfoThe Android device CPU information. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.storage.freeThe available storage on the device. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.storage.totalThe total storage on the device. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.ram.freeThe available RAM on the device. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.ram.totalThe total RAM on the device. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.ram.thresholdThe level of RAM at which certain services will be killed by Android. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.network.downSpeedThe reported download speed of the network. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.network.upSpeedThe reported upload speed of the network. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    dbInfo.doc_countThe number of docs in the local database. Added in 3.4.0.
    dbInfo.update_seqThe update sequence of the local database. Added in 3.4.0.
    dbInfo.idb_attachment_formatThe format of database attachments. Added in 3.4.0.
    dbInfo.db_nameThe name of the local database. Added in 3.4.0.
    dbInfo.auto_compactionWhether or not auto compaction is set. Added in 3.4.0.
    dbInfo.adapterThe database adapter being used. Added in 3.4.0.

    Export data to JSON

    To export all telemetry in JSON for further analysis or visualization, first meet these prerequisites:

    1. Ensure that both node and npm are installed and that the needed node libraries are installed: npm install inquirer pouchdb-core fs path minimist pouchdb-adapter-http
    2. Get a current copy of the export script: curl -s -o get_users_meta_docs.js https://raw.githubusercontent.com/medic/cht-core/master/scripts/get_users_meta_docs.js

    To export the data open a terminal in the folder where you want to save the export, and run this command:

    node get_users_meta_docs.js --mode batch --type telemetry https://USERNAME:PASSWORD@COUCHDB_SERVER:COUCHDB_PORT/medic-users-meta > telemetry.json
    + Create project issue

    User telemetry

    Performance data of certain user actions

    Introduced in v3.4.0

    The app collects performance data on certain user actions which is then aggregated each day and replicated to the server. This can be used to evaluate the performance of the code and configuration and to evaluate where improvements can be made.

    The aggregate doc for the previous day is created when the first telemetry item is recorded each day. This is stored in the medic-user-<username>-meta database on the device and replicated to the server when an internet connection is available. This user specific server db is then replicated into the medic-users-meta database which holds all aggregate telemetry docs for all users.

    The aggregate docs’ IDs follow the pattern telemetry-<year>-<month>-<day>-<username>-<uuid>.

    Performance data

    Each aggregate data point has the following fields:

    FieldDescription
    sumA sum of all the recorded times in milliseconds.
    minThe smallest time recorded in milliseconds.
    maxThe largest time recorded in milliseconds.
    countThe number of times recorded.
    sumsqrThe sum of squares of the times recorded in milliseconds. This is useful to see the variance, for example, a lower sumsqr can be indicative of having data closer together.

    The CHT records the Apdex (Application Performance Index) that is an open standard for measuring performance of software applications. Its purpose is to convert measurements into insights about user satisfaction, by specifying a uniform way to analyze and report on the degree to which measured performance meets user expectations.

    The Apdex level is satisfied when the duration is less than or equal to 3s; tolerable when the duration is more than 3s but less than or equal to 12s; frustrated when duration is more than 12s. The Apdex is recorded as a telemetry entry using the format: <telemetry_field>:apdex:<satisfied/tolerable/frustrated>, for example, if the telemetry is about boot time and it had a tolerable performance, the Apdex is recorded as boot_time:apdex:tolerable.

    Find below the list of telemetry data recorded by CHT:

    FieldDescriptionApdex
    boot_timeThe overall boot time including loading the code, purging, and accessing the database.Yes. Added in 4.7
    boot_time:1:to_first_code_executionThe time between the page loading and the JavaScript starting to run.
    boot_time:2:to_bootstrapThe time between JavaScript starting and the bootstrapping (purging, initial replication, etc) to complete.
    boot_time:2_1:to_replicationThe time it takes to complete initial replication. If initial replication was interrupted and retried, this value will be incorrect. Added in 3.14.
    boot_time:2_2:to_purgeThe time it takes to complete the purge. Added in 3.14 and removed in 4.3.
    boot_time:2_3:to_purge_metaThe time it takes to complete the purge of local meta database. Added in 3.14.
    boot_time:3:to_angular_bootstrapThe time between bootstrapping completing and the webapp being ready to use.
    boot_time:4:to_db_warmedThe time between the webapp being ready to use and the database being ready to use. Added in 3.6 and removed in 3.8.
    boot_time:purging:<boolean>boot_time:purging:true when purging ran successfully on device startup, boot_time:purging:false when purging did not run. Added in 3.14 and removed in 4.3.
    boot_time:purging_failedThe purging failed when running on device startup. Added in 3.14 and removed in 4.3.
    boot_time:purging_meta:<boolean>boot_time:purging_meta:true when purging of the local meta database ran successfully, boot_time:purging_meta:false when it did not run. Added in 3.14.
    boot_time:purging_meta_failedThe purging of the local meta database failed. Added in 3.14.
    contact_list:loadThe time taken to load the list of contacts on the left hand side of the Contacts tab. Added in 4.7.Yes. Added in 4.7
    contact_list:queryThe time taken to query the People tab on initial load, when searching or sorting, this metric covers from fetching the data to preparing the data before display. Added in 4.7.Yes. Added in 4.7
    enketo:reports:<form>:<action>:<component>The time taken to fill in app forms that are opened from Reports Tab. The action can either be “add” or “edit”. The component is one of: “render” covers getting the form and rendering it on screen; “user_edit_time” is the time the user took to fill in and submit the form; or “save” is about converting the form into a report and saving it.Yes, added for render and save actions. Added in 4.7
    enketo:contacts:<form>:<action>:<component>The time taken to fill contact forms and app forms that are opened from People Tab. The action can either be “add” or “edit”. The component is one of: “render” covers getting the form and rendering it on screen; “user_edit_time” is the time the user took to fill in and submit the form; or “save” is about converting the form into a report and saving it.Yes, added for render and save actions. Added in 4.7
    enketo:tasks:<form>:<action>:<component>As above but for forms on the Tasks tab.Yes, added for render and save actions. Added in 4.7
    message_list:loadThe time taken to load the list of messages on the left hand side of the Messages tab. Added in 4.7.Yes. Added in 4.7
    search:contactsThe time taken to list all contacts.
    search:contacts:<filter[:filter]>The time taken to search all contacts using the given filters.
    search:reportsThe time taken to list all reports.
    search:reports:<filter[:filter]>The time taken to search all reports using the given filters.
    contact_detail:<contact_type>:loadOn the People tab, the time taken to load a contact, from the time the contact was selected to the time all content for that contact (contact summary, condition cards, reports, tasks, etc…) has fully loaded on the screen. Added in 4.7.Yes. Added in 4.7
    contact_detail:<_form>:load:contact_dataThe time taken to load a contact’s data. Added in 4.7.
    contact_detail:<_form>:load:load_descendantsThe time taken to load a contact’s descendants, that is places and contacts under the contact hierarchy level. Added in 4.7.
    contact_detail:<_form>:load:load_reportsThe time taken to load a contact’s associated reports. Added in 4.7.
    contact_detail:<_form>:load:load_targetsThe time taken to load a contact’s targets. Added in 4.7.
    contact_detail:<_form>:load:load_tasksThe time taken to load a contact’s tasks. Added in 4.7.
    contact_detail:<_form>:load:load_contact_summaryThe time taken to load a contact’s contact-summary. Added in 4.7.
    report_detail:<form>:loadOn the Reports tab, the time taken to load a report from the point it was selected on the left hand side to the time it was fully rendered. Added in 4.7.Yes. Added in 4.7
    messages_detail:loadOn the Messages tab, the time taken to load the messages detail on the right hand side once has been selected from the list on the left hand side. Added in 4.7.Yes. Added in 4.7
    sidebar_filter:reports:openNumber of times the user opens the sidebar filter in Reports tab.
    client-date-offsetThe difference between the client datetime and the server datetime. Only recorded if the difference is large enough that it may cause issues.
    analytics:targets:loadThe time taken to load the targets page. Added in 3.9Yes. Added in 4.7
    analytics:target_aggregates:loadThe time taken to load the target aggregates. Added in 4.7.Yes. Added in 4.7
    tasks:loadThe time taken to load the tasks page. Added in 3.9Yes. Added in 4.7
    tasks:refreshThe time taken to refresh tasks on the tasks page. Added in 3.9Yes. Added in 4.7
    report_list:loadOn the Reports tab, the time taken to load the list of reports on the left hand side. Added in 4.7.Yes. Added in 4.7
    report_list:queryThe time taken to query the Reports tab on initial load, when searching or filtering, this metric covers from fetching the data to preparing the data before display. Added in 4.7.Yes. Added in 4.7
    rules-engine:initializeThe time taken to initialize the rules-engine . Added in 3.9
    rules-engine:update-emissionsThe time taken to update emissions in the rules-engine, when receiving a change. Added in 3.9
    rules-engine:tasks:all-contactsThe time taken to fetch tasks for all contacts in rules-engine. Added in 3.9
    rules-engine:tasks:some-contactsThe time taken to fetch tasks for some specific contacts in rules-engine. Added in 3.9
    rules-engine:targetsTime taken for the rules-engine to fetch targets. Added in 3.9
    rules-engine:targets:dirty-contactsNumber of “dirty” contacts[1] when fetching targets in the rules-engine. Added in 3.9
    rules-engine:tasks:dirty-contactsNumber of “dirty” contacts[1] when fetching tasks in the rules-engine. Added in 3.9
    rules-engine:ensureTaskFreshness:cancelThe time taken to cancel the automated task freshness thread in the rules-engine. This event is only recorded when the thread is cancelled before executing the refresh. Added in 3.9
    rules-engine:ensureTargetFreshness:cancelThe time taken to cancel the automated target freshness thread in the rules-engine. This event is only recorded when the thread is cancelled before executing the refresh. Added in 3.9
    rules-engine:tasks-breakdown:some-contactsThe time taken to fetch the tasks breakdown by status for some contacts. Added in 3.13.
    replication:user-initiatedNumber of times the user clicked “Sync now”. Added in 3.12.
    replication:<database>:<direction>:successTime taken to replicate, when replication was successful. Added in 3.12.
    replication:<database>:<direction>:failureTime taken to replicate, when replication failed. Added in 3.12.
    replication:<database>:<direction>:failure:reason:offline:clientNumber of times replication failed because of connection errors, and the app detects the client is offline. Added in 3.12.
    replication:<database>:<direction>:failure:reason:offline:serverNumber of times replication failed because of connection errors, and the app detects the client is online. Added in 3.12.
    replication:<database>:<direction>:failure:reason:errorNumber of times replication failed because of errors other than connection errors. Added in 3.12.
    replication:<database>:<direction>:docsNumber of replicated docs. For medic replication, stores number of “read” docs, for meta replication, stores sum of read docs for every direction. Added in 3.12.
    replication:medic:<direction>:ms-since-last-replicated-dateTime between a replication attempt and the last successful replication. Only recorded for medic database, every time replication is attempted. Added in 3.12.
    replication:medic:<direction>:deniedNumber of times replication was denied[2]. Added in 3.12.
    tasks:group:all-tasksTotal number of tasks for the household (includes all statuses), when displaying household tasks page. Added in 3.13.
    tasks:group:cancelledNumber of cancelled tasks for the household, when displaying household tasks page. Added in 3.13.
    tasks:group:readyNumber of tasks in “Ready” state for the household, when displaying household tasks page. Added in 3.13.
    tasks:group:ready:<task_title>Breakdown of “Ready” tasks by task_title for the household, when displaying household tasks page. Added in 3.13.
    tasks:group:modal:confirmNumber times the user confirms navigation away from the household tasks page. Added in 3.13.
    tasks:group:modal:rejectNumber times the user rejects navigation away from the household tasks page. Added in 3.13.
    user_settings:language:<language_code>The selected language by the user, example: user_settings:language:en. Added in 3.14.
    enketo:<training-card>:add:renderThe time it took to render the training card. Added in 4.2.0
    enketo:<training-card>:add:user_edit_timeThe time the user took to complete the training card. Added in 4.2.0
    enketo:<training-card>:add:saveThe time it took to save the training card. Added in 4.2.0
    enketo:<training-card>:add:quitThe time from when the training card was rendered to when the user quits the training. Added in 4.2.0
    geolocation:successA successful GPS response with the value showing the accuracy.
    geolocation:failure:<x>An unsuccessful GPS response. x is a constant matching the GeolocationPositionError or with one of the following values: -1 unknown failure, -2 timeout, or -3 geolocation services unavailable.

    [1] “Dirty” indicates that the contact’s task documents are not up to date. They will be refreshed before being used.
    [2] Replication can be denied when the user doesn’t have permissions to create a doc (hierarchy permissions) or when a doc fails a validate_doc_update check.

    Unless otherwise specified, database and direction placeholders stand for any combination of:

    databasedirection
    medicfrom or to
    metasync

    Metadata

    When the aggregate doc is created the Telemetry service also includes a snapshot of some metadata.

    FieldDescription
    yearThe year the data was collected. Added in 3.4.0.
    monthThe month the data was collected. Initially the month was 0 indexed (eg: 0=Jan, 1=Feb, …), but from 3.8.0 this bug was fixed so it changed to 1 indexed (eg: 1=Jan, 2=Feb, …). Added in 3.4.0.
    dayThe day of the month the data was collected. Added in 3.12.0.
    userThe username of the logged in user. Added in 3.4.0.
    deviceIdA unique key for this device. Added in 3.4.0.
    versions.appThe version of the webapp. Added in 3.5.0.
    versions.forms.<form>The version of each form. Added in 3.5.0.
    userAgentThe userAgent string from the user’s browser. Added in 3.4.0.
    hardwareConcurrencyThe number of cores reported from the browser. Added in 3.4.0.
    screen.widthThe width of the screen in pixels. Added in 3.4.0.
    screen.heightThe height of the screen in pixels. Added in 3.4.0.
    deviceInfo.app.versionThe version name of the Android app. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.app.packageNameThe package name of the Android app. Only captured when running in the Android wrapper v0.9.0+. Added in 3.12.0.
    deviceInfo.app.versionCodeThe Internal version code of the Android app. Only captured when running in the Android wrapper v0.9.0+. Added in 3.12.0.
    deviceInfo.software.androidVersionThe version of Android OS. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.software.osApiLevelThe API of the Android OS. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.software.osVersionThe version of Android OS (detailed). Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.hardware.deviceThe Android device name. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.hardware.modelThe Android model name. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.hardware.manufacturerThe Android device manufacturer. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.hardware.hardwareThe Android device hardware. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.hardware.cpuInfoThe Android device CPU information. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.storage.freeThe available storage on the device. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.storage.totalThe total storage on the device. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.ram.freeThe available RAM on the device. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.ram.totalThe total RAM on the device. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.ram.thresholdThe level of RAM at which certain services will be killed by Android. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.network.downSpeedThe reported download speed of the network. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    deviceInfo.network.upSpeedThe reported upload speed of the network. Only captured when running in the Android wrapper v0.4.0+. Added in 3.8.0.
    dbInfo.doc_countThe number of docs in the local database. Added in 3.4.0.
    dbInfo.update_seqThe update sequence of the local database. Added in 3.4.0.
    dbInfo.idb_attachment_formatThe format of database attachments. Added in 3.4.0.
    dbInfo.db_nameThe name of the local database. Added in 3.4.0.
    dbInfo.auto_compactionWhether or not auto compaction is set. Added in 3.4.0.
    dbInfo.adapterThe database adapter being used. Added in 3.4.0.

    Export data to JSON

    To export all telemetry in JSON for further analysis or visualization, first meet these prerequisites:

    1. Ensure that both node and npm are installed and that the needed node libraries are installed: npm install inquirer pouchdb-core fs path minimist pouchdb-adapter-http
    2. Get a current copy of the export script: curl -s -o get_users_meta_docs.js https://raw.githubusercontent.com/medic/cht-core/master/scripts/get_users_meta_docs.js

    To export the data open a terminal in the folder where you want to save the export, and run this command:

    node get_users_meta_docs.js --mode batch --type telemetry https://USERNAME:PASSWORD@COUCHDB_SERVER:COUCHDB_PORT/medic-users-meta > telemetry.json
     

    For example, if your username is admin, your password is pass, your CouchDB server is localhost and your CouchDB port is 5984, you would run:

    node get_users_meta_docs.js --mode batch --type telemetry https://admin:pass@localhost:5984/medic-users-meta > telemetry.json
     

    This will save a file named telemetry.json containing all the telemetry data in the current directory.

    Code Samples

    Logged in from the browser

    {
       "_id": "telemetry-2020-5-medic-016304ab-7167-4c97-93bb-a6626ef6128d",
    @@ -426,7 +426,8 @@
     

    CHT Core Framework > Overview > Data Flows

    An overview of data flows in the CHT for analytics, impact monitoring, and data science

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/privacy/index.html b/apps/guides/privacy/index.html index 024db4cc88..d77058edc3 100644 --- a/apps/guides/privacy/index.html +++ b/apps/guides/privacy/index.html @@ -1,9 +1,9 @@ -Privacy | Community Health Toolkit +Privacy | Community Health Toolkit

    Privacy

    Policies and templates for privacy and responsible data use

    Adding Privacy Policies to Apps

    Guide for adding privacy policies that users must accept before being allowed to use the app

    Privacy & Data Protection Policy

    Medic’s Privacy and Data Protection Policy, which applies to the Community Health Toolkit

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/privacy/index.xml b/apps/guides/privacy/index.xml index 4f83f30be6..7a350b16a0 100644 --- a/apps/guides/privacy/index.xml +++ b/apps/guides/privacy/index.xml @@ -1,131 +1,4 @@ -Community Health Toolkit – Privacyhttps://docs.communityhealthtoolkit.org/apps/guides/privacy/Recent content in Privacy on Community Health ToolkitHugo -- gohugo.ioenApps: Adding Privacy Policies to Appshttps://docs.communityhealthtoolkit.org/apps/guides/privacy/privacy-policy/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/privacy/privacy-policy/ -<p>As of <code>3.10.0</code>, privacy policies can be customized for every language, by adding desired content into HTML files.</p> -<p>Privacy policies are now publicly accessible rather than only being available after logging in. This means it can be shared with third parties, for example, app store compliance. If your instance URL is <code>https://my-health-facility.org</code>, then the privacy policy is available at <code>https://my-health-facility.org/medic/privacy-policy</code>. <em>Added in 3.17.0</em>.</p> -<p><img src="privacy.policy.login.page.png" alt="Privacy Policy on login page"></p> -<p>Add these HTML files to the <code>privacy-policies</code> folder in your configuration. The <code>privacy-policies.json</code> file, which associates the HTML files with the correct language, should reside in the root of the project directory, not inside the <code>privacy_policies</code> folder.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;en&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;en.attachment.html&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;fr&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;fr.file.html&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;sw&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;swahili.html&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="adding-and-editing-a-privacy-policy">Adding and Editing a Privacy Policy</h3> -<p>There are two ways to add or edit a privacy policy:</p> -<ol> -<li>Build the privacy policies into the application with the <code>upload-privacy-policies</code> action in <code>cht-conf</code>.</li> -</ol> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cht --local upload-privacy-policies -</span></span></code></pre></div><ol start="2"> -<li>Update and view privacy policies in the <a href="https://docs.communityhealthtoolkit.org/apps/features/admin/">Admin Console</a>, under <code>Display</code> &gt; <code>Privacy Policies</code></li> -</ol> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -<ul> -<li>Use language codes when associating HTML files to languages</li> -<li>Files that are not in HTML format will be skipped</li> -<li>Files that are not associated with a language will not be uploaded</li> -<li>Before being displayed to users, privacy policies HTML is <a href="https://docs.angularjs.org/api/ngSanitize/service/$sanitize">sanitized</a>, which strips all unsafe elements and attributes. The admin console has a previewing feature that will display your privacy policy contents after being sanitized.</li> -<li>When displayed to users, privacy policy HTML will be styled by webapp CSS.</li> -</ul> -</div> -<h3 id="view-in-webapp">View In Webapp</h3> -<p>When a privacy policy is configured for a language, users who load the app in this language are prompted to accept the policy.</p> -<p>If our <code>en.attachment.html</code> file looked like this:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">html</span><span style="color:#000;font-weight:bold">&gt;&lt;</span><span style="color:#204a87;font-weight:bold">body</span><span style="color:#000;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">h1</span><span style="color:#000;font-weight:bold">&gt;</span>Lorem Ipsum: Privacy <span style="color:#a40000">&amp;</span> Data Protection Policy<span style="color:#000;font-weight:bold">&lt;/</span><span style="color:#204a87;font-weight:bold">h1</span><span style="color:#000;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">h2</span><span style="color:#000;font-weight:bold">&gt;</span>The standard Lorem Ipsum passage, used since the 1500s<span style="color:#000;font-weight:bold">&lt;/</span><span style="color:#204a87;font-weight:bold">h2</span><span style="color:#000;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">p</span><span style="color:#000;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">&lt;/</span><span style="color:#204a87;font-weight:bold">p</span><span style="color:#000;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">&lt;/</span><span style="color:#204a87;font-weight:bold">body</span><span style="color:#000;font-weight:bold">&gt;&lt;/</span><span style="color:#204a87;font-weight:bold">html</span><span style="color:#000;font-weight:bold">&gt;</span> -</span></span></code></pre></div><p>Then visotors the privacy policy would see:</p> -<p><img src="accept-mobile.png" alt="Accept privacy policy"></p> -<p>The app will load normally after acceptance. The user cannot opt out or skip acceptance, meaning, if a privacy policy is configured, the app will be unusable until the user has accepted it. Once accepted, users will not be prompted to accept the same policy again. However, users will be prompted for acceptance again when the policy is updated or when they change their language. An acceptance log is saved in the <code>user-settings</code> file and synced to the server, containing a history of privacy policies accepted by the user.</p>Apps: Privacy & Data Protection Policyhttps://docs.communityhealthtoolkit.org/apps/guides/privacy/policy/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/privacy/policy/ -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -This policy is reviewed and updated periodically by our Responsible Data Working Group and is intended as a resource for the CHT community. -</div> -<p>If you have any questions, please reach out to our Data Protection Officer at <a href="mailto:support@medic.org">support@medic.org</a>.</p> -<h2 id="table-of-contents">Table of Contents</h2> -<ul> -<li><a href="#table-of-contents">Table of Contents</a></li> -<li><a href="#our-responsible-data-promise">Our responsible data promise</a></li> -<li><a href="#compliance-with-applicable-data-regulations-and-policies">Compliance with applicable data regulations and policies</a></li> -<li><a href="#data-and-protected-health-information">Data and protected health information</a> -<ul> -<li><a href="#use-of-software-to-collect-and-process-data">Use of software to collect and process data</a></li> -<li><a href="#protected-health-information-policy">Protected health information policy</a></li> -</ul> -</li> -<li><a href="#security-practices">Security practices</a> -<ul> -<li><a href="#technology-security-overview">Technology security overview</a></li> -<li><a href="#security-training-for-our-partners">Security training for our Partners</a></li> -</ul> -</li> -<li><a href="#inquiries-with-medics-data-protection-officer">Inquiries with Medic’s Data Protection Officer</a></li> -</ul> -<h2 id="our-responsible-data-promise">Our responsible data promise</h2> -<p>As a non-profit organization, Medic Mobile (“Medic”)’s mission is to advance good health and human flourishing by building open source technology with and for hard-to-reach communities. We value humanity, creativity, initiative, solidarity, and openness. Being responsible stewards of people’s data is critically important to our mission.</p> -<p>We use the concept of Responsible Data (RD) to outline our collective duty to prioritise and respond to the ethical, legal, social, and privacy-related challenges that come from using data in new and different ways in healthcare, advocacy, and social change. We have an obligation to account for unintended consequences of working with data by:</p> -<ol> -<li>Prioritising people’s rights to consent, privacy, security and ownership when using data in social change and advocacy efforts</li> -<li>Implementing values and practices of transparency and openness</li> -</ol> -<p>As an organization, we promise to do our best to live up to these obligations. We promise to show initiative, to seek feedback, and to proactively seek out ways to work with data more responsibly. We promise to accompany patients, health workers, researchers, organizational partners, and our open source community in the journey from best intentions to best practices in data-driven projects. The purpose of Medic’s data policy is to help our organization deliver on this responsible data promise.</p> -<h2 id="compliance-with-applicable-data-regulations-and-policies">Compliance with applicable data regulations and policies</h2> -<p>Medic serves as a technical partner to governments and non-governmental organizations around the world. As a result, our projects comply with a range of country-specific and region-specific data protection regulations, and the details of compliance are addressed on a project-by-project basis. Adhering to all data regulations that are relevant to a given project is a central element of Medic’s global privacy and data protection policy.</p> -<p>In most cases, the Ministries of Health, other government agencies, and non-governmental organizations that Medic supports (Medic “Partner(s)”) serve as data controlling entities, and they work with Medic as a business associate. Partners establish additional data handling policies and standard operating procedures, which are typically documented in Memoranda of Understanding or/and Data Sharing Agreements signed by these Partners as well as Medic.</p> -<h2 id="data-and-protected-health-information">Data and protected health information</h2> -<h3 id="use-of-software-to-collect-and-process-data">Use of software to collect and process data</h3> -<p>Medic has developed an open-source software toolkit (“Software”) that combines smart messaging, decision support, easy data gathering and management, and health system analytics. The Software can be accessed via many types of devices, including feature phones, smartphones, tablets, and desktop computers. Certain aspects of the Software can be downloaded for use on such end user devices. Health workers and families can use the Software to help monitor pregnancies, track outbreaks faster, treat illnesses, keep stock of essential medicines, and communicate about emergencies, among other things.</p> -<p>The Software is made available to the public on a free and open source basis, under licenses approved by the Open Source Initiative. These licenses limit Medic’s liability or responsibility with respect to any uses of the Software in which Medic personnel are not directly involved in Processing Data (defined below). The Software source code and the relevant licenses can be accessed at <a href="http://github.com/medic">http://github.com/medic</a>.</p> -<p>Medic hosts, maintains and supports certain components of the Software on Medic managed servers where Data obtained by use of Software on end user devices is received, accessed, handled, and/or stored (collectively, “Processed”). “Data” means any and all data, information and other content uploaded, posted, input or transmitted to the Software, or generated by the use of the Software, by or on behalf of End Users.</p> -<p>Subject to compliance with applicable law, Medic and its permitted subcontractors and agents shall not disclose Data to any third party and shall not use or access the Data for any purpose, except the following specific purposes:</p> -<ol> -<li>To provide services or conduct activities as permitted in writing by a Partner acting as the data controlling entity for the specific Data being accessed or shared (which may require execution of a memorandum of understand or data use agreement);</li> -<li>To maintain and improve the Software;</li> -<li>To perform routine monitoring and public reporting on aggregated Impact Metrics (defined below).</li> -</ol> -<p>For the purposes of this policy, “Impact Metrics” refers to aggregated Data which does not include personally identifiable information or Protected Health Information (defined below), and which is useful for Medic’s charitable purpose of monitoring the use of the Software and understanding its impacts on health systems. Examples of Impact Metrics include the total number of households registered in the Software, the total number of health workers using the Software, the total number of households visited in a given month, and the total number of Software-supported actions on antenatal care, postnatal care, integrated community case management, family planning, malnutrition, and immunization services. It may also include aggregate Data concerning COVID-19 services such as community event-based surveillance, contact tracing, CHW self-checks, and support for self-isolation provided in Medic-supported projects.</p> -<p>Any Impact Metrics shared with the general public will be aggregated globally; Medic personnel shall not de-aggregate the Impact Metrics to identify activities in any particular Country, without obtaining prior written consent from the Partner(s) acting as the relevant data controlling entities. Examples of previously reported Impact Metrics are available to the public in Medic’s quarterly and annual reports, which can be accessed at <a href="https://medic.org/reports/">https://medic.org/reports/</a>.</p> -<p>All additional uses of data for research and reporting will comply with applicable Ministry of Health policies and existing Partner policies for health data access and sharing. Medic personnel shall not use any Data for research purposes unless Medic has obtained the prior written consent of the relevant Partner(s), which may involve execution of a data sharing agreement.</p> -<h3 id="protected-health-information-policy">Protected health information policy</h3> -<p>For purposes of this policy, protected health information (“PHI”) is defined as any physically or electronically-encoded information containing at least one of the following:</p> -<ul> -<li>patient name(s);</li> -<li>patient-related dates (including birth, registration, visit, death);</li> -<li>patient-related ages;</li> -<li>patient and/or family phone/fax numbers, e-mail addresses, and/or social media account names;</li> -<li>references to a patient-related geographical subdivision (if smaller than 20,000 people);</li> -<li>social security, tax identification, or other patient account numbers;</li> -<li>patient-related device identifiers, addresses, or serial numbers (including IP addresses, MAC addresses, computer/-device serial numbers, beneficiary numbers, account numbers, and personally-identifying URLs);</li> -<li>licensure information (including drivers&rsquo; license and license plate numbers, and numbers related to professional membership/licensing);</li> -<li>images containing the face of a patient, patient&rsquo;s family member, or any other patient-related contact;</li> -<li>any biometric data describing a patient (including eye/hand measurements, height, weight, or clothing sizes); and</li> -<li>any unique identifier or code, other than a study-specific unique identifier assigned for purposes of managing an approved research protocol</li> -</ul> -<p>Medic’s PHI policy requires that:</p> -<ol> -<li>Access to protected health information – and to devices containing protected health information – must be password protected.</li> -<li>Any and all protected health information must be stored and retrieved using a full-disk encryption system.</li> -<li>Any device that connects to or uses any of Medic&rsquo;s mission-critical services must use a full-disk encryption system at all times.</li> -<li>When communicating information electronically, any and all protected health information must be protected using a secure transfer system.</li> -<li>Each person is responsible for his/her own compliance. If any person becomes aware of a violation of this policy, they have a duty to report the incident directly to Medic&rsquo;s People Operations Manager, and/or COO.</li> -</ol> -<p>All Medic personnel sign a Protected Health Information (PHI) policy which mandates the use of full disk encryption, secure communication channels, and two factor authentication when handling PHI. This policy applies to all Medic personnel &ndash; including, but not limited to, employees, contractors, associates, fellows, interns, advisors, and board members. -This policy applies to computer equipment and/or systems that are either: (i) property of Medic; (ii) property of any Medic personnel; or (iii) used directly by Medic personnel to carry out any assigned duties.</p> -<p>Exceptions to this policy may be granted – on a task-specific or project-specific basis only – at the discretion of the CPO or COO. This policy does not confer any ownership of data; Partner-submitted data remains in the control of Partner organizations. This policy does not apply to equipment stored in an authorized secure data center / facility (e.g. Amazon Web Services), or to equipment wholly owned and operated by an external Partner, or to external software deployments to which Medic has no access.</p> -<h2 id="security-practices">Security practices</h2> -<p>Medic is committed to data security. In addition to adhering to data security standards established by our Partners, we also recognize the security benefits and drawbacks of different technology tools and work with our partners to make the best choices and mitigate risk.</p> -<h3 id="technology-security-overview">Technology security overview</h3> -<p><strong>Web app:</strong> Medic uses secure transfers over HTTPS for all communication between the browser and our web application, with perfect forward secrecy (PFS) and 4096-bit SHA-2 certificates by default. We use a non-standard port for SSH access to reduce our exposure to automated brute-force attacks and can configure the web app to accept only public key authentication for SSH connections. Access to the web application requires a password, and user access can be established to varying degrees using a role-based access control facility (e.g. full access, restricted access, data entry only, and data export only).</p> -<p><strong>Data storage:</strong> Medic uses Amazon Web Services (AWS) with enforced two-factor authentication, HTTPS, and Identity and Access Management (IAM) for all hosted instances. We use IAM policies on AWS to restrict what any one individual Medic developer/administrator can do. Please see below for more information on AWS data security.</p> -<p><strong>SMS:</strong> We train users to input data using simple SMS codes or freeform SMS. We use &ldquo;plain text encoding&rdquo; which means viewers can see the value but not know the context of the data. As an example: when health workers text P 3 Jane (&ldquo;P&rdquo; for pregnancy, &ldquo;3&rdquo; for number of weeks pregnant, name), Medic registers the pregnancy, creates a patient ID, calculates the expected delivery date, and schedules automated reminder messages. SMS is inherently insecure but we work with every partner on safety practices to reduce and minimize mishandling of data and transmission of protected health information.</p> -<p><strong>Android phones:</strong> To secure the data, each device must be configured to use Full Disk Encryption and a Screen Lock.</p> -<h3 id="security-training-for-our-partners">Security training for our Partners</h3> -<p>We work with every partner to make sure they are trained and equipped to handle their data. This includes advising partners on how to create secure passwords and PINs, how to secure hardware, and how to safely transport data.</p> -<h2 id="inquiries-with-medics-data-protection-officer">Inquiries with Medic’s Data Protection Officer</h2> -<p>For any inquiries, please reach out to Medic’s Data Protection Officer, by emailing <a href="mailto:support@medic.org">support@medic.org</a> with the words “Data Protection” in the subject line.</p> \ No newline at end of file +Privacy on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/guides/privacy/Recent content in Privacy on Community Health ToolkitHugo -- gohugo.ioenAdding Privacy Policies to Appshttps://docs.communityhealthtoolkit.org/apps/guides/privacy/privacy-policy/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/privacy/privacy-policy/As of 3.10.0, privacy policies can be customized for every language, by adding desired content into HTML files. +Privacy policies are now publicly accessible rather than only being available after logging in. This means it can be shared with third parties, for example, app store compliance. If your instance URL is https://my-health-facility.org, then the privacy policy is available at https://my-health-facility.org/medic/privacy-policy. Added in 3.17.0. +Add these HTML files to the privacy-policies folder in your configuration.Privacy & Data Protection Policyhttps://docs.communityhealthtoolkit.org/apps/guides/privacy/policy/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/privacy/policy/Note This policy is reviewed and updated periodically by our Responsible Data Working Group and is intended as a resource for the CHT community. If you have any questions, please reach out to our Data Protection Officer at support@medic.org. +Table of Contents Table of Contents Our responsible data promise Compliance with applicable data regulations and policies Data and protected health information Use of software to collect and process data Protected health information policy Security practices Technology security overview Security training for our Partners Inquiries with Medic’s Data Protection Officer Our responsible data promise As a non-profit organization, Medic Mobile (“Medic”)’s mission is to advance good health and human flourishing by building open source technology with and for hard-to-reach communities. \ No newline at end of file diff --git a/apps/guides/privacy/policy/index.html b/apps/guides/privacy/policy/index.html index 145e13d768..9fc0b53595 100644 --- a/apps/guides/privacy/policy/index.html +++ b/apps/guides/privacy/policy/index.html @@ -1,9 +1,9 @@ -Privacy & Data Protection Policy | Community Health Toolkit +Privacy & Data Protection Policy | Community Health Toolkit

    Privacy & Data Protection Policy

    Medic’s Privacy and Data Protection Policy, which applies to the Community Health Toolkit

    If you have any questions, please reach out to our Data Protection Officer at support@medic.org.

    Table of Contents

    Our responsible data promise

    As a non-profit organization, Medic Mobile (“Medic”)’s mission is to advance good health and human flourishing by building open source technology with and for hard-to-reach communities. We value humanity, creativity, initiative, solidarity, and openness. Being responsible stewards of people’s data is critically important to our mission.

    We use the concept of Responsible Data (RD) to outline our collective duty to prioritise and respond to the ethical, legal, social, and privacy-related challenges that come from using data in new and different ways in healthcare, advocacy, and social change. We have an obligation to account for unintended consequences of working with data by:

    1. Prioritising people’s rights to consent, privacy, security and ownership when using data in social change and advocacy efforts
    2. Implementing values and practices of transparency and openness

    As an organization, we promise to do our best to live up to these obligations. We promise to show initiative, to seek feedback, and to proactively seek out ways to work with data more responsibly. We promise to accompany patients, health workers, researchers, organizational partners, and our open source community in the journey from best intentions to best practices in data-driven projects. The purpose of Medic’s data policy is to help our organization deliver on this responsible data promise.

    Compliance with applicable data regulations and policies

    Medic serves as a technical partner to governments and non-governmental organizations around the world. As a result, our projects comply with a range of country-specific and region-specific data protection regulations, and the details of compliance are addressed on a project-by-project basis. Adhering to all data regulations that are relevant to a given project is a central element of Medic’s global privacy and data protection policy.

    In most cases, the Ministries of Health, other government agencies, and non-governmental organizations that Medic supports (Medic “Partner(s)”) serve as data controlling entities, and they work with Medic as a business associate. Partners establish additional data handling policies and standard operating procedures, which are typically documented in Memoranda of Understanding or/and Data Sharing Agreements signed by these Partners as well as Medic.

    Data and protected health information

    Use of software to collect and process data

    Medic has developed an open-source software toolkit (“Software”) that combines smart messaging, decision support, easy data gathering and management, and health system analytics. The Software can be accessed via many types of devices, including feature phones, smartphones, tablets, and desktop computers. Certain aspects of the Software can be downloaded for use on such end user devices. Health workers and families can use the Software to help monitor pregnancies, track outbreaks faster, treat illnesses, keep stock of essential medicines, and communicate about emergencies, among other things.

    The Software is made available to the public on a free and open source basis, under licenses approved by the Open Source Initiative. These licenses limit Medic’s liability or responsibility with respect to any uses of the Software in which Medic personnel are not directly involved in Processing Data (defined below). The Software source code and the relevant licenses can be accessed at http://github.com/medic.

    Medic hosts, maintains and supports certain components of the Software on Medic managed servers where Data obtained by use of Software on end user devices is received, accessed, handled, and/or stored (collectively, “Processed”). “Data” means any and all data, information and other content uploaded, posted, input or transmitted to the Software, or generated by the use of the Software, by or on behalf of End Users.

    Subject to compliance with applicable law, Medic and its permitted subcontractors and agents shall not disclose Data to any third party and shall not use or access the Data for any purpose, except the following specific purposes:

    1. To provide services or conduct activities as permitted in writing by a Partner acting as the data controlling entity for the specific Data being accessed or shared (which may require execution of a memorandum of understand or data use agreement);
    2. To maintain and improve the Software;
    3. To perform routine monitoring and public reporting on aggregated Impact Metrics (defined below).

    For the purposes of this policy, “Impact Metrics” refers to aggregated Data which does not include personally identifiable information or Protected Health Information (defined below), and which is useful for Medic’s charitable purpose of monitoring the use of the Software and understanding its impacts on health systems. Examples of Impact Metrics include the total number of households registered in the Software, the total number of health workers using the Software, the total number of households visited in a given month, and the total number of Software-supported actions on antenatal care, postnatal care, integrated community case management, family planning, malnutrition, and immunization services. It may also include aggregate Data concerning COVID-19 services such as community event-based surveillance, contact tracing, CHW self-checks, and support for self-isolation provided in Medic-supported projects.

    Any Impact Metrics shared with the general public will be aggregated globally; Medic personnel shall not de-aggregate the Impact Metrics to identify activities in any particular Country, without obtaining prior written consent from the Partner(s) acting as the relevant data controlling entities. Examples of previously reported Impact Metrics are available to the public in Medic’s quarterly and annual reports, which can be accessed at https://medic.org/reports/.

    All additional uses of data for research and reporting will comply with applicable Ministry of Health policies and existing Partner policies for health data access and sharing. Medic personnel shall not use any Data for research purposes unless Medic has obtained the prior written consent of the relevant Partner(s), which may involve execution of a data sharing agreement.

    Protected health information policy

    For purposes of this policy, protected health information (“PHI”) is defined as any physically or electronically-encoded information containing at least one of the following:

    • patient name(s);
    • patient-related dates (including birth, registration, visit, death);
    • patient-related ages;
    • patient and/or family phone/fax numbers, e-mail addresses, and/or social media account names;
    • references to a patient-related geographical subdivision (if smaller than 20,000 people);
    • social security, tax identification, or other patient account numbers;
    • patient-related device identifiers, addresses, or serial numbers (including IP addresses, MAC addresses, computer/-device serial numbers, beneficiary numbers, account numbers, and personally-identifying URLs);
    • licensure information (including drivers’ license and license plate numbers, and numbers related to professional membership/licensing);
    • images containing the face of a patient, patient’s family member, or any other patient-related contact;
    • any biometric data describing a patient (including eye/hand measurements, height, weight, or clothing sizes); and
    • any unique identifier or code, other than a study-specific unique identifier assigned for purposes of managing an approved research protocol

    Medic’s PHI policy requires that:

    1. Access to protected health information – and to devices containing protected health information – must be password protected.
    2. Any and all protected health information must be stored and retrieved using a full-disk encryption system.
    3. Any device that connects to or uses any of Medic’s mission-critical services must use a full-disk encryption system at all times.
    4. When communicating information electronically, any and all protected health information must be protected using a secure transfer system.
    5. Each person is responsible for his/her own compliance. If any person becomes aware of a violation of this policy, they have a duty to report the incident directly to Medic’s People Operations Manager, and/or COO.

    All Medic personnel sign a Protected Health Information (PHI) policy which mandates the use of full disk encryption, secure communication channels, and two factor authentication when handling PHI. This policy applies to all Medic personnel – including, but not limited to, employees, contractors, associates, fellows, interns, advisors, and board members. + Create project issue

    Privacy & Data Protection Policy

    Medic’s Privacy and Data Protection Policy, which applies to the Community Health Toolkit

    If you have any questions, please reach out to our Data Protection Officer at support@medic.org.

    Table of Contents

    Our responsible data promise

    As a non-profit organization, Medic Mobile (“Medic”)’s mission is to advance good health and human flourishing by building open source technology with and for hard-to-reach communities. We value humanity, creativity, initiative, solidarity, and openness. Being responsible stewards of people’s data is critically important to our mission.

    We use the concept of Responsible Data (RD) to outline our collective duty to prioritise and respond to the ethical, legal, social, and privacy-related challenges that come from using data in new and different ways in healthcare, advocacy, and social change. We have an obligation to account for unintended consequences of working with data by:

    1. Prioritising people’s rights to consent, privacy, security and ownership when using data in social change and advocacy efforts
    2. Implementing values and practices of transparency and openness

    As an organization, we promise to do our best to live up to these obligations. We promise to show initiative, to seek feedback, and to proactively seek out ways to work with data more responsibly. We promise to accompany patients, health workers, researchers, organizational partners, and our open source community in the journey from best intentions to best practices in data-driven projects. The purpose of Medic’s data policy is to help our organization deliver on this responsible data promise.

    Compliance with applicable data regulations and policies

    Medic serves as a technical partner to governments and non-governmental organizations around the world. As a result, our projects comply with a range of country-specific and region-specific data protection regulations, and the details of compliance are addressed on a project-by-project basis. Adhering to all data regulations that are relevant to a given project is a central element of Medic’s global privacy and data protection policy.

    In most cases, the Ministries of Health, other government agencies, and non-governmental organizations that Medic supports (Medic “Partner(s)”) serve as data controlling entities, and they work with Medic as a business associate. Partners establish additional data handling policies and standard operating procedures, which are typically documented in Memoranda of Understanding or/and Data Sharing Agreements signed by these Partners as well as Medic.

    Data and protected health information

    Use of software to collect and process data

    Medic has developed an open-source software toolkit (“Software”) that combines smart messaging, decision support, easy data gathering and management, and health system analytics. The Software can be accessed via many types of devices, including feature phones, smartphones, tablets, and desktop computers. Certain aspects of the Software can be downloaded for use on such end user devices. Health workers and families can use the Software to help monitor pregnancies, track outbreaks faster, treat illnesses, keep stock of essential medicines, and communicate about emergencies, among other things.

    The Software is made available to the public on a free and open source basis, under licenses approved by the Open Source Initiative. These licenses limit Medic’s liability or responsibility with respect to any uses of the Software in which Medic personnel are not directly involved in Processing Data (defined below). The Software source code and the relevant licenses can be accessed at http://github.com/medic.

    Medic hosts, maintains and supports certain components of the Software on Medic managed servers where Data obtained by use of Software on end user devices is received, accessed, handled, and/or stored (collectively, “Processed”). “Data” means any and all data, information and other content uploaded, posted, input or transmitted to the Software, or generated by the use of the Software, by or on behalf of End Users.

    Subject to compliance with applicable law, Medic and its permitted subcontractors and agents shall not disclose Data to any third party and shall not use or access the Data for any purpose, except the following specific purposes:

    1. To provide services or conduct activities as permitted in writing by a Partner acting as the data controlling entity for the specific Data being accessed or shared (which may require execution of a memorandum of understand or data use agreement);
    2. To maintain and improve the Software;
    3. To perform routine monitoring and public reporting on aggregated Impact Metrics (defined below).

    For the purposes of this policy, “Impact Metrics” refers to aggregated Data which does not include personally identifiable information or Protected Health Information (defined below), and which is useful for Medic’s charitable purpose of monitoring the use of the Software and understanding its impacts on health systems. Examples of Impact Metrics include the total number of households registered in the Software, the total number of health workers using the Software, the total number of households visited in a given month, and the total number of Software-supported actions on antenatal care, postnatal care, integrated community case management, family planning, malnutrition, and immunization services. It may also include aggregate Data concerning COVID-19 services such as community event-based surveillance, contact tracing, CHW self-checks, and support for self-isolation provided in Medic-supported projects.

    Any Impact Metrics shared with the general public will be aggregated globally; Medic personnel shall not de-aggregate the Impact Metrics to identify activities in any particular Country, without obtaining prior written consent from the Partner(s) acting as the relevant data controlling entities. Examples of previously reported Impact Metrics are available to the public in Medic’s quarterly and annual reports, which can be accessed at https://medic.org/reports/.

    All additional uses of data for research and reporting will comply with applicable Ministry of Health policies and existing Partner policies for health data access and sharing. Medic personnel shall not use any Data for research purposes unless Medic has obtained the prior written consent of the relevant Partner(s), which may involve execution of a data sharing agreement.

    Protected health information policy

    For purposes of this policy, protected health information (“PHI”) is defined as any physically or electronically-encoded information containing at least one of the following:

    • patient name(s);
    • patient-related dates (including birth, registration, visit, death);
    • patient-related ages;
    • patient and/or family phone/fax numbers, e-mail addresses, and/or social media account names;
    • references to a patient-related geographical subdivision (if smaller than 20,000 people);
    • social security, tax identification, or other patient account numbers;
    • patient-related device identifiers, addresses, or serial numbers (including IP addresses, MAC addresses, computer/-device serial numbers, beneficiary numbers, account numbers, and personally-identifying URLs);
    • licensure information (including drivers’ license and license plate numbers, and numbers related to professional membership/licensing);
    • images containing the face of a patient, patient’s family member, or any other patient-related contact;
    • any biometric data describing a patient (including eye/hand measurements, height, weight, or clothing sizes); and
    • any unique identifier or code, other than a study-specific unique identifier assigned for purposes of managing an approved research protocol

    Medic’s PHI policy requires that:

    1. Access to protected health information – and to devices containing protected health information – must be password protected.
    2. Any and all protected health information must be stored and retrieved using a full-disk encryption system.
    3. Any device that connects to or uses any of Medic’s mission-critical services must use a full-disk encryption system at all times.
    4. When communicating information electronically, any and all protected health information must be protected using a secure transfer system.
    5. Each person is responsible for his/her own compliance. If any person becomes aware of a violation of this policy, they have a duty to report the incident directly to Medic’s People Operations Manager, and/or COO.

    All Medic personnel sign a Protected Health Information (PHI) policy which mandates the use of full disk encryption, secure communication channels, and two factor authentication when handling PHI. This policy applies to all Medic personnel – including, but not limited to, employees, contractors, associates, fellows, interns, advisors, and board members. This policy applies to computer equipment and/or systems that are either: (i) property of Medic; (ii) property of any Medic personnel; or (iii) used directly by Medic personnel to carry out any assigned duties.

    Exceptions to this policy may be granted – on a task-specific or project-specific basis only – at the discretion of the CPO or COO. This policy does not confer any ownership of data; Partner-submitted data remains in the control of Partner organizations. This policy does not apply to equipment stored in an authorized secure data center / facility (e.g. Amazon Web Services), or to equipment wholly owned and operated by an external Partner, or to external software deployments to which Medic has no access.

    Security practices

    Medic is committed to data security. In addition to adhering to data security standards established by our Partners, we also recognize the security benefits and drawbacks of different technology tools and work with our partners to make the best choices and mitigate risk.

    Technology security overview

    Web app: Medic uses secure transfers over HTTPS for all communication between the browser and our web application, with perfect forward secrecy (PFS) and 4096-bit SHA-2 certificates by default. We use a non-standard port for SSH access to reduce our exposure to automated brute-force attacks and can configure the web app to accept only public key authentication for SSH connections. Access to the web application requires a password, and user access can be established to varying degrees using a role-based access control facility (e.g. full access, restricted access, data entry only, and data export only).

    Data storage: Medic uses Amazon Web Services (AWS) with enforced two-factor authentication, HTTPS, and Identity and Access Management (IAM) for all hosted instances. We use IAM policies on AWS to restrict what any one individual Medic developer/administrator can do. Please see below for more information on AWS data security.

    SMS: We train users to input data using simple SMS codes or freeform SMS. We use “plain text encoding” which means viewers can see the value but not know the context of the data. As an example: when health workers text P 3 Jane (“P” for pregnancy, “3” for number of weeks pregnant, name), Medic registers the pregnancy, creates a patient ID, calculates the expected delivery date, and schedules automated reminder messages. SMS is inherently insecure but we work with every partner on safety practices to reduce and minimize mishandling of data and transmission of protected health information.

    Android phones: To secure the data, each device must be configured to use Full Disk Encryption and a Screen Lock.

    Security training for our Partners

    We work with every partner to make sure they are trained and equipped to handle their data. This includes advising partners on how to create secure passwords and PINs, how to secure hardware, and how to safely transport data.

    Inquiries with Medic’s Data Protection Officer

    For any inquiries, please reach out to Medic’s Data Protection Officer, by emailing support@medic.org with the words “Data Protection” in the subject line.


    CHT Applications > Reference > app_settings.json > .patient_reports

    Patient Reports: Defining SMS workflows with schedules, registration, and patient reports.

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/guides/privacy/privacy-policy/index.html b/apps/guides/privacy/privacy-policy/index.html index 3f83251a01..ca176054d4 100644 --- a/apps/guides/privacy/privacy-policy/index.html +++ b/apps/guides/privacy/privacy-policy/index.html @@ -1,9 +1,9 @@ -Adding Privacy Policies to Apps | Community Health Toolkit +Adding Privacy Policies to Apps | Community Health Toolkit

    Adding Privacy Policies to Apps

    Guide for adding privacy policies that users must accept before being allowed to use the app

    As of 3.10.0, privacy policies can be customized for every language, by adding desired content into HTML files.

    Privacy policies are now publicly accessible rather than only being available after logging in. This means it can be shared with third parties, for example, app store compliance. If your instance URL is https://my-health-facility.org, then the privacy policy is available at https://my-health-facility.org/medic/privacy-policy. Added in 3.17.0.

    Privacy Policy on login page

    Add these HTML files to the privacy-policies folder in your configuration. The privacy-policies.json file, which associates the HTML files with the correct language, should reside in the root of the project directory, not inside the privacy_policies folder.

    Adding Privacy Policies to Apps

    Guide for adding privacy policies that users must accept before being allowed to use the app

    As of 3.10.0, privacy policies can be customized for every language, by adding desired content into HTML files.

    Privacy policies are now publicly accessible rather than only being available after logging in. This means it can be shared with third parties, for example, app store compliance. If your instance URL is https://my-health-facility.org, then the privacy policy is available at https://my-health-facility.org/medic/privacy-policy. Added in 3.17.0.

    Privacy Policy on login page

    Add these HTML files to the privacy-policies folder in your configuration. The privacy-policies.json file, which associates the HTML files with the correct language, should reside in the root of the project directory, not inside the privacy_policies folder.

    {
       "en": "en.attachment.html",
       "fr": "fr.file.html",
       "sw": "swahili.html"
    @@ -317,7 +317,8 @@
         </p>
       </body></html>
     

    Then visotors the privacy policy would see:

    Accept privacy policy

    The app will load normally after acceptance. The user cannot opt out or skip acceptance, meaning, if a privacy policy is configured, the app will be unusable until the user has accepted it. Once accepted, users will not be prompted to accept the same policy again. However, users will be prompted for acceptance again when the policy is updated or when they change their language. An acceptance log is saved in the user-settings file and synced to the server, containing a history of privacy policies accepted by the user.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/security/index.html b/apps/guides/security/index.html index b1a8c19778..1526e47cff 100644 --- a/apps/guides/security/index.html +++ b/apps/guides/security/index.html @@ -1,9 +1,9 @@ -Security | Community Health Toolkit +Security | Community Health Toolkit

    Security

    Guides and best practices for securing CHT applications

    Securely onboarding users at scale

    How to securely create users, handle password changes and password breaches

    Securing Android Devices

    How to secure android devices used in deployments

    -

    Last modified 05.06.2020: Categorized guides (d26e182e)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/security/index.xml b/apps/guides/security/index.xml index 7e04af31e7..e09fa37dfc 100644 --- a/apps/guides/security/index.xml +++ b/apps/guides/security/index.xml @@ -1,101 +1,5 @@ -Community Health Toolkit – Securityhttps://docs.communityhealthtoolkit.org/apps/guides/security/Recent content in Security on Community Health ToolkitHugo -- gohugo.ioenApps: Securely onboarding users at scalehttps://docs.communityhealthtoolkit.org/apps/guides/security/securely-onboarding-users-at-scale/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/security/securely-onboarding-users-at-scale/ -<div class="pageinfo pageinfo-primary"> -<p>This document shows how to achieve a high level of credential management security for a CHT deployment. Implementers need to know when ease of use is more important than a more secure system. By reading this document you should be able to know when to make the &ldquo;more secure&rdquo; vs &ldquo;easier to use&rdquo; trade off.</p> -<p>No system is perfectly secure - be prepared to remediate a security breach!</p> -</div> -<p>When a CHT deployment will support hundreds of users or more, secure credential management becomes critical. Patient data is sensitive and should never be accessed in any way by unauthorized parties. By following best practices and preparing for the worst case scenario of a password breach, patient data can be kept safe and CHWs can be kept online and able to deliver care.</p> -<h2 id="before-starting">Before starting</h2> -<h3 id="secure-devices">Secure devices</h3> -<p>Firstly, ensure that the CHWs&rsquo; devices are secure: they all employ disk encryption and require a password or PIN to unlock and use. Please see our <a href="https://docs.communityhealthtoolkit.org/apps/guides/security/securing-android/">Securing Android Devices document</a> for more information. As well, an <a href="https://docs.communityhealthtoolkit.org/apps/guides/android/publishing/#mobile-device-management">MDM</a> may be used to enforce disk encryption and device unlock protocols.</p> -<h3 id="secure-administrative-users">Secure administrative users</h3> -<p>CHT administrators have the ability to create and delete users and push new configurations to the CHT so they should take extra precautions in managing their password. They should use a <a href="https://en.wikipedia.org/wiki/Passphrase">strong passphrase</a> (instead of a password) that is unique to their CHT login. They should use a <a href="https://en.wikipedia.org/wiki/Password_manager">password manager</a> to store this passphrase. -By following these steps, unauthorized people are less likely to be able to access administrator accounts.</p> -<h3 id="secure-passwords">Secure passwords</h3> -<p>When generating passwords for CHWs, do not use a formula which repeats itself (eg <code>password123</code> for user A, <code>password234</code> for user B etc.). Do not use the CHW information such as username, email or phone number in the password. Train CHWs to not re-use passwords.</p> -<p>For a reference application showing secure password generation, please see <code>generatePassword()</code> in the <a href="https://github.com/medic/cht-core/blob/master/scripts/bulk-password-update-export.js">CHT Core scripts directory</a>. This will generate a truly random 14 character password with uppercase (<code>A-Z</code>), lowercase (<code>a-z</code>), numerical (<code>0-9</code>) and one special character (<code>-</code>).</p> -<p>To generate a password that is easier to remember, type and speak over a phone, consider using <a href="https://en.wikipedia.org/wiki/Diceware">Diceware passphrases</a>. For added accessibility, use a word list from the CHWs native language if it is not English. This will make the words easier to spell and more likely to be remembered, but still secure.</p> -<h2 id="spreadsheet-use">Spreadsheet use</h2> -<p>Most deployments manage users in a spreadsheet shared either in Google Docs or other cloud service. It is convenient to have a canonical shared location to access the data. This is an acceptable, but not ideal, solution as it ensures changes are instantly shared while still ensuring a number of key security requirements. For an ideal solution, see <a href="#ideal-practice-1-only-magic-links">&ldquo;Ideal&rdquo; below</a>.</p> -<p>Of paramount importance:</p> -<ul> -<li><strong>Never</strong> enable anonymous access to the URL for the spreadsheet. You <strong>must always require authentication</strong> to access user credential lists</li> -<li>Audit regularly who has access to the shared spreadsheet. Consider setting up an alert every time a new person is granted access</li> -<li>Reduce the total number of users that need access by breaking up user lists by logical group. For example, each sub-county could have its own user spreadsheet. That way the user credentials are only shared with the local administrators who need it</li> -<li>Refrain from printing spreadsheets of users and passwords. They can be lost, stolen or easily photographed when shown in public</li> -<li>If you ever need to download a plaintext CSV with username and password, ensure the computer also has disk encryption enabled and requires a password to unlock</li> -<li>Do not keep downloaded CSV plaintext copies of credentials. Instead, delete and re-download them from the authorized, authenticated cloud server as needed</li> -</ul> -<h2 id="transmitting-credentials">Transmitting credentials</h2> -<p>When it comes time get a username and password onto a device or to a remote user, be sure to use mediums that are secure. One of the main concerns is credentials being found long after they were sent.</p> -<ul> -<li>A best practice is for the sender to add a credential to a shared password manager. The person receiving the credentials can then securely open the password manager.</li> -<li>If no password manager is available, consider sending the password via <a href="https://onetimesecret.com/">One Time Secret</a></li> -<li>To send credentials in to many CHWs, consider using <a href="https://docs.communityhealthtoolkit.org/apps/concepts/access/#magic-links-for-logging-in-token-login">token login</a>.</li> -<li>For sending large lists of credentials, as mentioned above, using a cloud provider like Google Sheets, is a good way to have an audit trail and still provide easy, remote access.</li> -</ul> -<h2 id="example-scenarios">Example scenarios</h2> -<h3 id="ideal-practice-1-only-magic-links">Ideal practice 1: Only Magic Links</h3> -<p>Create a spreadsheet with all your users&rsquo; data. Included is a username but NOT a password. When users are created in bulk via <a href="https://docs.communityhealthtoolkit.org/apps/guides/data/csv-to-docs/#creating-csv-files-for-users">the command line</a> or <a href="https://docs.communityhealthtoolkit.org/apps/guides/data/users-bulk-load/">bulk user upload</a>, have a <a href="https://docs.communityhealthtoolkit.org/apps/concepts/access/#magic-links-for-logging-in-token-login">token login</a> sent to the user via an SMS gateway. This avoids the problem of passwords being stored in clear text in the spreadsheet or on a printed version. The token login links can only be used once and are only valid for 24 hours.</p> -<h3 id="ideal-practice-2-unknown-passwords-reset-during-provision">Ideal practice 2: Unknown passwords, reset during provision</h3> -<p>An alternate and also secure approach, is to bulk create the users as described above, not use magic links, and use random passwords that you do not save after giving users the credentials. Train the users on changing their password after they&rsquo;ve logged in for the first time. This makes it harder for a password to be leaked because the password list isn&rsquo;t kept. Additionally, as users are trained to change their password, a leaked list is likely not useful as all passwords have changed.</p> -<h3 id="acceptable-practice-shared-list-limited-access-unique-passwords">Acceptable practice: Shared list, limited access, unique passwords</h3> -<p>For deployments that are centrally provisioning devices, it is acceptable for generate a <a href="#secure-passwords">strong password</a> per user in a centrally accessible, <a href="#spreadsheet-use">secure spreadsheet</a>. Working off a computer to view the spreadsheet, provision each device. Do not to print the list of credentials.</p> -<h3 id="worst-practice-shared-list-anonymous-access-similar-passwords">Worst practice: Shared list, anonymous access, similar passwords</h3> -<p>Create all users with near identical passwords (eg <code>password123</code>, <code>passord234</code>, <code>password345</code> etc.) that are then printed out, shared via email or posted to a public URL which requires no authentication. Send an SMS to the CHW with username, password and URL of the device.</p> -<p>There&rsquo;s many failures here:</p> -<ul> -<li>Passwords are predictable and easy to guess</li> -<li>Credential lists are shared too widely</li> -<li>Users will have their credentials in SMS which may be easily discovered weeks or months later by an unauthorized party</li> -</ul> -<h2 id="remediation-of-security-failure">Remediation of security failure</h2> -<p>By knowing what the security threats are you can know the most helpful steps to remediate them to limit the damage done to the CHT deployment, the privacy of the patients and the security of CHWs.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Users who have their password changed can continue to use the CHT to deliver care. They need to <em>keep their device offline</em> though. After a password change, when a CHW attempts to sync, they will be prompted to log in. At that point, they will not be able to continue using the CHT until they log in. -No data will be lost if they log in as the <em>same user</em>. -</div> -<h3 id="credential-list-shared-on-internet">Credential list shared on internet</h3> -<p>If an online list of credentials is leaked to unauthorized parties, or worse, the Internet at large in the form of being indexed by a search engine, you need to change all passwords as soon as possible for any user on the leaked list. Per the note above, changing a password will log the user out immediately unless they are offline. Having supervisors encourage a user to switch their device to offline mode (turn off all data) is a good way to ensure they can continue to deliver care so they&rsquo;re not locked out.</p> -<h3 id="programmatic-password-reset">Programmatic password reset</h3> -<p>Medic <a href="https://github.com/medic/cht-core/blob/master/scripts/bulk-password-update-export.js">has published a script</a> to easily change all passwords for a list of users. Administrators will then be responsible to log CHWs back in by <a href="#transmitting-credentials">securely sending</a> them their password.</p> -<p>Additionally, this script could be updated to immediately send a <a href="https://docs.communityhealthtoolkit.org/apps/concepts/access/#magic-links-for-logging-in-token-login">token login</a> link to the user. There would be no need to change the password as this is done automatically for you. Note that users would need their phone numbers recorded in the CHT to receive a token login link. Here&rsquo;s an example <code>curl</code> command to send a token login link for the <code>mary</code> user:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>curl https://medic:password@cht.example.com/api/v1/users/mary <span style="color:#4e9a06">\ -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span> -X POST -H <span style="color:#4e9a06">&#34;Content-Type: application/json&#34;</span> <span style="color:#4e9a06">\ -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span> -d <span style="color:#4e9a06">&#39;{&#34;token_login&#34;:true}&#39;</span> -</span></span></code></pre></div><h3 id="audit-who-has-changed-their-password">Audit who has changed their password</h3> -<p>If you have instructed all users to manually change their password, CHT administrators can search their server logs to see which users have made the update. First find a list of all running services that have the word <code>haproxy</code> in it:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>docker ps --filter <span style="color:#4e9a06">&#34;name=haproxy&#34;</span> --format <span style="color:#4e9a06">&#39;{{.Names}}&#39;</span> -</span></span></code></pre></div><p>Then, assuming your service is called <code>cht_haproxy_1</code>, run this command to find all password changes in the past 24 hours:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>docker logs cht_haproxy_1 --since 24h 2&gt;<span style="color:#000;font-weight:bold">&amp;</span><span style="color:#0000cf;font-weight:bold">1</span> <span style="color:#000;font-weight:bold">|</span> grep password_scheme -</span></span></code></pre></div><p>Here is an example event showing user <code>mary</code> having changed their password on Feb 9 18:47:</p> -<p><code>Feb 9 18:47:08 haproxy[12]: 172.22.0.2,couchdb,201,15,0,0,PUT,/_users/org.couchdb.user%3Amary,api,medic,'{&quot;_id&quot;:&quot;org.couchdb.user:mary&quot;,&quot;_rev&quot;:&quot;3-d3dd14f3026942245ce3881adfcd513e&quot;,&quot;name&quot;:&quot;mary&quot;,&quot;type&quot;:&quot;user&quot;,&quot;roles&quot;:[&quot;chw&quot;],&quot;facility_id&quot;:&quot;14046008-f796-4418-9ff2-12c2ef77b478&quot;,&quot;password_scheme&quot;:&quot;***&quot;,&quot;iterations&quot;:10,&quot;derived_key&quot;:&quot;f846a7a20209592c613c09ea4405170735c7a557&quot;,&quot;salt&quot;:&quot;bbb089e5fc459c2ca1088c8316e0cc99&quot;,&quot;password&quot;:&quot;***&quot;}',411,15,86,'node-fetch/1.0 (+https://github.com/bitinn/node-fetch)'</code></p> -<h2 id="frequently-asked-questions">Frequently Asked Questions</h2> -<ul> -<li><a href="https://forum.communityhealthtoolkit.org/t/best-practices-for-user-management-at-scale/1668/1">Best practices for user management at scale?</a></li> -<li><a href="https://forum.communityhealthtoolkit.org/t/send-magic-link-via-mobile-bundle/2760/2">Is it possible to send Magic Link to users using their own mobile bundles instead of setting up a messaging gateway?</a></li> -</ul>Apps: Securing Android Deviceshttps://docs.communityhealthtoolkit.org/apps/guides/security/securing-android/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/security/securing-android/ -<p>To secure an android device you should enable at least a pin code lock on the device, enable FDE (full disc encryption) and setup remote wiping capabilities by enabling mobile device management.</p> -<h2 id="pin-setup-and-fde">PIN Setup and FDE</h2> -<p>Instructions are slightly different per device. Enabling FDE has the added benefit that you must also lock the device with a pin code or password.</p> -<h3 id="android-50-or-later">Android 5.0 or later</h3> -<p>Open the <code>Security</code> menu under settings. On unmodified versions of Android, this -can be found under <code>Settings</code> <code>&gt;</code> <code>Personal</code> <code>&gt;</code> <code>Security</code>. Choose <code>Encrypt Phone</code>, <code>Encrypt Tablet</code>, or <code>Encrypt Device</code>. If you haven&rsquo;t already set a PIN -or passcode for the lock screen, you will be prompted to do so. Remember this -PIN and do not write it down.</p> -<p><img src="encrypt-50.jpg" alt="Screenshot for Android 5.0"></p> -<h3 id="android-44-or-earlier">Android 4.4 or earlier</h3> -<p>First, you&rsquo;ll need to set up a PIN. Navigate to <code>Settings</code> <code>&gt;</code> <code>Security</code> <code>&gt;</code> -<code>Screen Lock</code>, and set a numeric PIN for the device of <em>at least</em> four digits. -Remember this PIN and do not write it down.</p> -<p>Then, open the <code>Security</code> menu under settings. On unmodified versions of -Android, this can be found under <code>Settings</code> <code>&gt;</code> <code>Personal</code> <code>&gt;</code> <code>Security</code>. -On some devices, the exact menu layout may have been modified by the hardware -manufacturer. Choose <code>Encrypt Phone</code> or <code>Encrypt Tablet</code>.</p> -<p><img src="encrypt-44.jpg" alt="Screenshot for Android 4.4"></p> -<h3 id="android-versions-before-30">Android Versions before 3.0</h3> -<p>Android does not support disk encryption in versions earlier than 3.0 -(Honeycomb). If you have one of these devices, we recommend that you do not use it.</p> -<h2 id="mobile-device-management">Mobile device management</h2> -<p>Using mobile device management (MDM) software allows administrators to remotely manage mobile devices, which often includes the option to delete apps and data from lost or stolen mobile devices.</p> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/guides/android/publishing/#mobile-device-management">Publishing &gt; Mobile Device Management</a></p> \ No newline at end of file +Security on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/guides/security/Recent content in Security on Community Health ToolkitHugo -- gohugo.ioenSecurely onboarding users at scalehttps://docs.communityhealthtoolkit.org/apps/guides/security/securely-onboarding-users-at-scale/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/security/securely-onboarding-users-at-scale/This document shows how to achieve a high level of credential management security for a CHT deployment. Implementers need to know when ease of use is more important than a more secure system. By reading this document you should be able to know when to make the &ldquo;more secure&rdquo; vs &ldquo;easier to use&rdquo; trade off. +No system is perfectly secure - be prepared to remediate a security breach! +When a CHT deployment will support hundreds of users or more, secure credential management becomes critical.Securing Android Deviceshttps://docs.communityhealthtoolkit.org/apps/guides/security/securing-android/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/security/securing-android/To secure an android device you should enable at least a pin code lock on the device, enable FDE (full disc encryption) and setup remote wiping capabilities by enabling mobile device management. +PIN Setup and FDE Instructions are slightly different per device. Enabling FDE has the added benefit that you must also lock the device with a pin code or password. +Android 5.0 or later Open the Security menu under settings. \ No newline at end of file diff --git a/apps/guides/security/securely-onboarding-users-at-scale/index.html b/apps/guides/security/securely-onboarding-users-at-scale/index.html index 9b2ab9d326..29ea54cdd9 100644 --- a/apps/guides/security/securely-onboarding-users-at-scale/index.html +++ b/apps/guides/security/securely-onboarding-users-at-scale/index.html @@ -1,9 +1,9 @@ -Securely onboarding users at scale | Community Health Toolkit +Securely onboarding users at scale | Community Health Toolkit

    Securely onboarding users at scale

    How to securely create users, handle password changes and password breaches

    This document shows how to achieve a high level of credential management security for a CHT deployment. Implementers need to know when ease of use is more important than a more secure system. By reading this document you should be able to know when to make the “more secure” vs “easier to use” trade off.

    No system is perfectly secure - be prepared to remediate a security breach!

    When a CHT deployment will support hundreds of users or more, secure credential management becomes critical. Patient data is sensitive and should never be accessed in any way by unauthorized parties. By following best practices and preparing for the worst case scenario of a password breach, patient data can be kept safe and CHWs can be kept online and able to deliver care.

    Before starting

    Secure devices

    Firstly, ensure that the CHWs’ devices are secure: they all employ disk encryption and require a password or PIN to unlock and use. Please see our Securing Android Devices document for more information. As well, an MDM may be used to enforce disk encryption and device unlock protocols.

    Secure administrative users

    CHT administrators have the ability to create and delete users and push new configurations to the CHT so they should take extra precautions in managing their password. They should use a strong passphrase (instead of a password) that is unique to their CHT login. They should use a password manager to store this passphrase. + Create project issue

    Securely onboarding users at scale

    How to securely create users, handle password changes and password breaches

    This document shows how to achieve a high level of credential management security for a CHT deployment. Implementers need to know when ease of use is more important than a more secure system. By reading this document you should be able to know when to make the “more secure” vs “easier to use” trade off.

    No system is perfectly secure - be prepared to remediate a security breach!

    When a CHT deployment will support hundreds of users or more, secure credential management becomes critical. Patient data is sensitive and should never be accessed in any way by unauthorized parties. By following best practices and preparing for the worst case scenario of a password breach, patient data can be kept safe and CHWs can be kept online and able to deliver care.

    Before starting

    Secure devices

    Firstly, ensure that the CHWs’ devices are secure: they all employ disk encryption and require a password or PIN to unlock and use. Please see our Securing Android Devices document for more information. As well, an MDM may be used to enforce disk encryption and device unlock protocols.

    Secure administrative users

    CHT administrators have the ability to create and delete users and push new configurations to the CHT so they should take extra precautions in managing their password. They should use a strong passphrase (instead of a password) that is unique to their CHT login. They should use a password manager to store this passphrase. By following these steps, unauthorized people are less likely to be able to access administrator accounts.

    Secure passwords

    When generating passwords for CHWs, do not use a formula which repeats itself (eg password123 for user A, password234 for user B etc.). Do not use the CHW information such as username, email or phone number in the password. Train CHWs to not re-use passwords.

    For a reference application showing secure password generation, please see generatePassword() in the CHT Core scripts directory. This will generate a truly random 14 character password with uppercase (A-Z), lowercase (a-z), numerical (0-9) and one special character (-).

    To generate a password that is easier to remember, type and speak over a phone, consider using Diceware passphrases. For added accessibility, use a word list from the CHWs native language if it is not English. This will make the words easier to spell and more likely to be remembered, but still secure.

    Spreadsheet use

    Most deployments manage users in a spreadsheet shared either in Google Docs or other cloud service. It is convenient to have a canonical shared location to access the data. This is an acceptable, but not ideal, solution as it ensures changes are instantly shared while still ensuring a number of key security requirements. For an ideal solution, see “Ideal” below.

    Of paramount importance:

    • Never enable anonymous access to the URL for the spreadsheet. You must always require authentication to access user credential lists
    • Audit regularly who has access to the shared spreadsheet. Consider setting up an alert every time a new person is granted access
    • Reduce the total number of users that need access by breaking up user lists by logical group. For example, each sub-county could have its own user spreadsheet. That way the user credentials are only shared with the local administrators who need it
    • Refrain from printing spreadsheets of users and passwords. They can be lost, stolen or easily photographed when shown in public
    • If you ever need to download a plaintext CSV with username and password, ensure the computer also has disk encryption enabled and requires a password to unlock
    • Do not keep downloaded CSV plaintext copies of credentials. Instead, delete and re-download them from the authorized, authenticated cloud server as needed

    Transmitting credentials

    When it comes time get a username and password onto a device or to a remote user, be sure to use mediums that are secure. One of the main concerns is credentials being found long after they were sent.

    • A best practice is for the sender to add a credential to a shared password manager. The person receiving the credentials can then securely open the password manager.
    • If no password manager is available, consider sending the password via One Time Secret
    • To send credentials in to many CHWs, consider using token login.
    • For sending large lists of credentials, as mentioned above, using a cloud provider like Google Sheets, is a good way to have an audit trail and still provide easy, remote access.

    Example scenarios

    Create a spreadsheet with all your users’ data. Included is a username but NOT a password. When users are created in bulk via the command line or bulk user upload, have a token login sent to the user via an SMS gateway. This avoids the problem of passwords being stored in clear text in the spreadsheet or on a printed version. The token login links can only be used once and are only valid for 24 hours.

    Ideal practice 2: Unknown passwords, reset during provision

    An alternate and also secure approach, is to bulk create the users as described above, not use magic links, and use random passwords that you do not save after giving users the credentials. Train the users on changing their password after they’ve logged in for the first time. This makes it harder for a password to be leaked because the password list isn’t kept. Additionally, as users are trained to change their password, a leaked list is likely not useful as all passwords have changed.

    Acceptable practice: Shared list, limited access, unique passwords

    For deployments that are centrally provisioning devices, it is acceptable for generate a strong password per user in a centrally accessible, secure spreadsheet. Working off a computer to view the spreadsheet, provision each device. Do not to print the list of credentials.

    Worst practice: Shared list, anonymous access, similar passwords

    Create all users with near identical passwords (eg password123, passord234, password345 etc.) that are then printed out, shared via email or posted to a public URL which requires no authentication. Send an SMS to the CHW with username, password and URL of the device.

    There’s many failures here:

    • Passwords are predictable and easy to guess
    • Credential lists are shared too widely
    • Users will have their credentials in SMS which may be easily discovered weeks or months later by an unauthorized party

    Remediation of security failure

    By knowing what the security threats are you can know the most helpful steps to remediate them to limit the damage done to the CHT deployment, the privacy of the patients and the security of CHWs.

    Credential list shared on internet

    If an online list of credentials is leaked to unauthorized parties, or worse, the Internet at large in the form of being indexed by a search engine, you need to change all passwords as soon as possible for any user on the leaked list. Per the note above, changing a password will log the user out immediately unless they are offline. Having supervisors encourage a user to switch their device to offline mode (turn off all data) is a good way to ensure they can continue to deliver care so they’re not locked out.

    Programmatic password reset

    Medic has published a script to easily change all passwords for a list of users. Administrators will then be responsible to log CHWs back in by securely sending them their password.

    Additionally, this script could be updated to immediately send a token login link to the user. There would be no need to change the password as this is done automatically for you. Note that users would need their phone numbers recorded in the CHT to receive a token login link. Here’s an example curl command to send a token login link for the mary user:

    curl https://medic:password@cht.example.com/api/v1/users/mary \
         -X POST -H "Content-Type: application/json" \
    @@ -324,7 +324,8 @@
     Concepts >
     Access
     : Magic Links for Logging in Token Login

    Starting up your digital health apps

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/guides/security/securing-android/index.html b/apps/guides/security/securing-android/index.html index c928cd0662..8bfb14a5c7 100644 --- a/apps/guides/security/securing-android/index.html +++ b/apps/guides/security/securing-android/index.html @@ -1,9 +1,9 @@ -Securing Android Devices | Community Health Toolkit +Securing Android Devices | Community Health Toolkit

    Securing Android Devices

    How to secure android devices used in deployments

    To secure an android device you should enable at least a pin code lock on the device, enable FDE (full disc encryption) and setup remote wiping capabilities by enabling mobile device management.

    PIN Setup and FDE

    Instructions are slightly different per device. Enabling FDE has the added benefit that you must also lock the device with a pin code or password.

    Android 5.0 or later

    Open the Security menu under settings. On unmodified versions of Android, this + Create project issue

    Securing Android Devices

    How to secure android devices used in deployments

    To secure an android device you should enable at least a pin code lock on the device, enable FDE (full disc encryption) and setup remote wiping capabilities by enabling mobile device management.

    PIN Setup and FDE

    Instructions are slightly different per device. Enabling FDE has the added benefit that you must also lock the device with a pin code or password.

    Android 5.0 or later

    Open the Security menu under settings. On unmodified versions of Android, this can be found under Settings > Personal > Security. Choose Encrypt Phone, Encrypt Tablet, or Encrypt Device. If you haven’t already set a PIN or passcode for the lock screen, you will be prompted to do so. Remember this PIN and do not write it down.

    Screenshot for Android 5.0

    Android 4.4 or earlier

    First, you’ll need to set up a PIN. Navigate to Settings > Security > @@ -318,7 +318,8 @@ Quick Guides > Android > Publishing

    Instructions for Publishing Android Apps

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/guides/tasks/index.html b/apps/guides/tasks/index.html index 4b74a7f546..ff22825d22 100644 --- a/apps/guides/tasks/index.html +++ b/apps/guides/tasks/index.html @@ -1,9 +1,9 @@ -Tasks | Community Health Toolkit +Tasks | Community Health Toolkit

    Tasks

    Building and managing Tasks and their data

    Passing data from a task into the app form

    Demonstrates how to pass data into an application form via tasks

    Querying Task Documents

    Querying the data which results from an example task. Notes on the performance implications of tasks.

    Understanding the parameters in the Task Schema

    Understanding the data which is passed into Task interfaces

    -

    Last modified 07.01.2022: Add index for Tasks (#599) (963e5b9d)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/tasks/index.xml b/apps/guides/tasks/index.xml index bef020055b..5d8b793a99 100644 --- a/apps/guides/tasks/index.xml +++ b/apps/guides/tasks/index.xml @@ -1,355 +1,6 @@ -Community Health Toolkit – Taskshttps://docs.communityhealthtoolkit.org/apps/guides/tasks/Recent content in Tasks on Community Health ToolkitHugo -- gohugo.ioenApps: Passing data from a task into the app formhttps://docs.communityhealthtoolkit.org/apps/guides/tasks/pass-data-to-form/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/tasks/pass-data-to-form/ -<div class="pageinfo pageinfo-primary"> -<p>This guide explains how to pass data from a task into the action <em>application form</em>.</p> -</div> -<h2 id="prerequisites">Prerequisites</h2> -<ul> -<li><a href="https://docs.communityhealthtoolkit.org/apps/tutorials/tasks-2/">Complex Tasks Tutorial</a></li> -<li><a href="https://docs.communityhealthtoolkit.org/apps/tutorials/app-forms/">Application Forms Tutorial</a></li> -</ul> -<h2 id="scenario">Scenario</h2> -<p>Let&rsquo;s look deeper at the scenario from the <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/tasks-2/">Complex Tasks Tutorial</a> where we have an ANC follow-up task which recurs eight times, and we want to ask the user different questions on the first and last follow-up.</p> -<h2 id="developing-the-task">Developing the task</h2> -<p>From the <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/tasks-2/">Complex Tasks Tutorial</a>, here is the task.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">DateTime</span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">require</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;luxon&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#000">module</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">exports</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">name</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;pnc-after-pregnancy&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;icon-follow-up&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">title</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;task.pnc_followup&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesTo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contacts&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;patient&#39;</span><span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">userIsChw</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">user</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">user</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact_type</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;chw_area&#39;</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">isDead</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">date_of_death</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">isMuted</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">muted</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">userIsChw</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#000">isDead</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#000">isMuted</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">mostRecentPregnancy</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">Utils</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getMostRecentReport</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reports</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;pregnancy&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">calculatedLmp</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">mostRecentPregnancy</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">Utils</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getField</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">mostRecentPregnancy</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;g_details.estimated_lmp&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">this</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">lmp</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">calculatedLmp</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">DateTime</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">fromFormat</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">calculatedLmp</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;yyyy-MM-dd&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#204a87;font-weight:bold">this</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">lmp</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#204a87;font-weight:bold">this</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">lmp</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">isValid</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">events</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">12</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">20</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">26</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">30</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">34</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">36</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">38</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">40</span><span style="color:#000;font-weight:bold">]</span> <span style="color:#8f5902;font-style:italic">// follow-up weeks after LMP -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">map</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">weekAfterLmp</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">({</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">`pnc-week-</span><span style="color:#4e9a06">${</span><span style="color:#000">weekAfterLmp</span><span style="color:#4e9a06">}</span><span style="color:#4e9a06">`</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">start</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">weekAfterLmp</span> <span style="color:#ce5c00;font-weight:bold">&gt;</span> <span style="color:#0000cf;font-weight:bold">30</span> <span style="color:#ce5c00;font-weight:bold">?</span> <span style="color:#0000cf;font-weight:bold">6</span> <span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">7</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">end</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">weekAfterLmp</span> <span style="color:#ce5c00;font-weight:bold">&gt;</span> <span style="color:#0000cf;font-weight:bold">30</span> <span style="color:#ce5c00;font-weight:bold">?</span> <span style="color:#0000cf;font-weight:bold">7</span> <span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">14</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">dueDate</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#204a87;font-weight:bold">this</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">lmp</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">plus</span><span style="color:#000;font-weight:bold">({</span> <span style="color:#000">weeks</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">weekAfterLmp</span> <span style="color:#000;font-weight:bold">}).</span><span style="color:#000">toJsDate</span><span style="color:#000;font-weight:bold">();</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">})),</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">resolvedIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">report</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">event</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">dueDate</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">start</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">Utils</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">addDate</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">dueDate</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#000">event</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">start</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">getTime</span><span style="color:#000;font-weight:bold">();</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">end</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">Utils</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">addDate</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">dueDate</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">event</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">end</span> <span style="color:#ce5c00;font-weight:bold">+</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">getTime</span><span style="color:#000;font-weight:bold">();</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">pncInWindow</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">Utils</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">isFormSubmittedInWindow</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reports</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;pnc_followup&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">start</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">end</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">assessmentInWindow</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">Utils</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">isFormSubmittedInWindow</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reports</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;assessment_followup&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">start</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">end</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">pncInWindow</span> <span style="color:#ce5c00;font-weight:bold">||</span> <span style="color:#000">assessmentInWindow</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">actions</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">form</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;pnc_followup&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">modifyContent</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">content</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">report</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">event</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">followupCount</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">this</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">definition</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">events</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">findIndex</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">e</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000">event</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">id</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#000">e</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">id</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">+</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">content</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">t_followup_count</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">followupCount</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">toString</span><span style="color:#000;font-weight:bold">();</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">};</span> -</span></span></code></pre></div><h2 id="modifycontent">modifyContent</h2> -<p>Let&rsquo;s take a look at the <code>actions</code> section and specifically the <a href="https://docs.communityhealthtoolkit.org/apps/reference/tasks/">modifyContent</a> attribute. This <code>modifyContent</code> attribute allows the task to pass data from the task (in JavaScript) into the action app form (xlsx). The <code>content</code> object is the object which binds to the <code>inputs</code> section in the app form. You can pass data into the app form by assigning values onto this object.</p> -<p>This function calculates <code>t_followup_count</code> to be the index of the event which is being completed. So the first task event (which appears after 12 weeks) is followup <code>1</code>, and the task event after 34 weeks is followup <code>5</code>.</p> -<h2 id="the-action-form">The action form</h2> -<p>In this sample app form <code>survey</code>, the value of <code>t_followup_count</code> is listed in the inputs section and the CHT <em>binds</em> the data passed by the task onto this hidden variable. An example calculation <code>is_first_followup</code> demonstrates how to use the value in the form&rsquo;s logic. <code>is_first_followup</code> will be true 12 weeks after the LMP date, and false on all other followups.</p> -<table> -<thead> -<tr> -<th>type</th> -<th>name</th> -<th>label</th> -<th>required</th> -<th>relevant</th> -<th>appearance</th> -<th>constraint</th> -<th>constraint_message</th> -<th>calculation</th> -</tr> -</thead> -<tbody> -<tr> -<td>begin group</td> -<td>inputs</td> -<td></td> -<td></td> -<td></td> -<td>field-list</td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>hidden</td> -<td>t_followup_count</td> -<td>Data from task</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>end group</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>calculate</td> -<td>is_first_followup</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td>if(${t_follow_up_count}=&lsquo;1&rsquo;,true,false)</td> -</tr> -</tbody> -</table>Apps: Querying Task Documentshttps://docs.communityhealthtoolkit.org/apps/guides/tasks/query-task-data/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/tasks/query-task-data/ -<div class="pageinfo pageinfo-primary"> -<p>This guide explains the data which results from tasks and how to query it.</p> -<ul> -<li>Write a PostgreSQL query to examine task data</li> -<li>Build deeper understanding of task data</li> -<li>Present some data considerations of which task authors should remain mindful</li> -</ul> -</div> -<h2 id="prerequisites">Prerequisites</h2> -<ul> -<li><a href="https://docs.communityhealthtoolkit.org/core/overview/data-flows-for-analytics/">Data Flows for Analytics</a></li> -</ul> -<h2 id="querying-task-data">Querying task data</h2> -<p>The task system running on each user&rsquo;s device is powered by <a href="https://docs.communityhealthtoolkit.org/core/overview/db-schema/#tasks">task documents</a> and those task documents sync to the server and to PostgreSQL just like a contact or a report. Having task documents in PostgreSQL allows system administrators to analyse how users are interacting with tasks.</p> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/core/overview/data-flows-for-analytics/">Data flows for analytics</a></p> -<h3 id="first-assessment-completion-rate">First Assessment Completion Rate</h3> -<p>Working with the <em>First Assessment</em> task from the <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/tasks-1/">Configuring Tasks Tutorial</a>, let&rsquo;s try to answer the question <strong>What percentage of the scheduled <em>first assessment</em> events have been completed?</strong>.</p> -<p>Let&rsquo;s query data from the last three months to see how the <em>first assessment</em> task is behaving in production:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">SELECT</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">date_trunc</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;month&#39;</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">duedate</span><span style="color:#000;font-weight:bold">)</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">AS</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">due_date_month</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">task_state</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">count</span><span style="color:#000;font-weight:bold">(</span><span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000;font-weight:bold">)</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">FROM</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">useview_task</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">WHERE</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">title</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#39;assessment-after-registration&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">and</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">duedate</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#ce5c00;font-weight:bold">&gt;=</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">date_trunc</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;month&#39;</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">now</span><span style="color:#000;font-weight:bold">())</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#39;3 months&#39;</span><span style="color:#000;font-weight:bold">::</span><span style="color:#204a87">interval</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">and</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">duedate</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">date_trunc</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;month&#39;</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">now</span><span style="color:#000;font-weight:bold">())</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#39;1 months&#39;</span><span style="color:#000;font-weight:bold">::</span><span style="color:#204a87">interval</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">GROUP</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">BY</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#0000cf;font-weight:bold">2</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">ORDER</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">BY</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#0000cf;font-weight:bold">2</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#000;font-weight:bold">;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span></code></pre></div><p>For convenience, here is the task definition from the <em>First Assessment</em> task in the tutorial:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#000">module</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">exports</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">[{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">name</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;assessment-after-registration&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">title</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;First Assessment&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;icon-healthcare&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesTo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contacts&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;patient&#39;</span><span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">c</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000">user</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">user</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact_type</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;chw_area&#39;</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#000">c</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">date_of_death</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#000">c</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">muted</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">actions</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[{</span> <span style="color:#000">form</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;assessment&#39;</span> <span style="color:#000;font-weight:bold">}],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">events</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">start</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">7</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">days</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">7</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">end</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}],</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}];</span> -</span></span></code></pre></div><p><strong>What is this code doing?</strong></p> -<ul> -<li><code>useview_task</code> - This is a materialized view created automatically by the medic-couch2pg service. It is an intuitive view of the data from the <a href="https://docs.communityhealthtoolkit.org/core/overview/db-schema/#tasks">task document schema</a>.</li> -<li><code>task_state</code> - The meaning of each task state is explained in the <a href="https://docs.communityhealthtoolkit.org/core/overview/db-schema/#tasks">task document schema</a>.</li> -<li><code>WHERE title</code> - The <em>name</em> attribute in the <a href="https://docs.communityhealthtoolkit.org/apps/reference/tasks/#tasksjs">task.js schema</a> is used exclusively in the task&rsquo;s backend data. Here we limit the query to task documents resulting from our named task. The <code>title</code> in postgres maps to the <code>name</code> in JavaScript not the <code>title</code> in JavaScript - which is confusing.</li> -<li><code>WHERE duedate</code> - One task document is created per event and this task has one event per contact which is due 7 days after the contact&rsquo;s creation date. Here we limit the query to task documents which are <em>due in the last 3 calendar months</em>.</li> -</ul> -<h2 id="understanding-the-data">Understanding the data</h2> -<p>Here is a sample output from that query above. The query was executed some day in July (07), 2021.</p> -<table> -<thead> -<tr> -<th>due_date_month</th> -<th>task_state</th> -<th>count</th> -</tr> -</thead> -<tbody> -<tr> -<td>2021-05-01</td> -<td>Cancelled</td> -<td>6</td> -</tr> -<tr> -<td>2021-05-01</td> -<td>Completed</td> -<td>749</td> -</tr> -<tr> -<td>2021-05-01</td> -<td>Failed</td> -<td>226</td> -</tr> -<tr> -<td>2021-06-01</td> -<td>Cancelled</td> -<td>3</td> -</tr> -<tr> -<td>2021-06-01</td> -<td>Completed</td> -<td>769</td> -</tr> -<tr> -<td>2021-06-01</td> -<td>Draft</td> -<td>3</td> -</tr> -<tr> -<td>2021-06-01</td> -<td>Failed</td> -<td>177</td> -</tr> -<tr> -<td>2021-07-01</td> -<td>Completed</td> -<td>1135</td> -</tr> -<tr> -<td>2021-07-01</td> -<td>Draft</td> -<td>399</td> -</tr> -<tr> -<td>2021-07-01</td> -<td>Failed</td> -<td>193</td> -</tr> -<tr> -<td>2021-07-01</td> -<td>Ready</td> -<td>13</td> -</tr> -</tbody> -</table> -<p>Task docs from May and June are mostly in states <code>Completed</code> or <code>Failed</code>. In May, you could say with high confidence that the <em>completion rate</em> for this task was 749/(749+226) or 76%. Task docs in July have end dates in the future still, so they are in state <code>Draft</code> or <code>Ready</code>.</p> -<p><strong>Why are tasks Cancelled?</strong> - A task document is cancelled when <code>tasks.js</code> schedules the task event (<code>appliesToType</code> and <code>appliesIf</code> both pass), and later does not schedule the task event. So in this <em>first assessment</em> scenario, a likely cause of task cancellation would be that a contact was deleted, muted, or dead. The task document is created when <code>appliesIf</code> returns true. When the contact is muted, the task disappears from the UI, and the task document is moved to state <em>Cancelled</em>.</p> -<p><strong>How can there be documents in state &ldquo;Draft&rdquo; in June?</strong> - The state <em>Draft</em> means that the task event is <em>scheduled in the future</em>. How can these documents with due dates in the past (June) be in a state which says they are in the future? <strong>Task documents are calculated and updated on the <em>user&rsquo;s device</em></strong>, so the most likely explanation is that the user hasn&rsquo;t synced. <a href="https://forum.communityhealthtoolkit.org/t/task-state-for-tasks-whose-enddate-is-in-the-past/1011">Other potential explanations are possible</a>.</p> -<h2 id="task-data-considerations">Task data considerations</h2> -<h3 id="performance">Performance</h3> -<p>Performance of CHT Applications is a major factor for many users and partners. The CHT Core is designed and tested to work on low-cost devices, but tasks have the potential to cause performance problems by creating too many task documents or performing excessive computations. Be mindful of the task documents which will be created by your tasks. Monitor the number of task documents being created in production.</p> -<h3 id="completion-vs-cancellation">Completion vs Cancellation</h3> -<p>A task can &ldquo;disappear&rdquo; because <code>appliesIf</code> returns false or because <code>resolvedIf</code> returns true. To the user the experience is identical - but the difference is in the data.</p> -<p><code>resolvedIf</code> should contain only your programmatic <em>task success criteria</em>. Everything else should be in <code>appliesIf</code>.</p> -<table> -<thead> -<tr> -<th>Resultant State</th> -<th>appliesIf</th> -<th>resolvedIf</th> -</tr> -</thead> -<tbody> -<tr> -<td>Draft/Ready/Failed</td> -<td>true</td> -<td>false</td> -</tr> -<tr> -<td>Completed</td> -<td>true</td> -<td>true</td> -</tr> -<tr> -<td>Cancelled</td> -<td>false</td> -<td>-</td> -</tr> -</tbody> -</table> -<h3 id="testing-task-document-data">Testing task document data</h3> -<p>The <a href="http://docs.communityhealthtoolkit.org/cht-conf-test-harness/">medic-conf-test-harness</a> is useful for making assertions about the expected behaviour of tasks in different user scenarios. The <a href="https://docs.communityhealthtoolkit.org/cht-conf-test-harness/Harness.html#countTaskDocsByState">countTaskDocsByState</a> interface is relevant for making assertions about task document creation and state.</p> -<h2 id="frequently-asked-questions">Frequently Asked Questions</h2> -<ul> -<li><a href="https://forum.communityhealthtoolkit.org/t/are-task-documents-indefinitely-on-users-devices/1432">Are task documents indefinitely on user&rsquo;s devices?</a></li> -<li><a href="https://forum.communityhealthtoolkit.org/t/task-state-for-tasks-whose-enddate-is-in-the-past/1011">&ldquo;State&rdquo; for tasks whose endDate is in the past</a></li> -</ul>Apps: Understanding the parameters in the Task Schemahttps://docs.communityhealthtoolkit.org/apps/guides/tasks/task-schema-parameters/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/tasks/task-schema-parameters/ -<div class="pageinfo pageinfo-primary"> -<p>This guide explains the parameters available in the Task Schema and important constraints governing the design of tasks.</p> -<ul> -<li>Useful knowledge if you are stuck writing your first <code>appliesIf</code> predicate</li> -<li>Understanding the data which is available in the task system and important constraints</li> -<li>Understand the special significance of the appliesTo attribute</li> -</ul> -</div> -<p>Let&rsquo;s synthesize some knowledge about CHT applications to help clarify what is happening within the task system:</p> -<ol> -<li> -<p>All contacts in CHT applications are organised into hierarchies. For more information, read the <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/contact-and-users-1/">Contact and User Management Tutorial</a> or <a href="https://docs.communityhealthtoolkit.org/core/overview/db-schema/#contacts-persons-and-places">schema for contact documents</a>.</p> -</li> -<li> -<p>All reports in the system are linked to one (and only one) contact. For more information, read <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/app-forms/">App Forms Tutorial</a> or the <a href="https://docs.communityhealthtoolkit.org/core/overview/db-schema/#reports">schema for report documents</a>.</p> -</li> -<li> -<p>Documents are stored <a href="https://docs.communityhealthtoolkit.org/apps/guides/data/hydration/">minified</a> (not hydrated). All data that is passed into the tasks system is minified.</p> -</li> -<li> -<p>Settings which control the documents which are available on the user&rsquo;s device are an important considerations to remember (eg <a href="https://docs.communityhealthtoolkit.org/apps/guides/performance/replication/#depth">replication depth</a> or <a href="https://docs.communityhealthtoolkit.org/apps/guides/performance/purging/">purging</a>) since both tasks can only process docs which are present on the device.</p> -</li> -</ol> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Key Point</h4> -The code in <code>tasks.js</code> runs on the user&rsquo;s devices, not in the cloud and not on a server. -</div> -<h2 id="an-important-constraint-of-the-tasks-system">An important constraint of the tasks system</h2> -<p>Every contact and every report on the user&rsquo;s device is processed by tasks. That said, it is important to remember that this processing is scoped to happen <strong>one contact at a time</strong>. The code in <code>tasks.js</code> knows about one contact, but it is not possible to simultaneously know about that contact&rsquo;s siblings, descendents, ancestors, etc.</p> -<p>With this constraint in mind, we can infer that tasks cannot know the answer to questions like:</p> -<ul> -<li>Is the sibling of this patient ill?</li> -<li>Does this family have active patients?</li> -<li>Are there cases of tuberculosis in a neighbouring household?</li> -</ul> -<h2 id="the-special-significance-of-the-appliesto-attribute">The special significance of the appliesTo attribute</h2> -<p>The <a href="https://docs.communityhealthtoolkit.org/apps/reference/tasks/#tasksjs">task.js schema</a> includes the noteworthy attribute <code>appliesTo</code> which has two options: <code>contacts</code> and <code>reports</code>. This attribute is important! It changes the algorithm used to process the task, and the meaning of other attributes in the schema.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Hint</h4> -<code>appliesTo</code> is important. When you&rsquo;re ready to write a task, one of the first thing you must decide is the <code>appliesTo</code> value. -</div> -<p>The below algorithmic pseudocode explains the relationship between <code>appliesTo</code> and the attributes <code>appliesIf</code> and <code>appliesToType</code>:</p> -<h3 id="appliesto-contacts">appliesTo: &lsquo;contacts&rsquo;</h3> -<pre tabindex="0"><code class="language-pseudocode" data-lang="pseudocode">algorithm appliesTo is &#39;contacts&#39; -for contact of contacts: -if contact.type is in task.appliesToType: -if task.appliesIf(contact): -create task events -</code></pre><ul> -<li><code>appliesToType</code> filters based on the contact document&rsquo;s <code>contact_type</code> value</li> -<li><code>appliesIf</code> predicate is called once per contact (even if that contact has no reports)</li> -<li><code>appliesIf(c)</code> is passed information about the contact (<code>c.contact</code>), and an array of all the contact&rsquo;s reports (<code>c.reports</code>)</li> -<li><code>events[].dueDate</code> defaults to the contact&rsquo;s creation date</li> -<li>Results in up to one task schedule per contact</li> -</ul> -<h3 id="appliesto-reports">appliesTo: &lsquo;reports&rsquo;</h3> -<pre tabindex="0"><code class="language-pseudocode" data-lang="pseudocode">algorithm appliesTo is &#39;reports&#39; -for contact of contacts: -for report of contact.reports: -if report.form is in task.appliesToType: -if task.appliesIf(contact, report): -create task events -</code></pre><ul> -<li><code>appliesToType</code> filters based on the report document&rsquo;s <code>form</code> value</li> -<li><code>appliesIf</code> predicate is called once per report</li> -<li><code>appliesIf(c, report)</code> is passed information about the contact (<code>c.contact</code>), an array of all the contact&rsquo;s reports (<code>c.reports</code>), and the current report being iterated on <code>report</code></li> -<li><code>events[].dueDate</code> defaults to the report&rsquo;s creation date</li> -<li>Can result in multiple, potentially overlapping task schedules per contact</li> -</ul> \ No newline at end of file +Tasks on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/guides/tasks/Recent content in Tasks on Community Health ToolkitHugo -- gohugo.ioenPassing data from a task into the app formhttps://docs.communityhealthtoolkit.org/apps/guides/tasks/pass-data-to-form/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/tasks/pass-data-to-form/This guide explains how to pass data from a task into the action application form. +Prerequisites Complex Tasks Tutorial Application Forms Tutorial Scenario Let&rsquo;s look deeper at the scenario from the Complex Tasks Tutorial where we have an ANC follow-up task which recurs eight times, and we want to ask the user different questions on the first and last follow-up. +Developing the task From the Complex Tasks Tutorial, here is the task.Querying Task Documentshttps://docs.communityhealthtoolkit.org/apps/guides/tasks/query-task-data/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/tasks/query-task-data/This guide explains the data which results from tasks and how to query it. +Write a PostgreSQL query to examine task data Build deeper understanding of task data Present some data considerations of which task authors should remain mindful Prerequisites Data Flows for Analytics Querying task data The task system running on each user&rsquo;s device is powered by task documents and those task documents sync to the server and to PostgreSQL just like a contact or a report.Understanding the parameters in the Task Schemahttps://docs.communityhealthtoolkit.org/apps/guides/tasks/task-schema-parameters/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/tasks/task-schema-parameters/This guide explains the parameters available in the Task Schema and important constraints governing the design of tasks. +Useful knowledge if you are stuck writing your first appliesIf predicate Understanding the data which is available in the task system and important constraints Understand the special significance of the appliesTo attribute Let&rsquo;s synthesize some knowledge about CHT applications to help clarify what is happening within the task system: +All contacts in CHT applications are organised into hierarchies. \ No newline at end of file diff --git a/apps/guides/tasks/pass-data-to-form/index.html b/apps/guides/tasks/pass-data-to-form/index.html index 9820da2f9e..446f2c4aea 100644 --- a/apps/guides/tasks/pass-data-to-form/index.html +++ b/apps/guides/tasks/pass-data-to-form/index.html @@ -1,9 +1,9 @@ -Passing data from a task into the app form | Community Health Toolkit +Passing data from a task into the app form | Community Health Toolkit

    Passing data from a task into the app form

    Demonstrates how to pass data into an application form via tasks

    This guide explains how to pass data from a task into the action application form.

    Prerequisites

    Scenario

    Let’s look deeper at the scenario from the Complex Tasks Tutorial where we have an ANC follow-up task which recurs eight times, and we want to ask the user different questions on the first and last follow-up.

    Developing the task

    From the Complex Tasks Tutorial, here is the task.

    const { DateTime } = require('luxon');
    + Create project issue

    Passing data from a task into the app form

    Demonstrates how to pass data into an application form via tasks

    This guide explains how to pass data from a task into the action application form.

    Prerequisites

    Scenario

    Let’s look deeper at the scenario from the Complex Tasks Tutorial where we have an ANC follow-up task which recurs eight times, and we want to ask the user different questions on the first and last follow-up.

    Developing the task

    From the Complex Tasks Tutorial, here is the task.

    const { DateTime } = require('luxon');
     
     module.exports = {
       name: 'pnc-after-pregnancy',
    @@ -355,7 +355,8 @@
     Quick Guides >
     Forms >
     Form Inputs

    Data accessible from within CHT forms

    -

    Last modified 06.04.2023: 1008 - Fix Partials (#1009) (4938b3e0)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/tasks/query-task-data/index.html b/apps/guides/tasks/query-task-data/index.html index ba52b7d0f7..70719f990f 100644 --- a/apps/guides/tasks/query-task-data/index.html +++ b/apps/guides/tasks/query-task-data/index.html @@ -1,9 +1,9 @@ -Querying Task Documents | Community Health Toolkit +Querying Task Documents | Community Health Toolkit

    Querying Task Documents

    Querying the data which results from an example task. Notes on the performance implications of tasks.

    This guide explains the data which results from tasks and how to query it.

    • Write a PostgreSQL query to examine task data
    • Build deeper understanding of task data
    • Present some data considerations of which task authors should remain mindful

    Prerequisites

    Querying task data

    The task system running on each user’s device is powered by task documents and those task documents sync to the server and to PostgreSQL just like a contact or a report. Having task documents in PostgreSQL allows system administrators to analyse how users are interacting with tasks.

    See Also: Data flows for analytics

    First Assessment Completion Rate

    Working with the First Assessment task from the Configuring Tasks Tutorial, let’s try to answer the question What percentage of the scheduled first assessment events have been completed?.

    Let’s query data from the last three months to see how the first assessment task is behaving in production:

    Querying Task Documents

    Querying the data which results from an example task. Notes on the performance implications of tasks.

    This guide explains the data which results from tasks and how to query it.

    • Write a PostgreSQL query to examine task data
    • Build deeper understanding of task data
    • Present some data considerations of which task authors should remain mindful

    Prerequisites

    Querying task data

    The task system running on each user’s device is powered by task documents and those task documents sync to the server and to PostgreSQL just like a contact or a report. Having task documents in PostgreSQL allows system administrators to analyse how users are interacting with tasks.

    See Also: Data flows for analytics

    First Assessment Completion Rate

    Working with the First Assessment task from the Configuring Tasks Tutorial, let’s try to answer the question What percentage of the scheduled first assessment events have been completed?.

    Let’s query data from the last three months to see how the first assessment task is behaving in production:

    SELECT
      date_trunc('month', duedate) AS due_date_month,
      task_state,
      count(*)
    @@ -334,7 +334,8 @@
     : Tasks

    Schema for database objects

    CHT Core Framework > Overview > Data Flows

    An overview of data flows in the CHT for analytics, impact monitoring, and data science

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/tasks/task-schema-parameters/index.html b/apps/guides/tasks/task-schema-parameters/index.html index f81b80a401..189b3fc44b 100644 --- a/apps/guides/tasks/task-schema-parameters/index.html +++ b/apps/guides/tasks/task-schema-parameters/index.html @@ -1,9 +1,9 @@ -Understanding the parameters in the Task Schema | Community Health Toolkit +Understanding the parameters in the Task Schema | Community Health Toolkit

    Understanding the parameters in the Task Schema

    Understanding the data which is passed into Task interfaces

    This guide explains the parameters available in the Task Schema and important constraints governing the design of tasks.

    • Useful knowledge if you are stuck writing your first appliesIf predicate
    • Understanding the data which is available in the task system and important constraints
    • Understand the special significance of the appliesTo attribute

    Let’s synthesize some knowledge about CHT applications to help clarify what is happening within the task system:

    1. All contacts in CHT applications are organised into hierarchies. For more information, read the Contact and User Management Tutorial or schema for contact documents.

    2. All reports in the system are linked to one (and only one) contact. For more information, read App Forms Tutorial or the schema for report documents.

    3. Documents are stored minified (not hydrated). All data that is passed into the tasks system is minified.

    4. Settings which control the documents which are available on the user’s device are an important considerations to remember (eg replication depth or purging) since both tasks can only process docs which are present on the device.

    An important constraint of the tasks system

    Every contact and every report on the user’s device is processed by tasks. That said, it is important to remember that this processing is scoped to happen one contact at a time. The code in tasks.js knows about one contact, but it is not possible to simultaneously know about that contact’s siblings, descendents, ancestors, etc.

    With this constraint in mind, we can infer that tasks cannot know the answer to questions like:

    • Is the sibling of this patient ill?
    • Does this family have active patients?
    • Are there cases of tuberculosis in a neighbouring household?

    The special significance of the appliesTo attribute

    The task.js schema includes the noteworthy attribute appliesTo which has two options: contacts and reports. This attribute is important! It changes the algorithm used to process the task, and the meaning of other attributes in the schema.

    The below algorithmic pseudocode explains the relationship between appliesTo and the attributes appliesIf and appliesToType:

    appliesTo: ‘contacts’

    algorithm appliesTo is 'contacts'
    + Create project issue

    Understanding the parameters in the Task Schema

    Understanding the data which is passed into Task interfaces

    This guide explains the parameters available in the Task Schema and important constraints governing the design of tasks.

    • Useful knowledge if you are stuck writing your first appliesIf predicate
    • Understanding the data which is available in the task system and important constraints
    • Understand the special significance of the appliesTo attribute

    Let’s synthesize some knowledge about CHT applications to help clarify what is happening within the task system:

    1. All contacts in CHT applications are organised into hierarchies. For more information, read the Contact and User Management Tutorial or schema for contact documents.

    2. All reports in the system are linked to one (and only one) contact. For more information, read App Forms Tutorial or the schema for report documents.

    3. Documents are stored minified (not hydrated). All data that is passed into the tasks system is minified.

    4. Settings which control the documents which are available on the user’s device are an important considerations to remember (eg replication depth or purging) since both tasks can only process docs which are present on the device.

    An important constraint of the tasks system

    Every contact and every report on the user’s device is processed by tasks. That said, it is important to remember that this processing is scoped to happen one contact at a time. The code in tasks.js knows about one contact, but it is not possible to simultaneously know about that contact’s siblings, descendents, ancestors, etc.

    With this constraint in mind, we can infer that tasks cannot know the answer to questions like:

    • Is the sibling of this patient ill?
    • Does this family have active patients?
    • Are there cases of tuberculosis in a neighbouring household?

    The special significance of the appliesTo attribute

    The task.js schema includes the noteworthy attribute appliesTo which has two options: contacts and reports. This attribute is important! It changes the algorithm used to process the task, and the meaning of other attributes in the schema.

    The below algorithmic pseudocode explains the relationship between appliesTo and the attributes appliesIf and appliesToType:

    appliesTo: ‘contacts’

    algorithm appliesTo is 'contacts'
       for contact of contacts:
         if contact.type is in task.appliesToType:
           if task.appliesIf(contact):
    @@ -325,7 +325,8 @@
     : Reports

    Schema for database objects

    CHT Applications > Tutorials > Tasks

    Writing and testing a simple task

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/guides/training/index.html b/apps/guides/training/index.html index a981db923d..dda583a66a 100644 --- a/apps/guides/training/index.html +++ b/apps/guides/training/index.html @@ -1,9 +1,9 @@ -Training | Community Health Toolkit +Training | Community Health Toolkit

    Training

    Building and managing training resources

    Onboarding Using a Training App

    Best practices when using a Training App to keep training and production data apart

    Training Cards

    Deploy in-app training cards for remote training.

    Training Cards Resources

    Training cards for CHT

    -

    Last modified 09.03.2023: Training card guide (#970) (fb07ed89)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/training/index.xml b/apps/guides/training/index.xml index d4043f47cd..49e525f0d0 100644 --- a/apps/guides/training/index.xml +++ b/apps/guides/training/index.xml @@ -1,154 +1 @@ -Community Health Toolkit – Traininghttps://docs.communityhealthtoolkit.org/apps/guides/training/Recent content in Training on Community Health ToolkitHugo -- gohugo.ioenApps: Onboarding Using a Training Apphttps://docs.communityhealthtoolkit.org/apps/guides/training/onboarding/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/training/onboarding/ -<p>When onboarding new users, having a dedicated CHT app and instance for training can be helpful; it allows new users to do training exercises with mock data to get familiar with the app, while not having the data from their training interfering with the future use of their CHT app. Different approaches are possible, such as only entering real patient data during training, or manually deleting all the training data, but these methods are less practical for large deployments.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -The suggestions in this guide should be assessed and adapted as needed to benefit a deployment. It is important that users don’t accidentally use the wrong app. The <a href="https://docs.communityhealthtoolkit.org/apps/guides/data/training-instance/">troubleshooting guide</a> can help to monitor and remediate training data being in the production instance, or the opposite. -</div> -<h2 id="setting-up-a-training-app">Setting up a training app</h2> -<p>A separate Android App can be created for training, which would point to a CHT instance dedicated to training. The training instance should have the same configuration as the production instance, and have users created for training. To differentiate the Android app used for training from the production one, create a duplicate of <a href="https://docs.communityhealthtoolkit.org/apps/guides/android/branding/">your flavor</a> and modify the following aspects</p> -<ul> -<li> -<figure class="right col-6 col-lg-4"><a href="training-app.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/guides/training/onboarding/training-app.png" -alt="CHT training app with border and message"/> </a> -</figure> -<strong>Border &amp; Message</strong>: Consider adding a distinctive border and message when using the training app. This can be done by setting the build config field <code>IS_TRAINING_APP</code> to <code>true</code>, as seen in <a href="https://github.com/medic/cht-android/blob/8d077ed08dc3889ef1f4e3bad7231931bca55d87/build.gradle#L212-L216"><code>build.gradle</code></a> for the training version of the Gamma app.</li> -<li><strong>CHT Instance</strong>: In your flavor&rsquo;s <code>res/values/strings.xml</code> file set the <code>app_host</code> string to be the URL of your training instance, as seen in the <a href="https://github.com/medic/cht-android/blob/8d077ed08dc3889ef1f4e3bad7231931bca55d87/src/medicmobilegamma_training/res/values/strings.xml#L3">Gamma Training app</a>. If left the same as the production app both training and production data will end up in your production instance.</li> -<li><strong>Launcher icons</strong>: Consider using completely different icons, or at least change the color of the launcher icons.</li> -<li><strong>App name</strong>: Provide a noticeably different name to the training app. Since app names are often cut short on Android devices, make the change at beginning of the text. For example, <code>CHW App [TRAINING VERSION]</code> may display as <code>CHW App...</code> so it would be better to use <code>[TRAINING] CHW App</code>. The app name is set in the flavor&rsquo;s <code>res/values/strings.xml</code> file, as seen in <a href="https://github.com/medic/cht-android/blob/8d077ed08dc3889ef1f4e3bad7231931bca55d87/src/medicmobilegamma_training/res/values/strings.xml#L4">the Gamma training app</a>.</li> -<li><strong>App ID</strong>: If you want to allow both apps to be on a device at once you will need to make sure your training app has a different <code>applicationId</code>, as seen <a href="https://github.com/medic/cht-android/blob/8d077ed08dc3889ef1f4e3bad7231931bca55d87/build.gradle#L214">in <code>build.gradle</code> for the Gamma training app</a>. -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Keeping the <code>applicationId</code> values the same will make it impossible to have both the training and production apps installed at the same time on a device. If you have a way to install the production app after the training is complete then you may choose to do this to prevent users from using the wrong app. -</div> -</li> -</ul> -<h2 id="switching-from-training-to-production-app">Switching from training to production app</h2> -<p>To avoid having production data in the training app, it is encouraged to <strong>remove</strong> the training app from the device once training is complete.</p> -<p>If the production app can always be installed after the use of the training is complete, then using the same <code>applicationId</code> guarantees that only one of the apps is installed at any given time.</p> -<p>Changing passwords for the training users in an attempt to lock them out is not recommended. In some circumstances a user would be able to continue to use the training app for production use and not have the data sync back to the server.</p> -<p>It is preferable to remove the training app from devices, and <a href="https://docs.communityhealthtoolkit.org/apps/guides/data/training-instance/">monitor the training instance for unexpected activity</a> that can be brought over to the production instance if needed.</p>Apps: Training Cardshttps://docs.communityhealthtoolkit.org/apps/guides/training/training-cards/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/training/training-cards/ -<p><em>Introduced in 4.2.0</em></p> -<p><a href="https://docs.communityhealthtoolkit.org/apps/features/training/">Training Cards</a> enable remote training from within the CHT by showing a sequence of &ldquo;cards&rdquo; containing content provided by App Developers. The content might include information about a newly deployed feature, changes to a <a href="https://docs.communityhealthtoolkit.org/apps/concepts/care-guides/">care guide</a>, or simply a reminder about an underused feature or workflow. Enketo forms are used to display the content, and App Developers can specify a start date, duration, and to which <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-roles/">user roles</a> the cards should be shown. Like <a href="https://docs.communityhealthtoolkit.org/apps/reference/forms/app/">app forms</a>, forms used by training cards will automatically be downloaded to the user’s devices.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Example training forms are available <a href="https://docs.communityhealthtoolkit.org/apps/guides/training/training-cards-resources/">here</a> and provide a good starting point. -</div> -<h1 id="step-1-create-the-training-form">Step 1: Create the training form</h1> -<p>Create an <a href="https://docs.communityhealthtoolkit.org/apps/reference/forms/app/#xlsform">XLS Form</a>. In the following example, the training form is called <code>my_new_feature</code>, it has some text in the <code>label::en</code> column, and some images in the column <code>media::images</code> to illustrate the feature.</p> -<figure class="left col-10"><a href="step-1-xls-form.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/guides/training/training-cards/step-1-xls-form.png"/> </a> -</figure> -<br clear="all"> -<h1 id="step-2-add-the-forms-id">Step 2: Add the form’s ID</h1> -<p>Important, define the <code>form_id</code> located in the <code>settings</code> sheet with the prefix <code>training:</code> and add the form name, otherwise the training card won&rsquo;t display. In our example, the <code>form_id</code> should be <code>training:my_new_feature</code>.</p> -<figure class="left col-10"><a href="step-2-xls-form-id.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/guides/training/training-cards/step-2-xls-form-id.png"/> </a> -</figure> -<br clear="all"> -<h1 id="step-3-configure-the-training-form">Step 3: Configure the training form</h1> -<p>Create a <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/form-properties/#3-define-the-forms-context">properties file</a> to define the starting date of the training, the number of days it will be active, and the user roles that can access the training. In our example, the file name is <code>my_new_feature.properties.json</code> and contains the following properties:</p> -<pre tabindex="0"><code>{ -&#34;title&#34;: &#34;&#34;, -&#34;context&#34;: { -&#34;start_date&#34;: &#34;2023-07-13&#34;, -&#34;duration&#34;: 60, -&#34;user_roles&#34;: [ &#34;nurse&#34; ] -} -} -</code></pre><p>In the example above, the training cards could be shown to any user with the &ldquo;nurse&rdquo; role between July 13, 2023 and September 11, 2023 (inclusive). See more information about these configuration settings below:</p> -<table> -<thead> -<tr> -<th>Property</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>&ldquo;title&rdquo;</td> -<td>Optional. Enketo’s form title that is displayed under the modal’s header section. Leave this property empty if you don’t need to display a title.</td> -</tr> -<tr> -<td>&ldquo;start_date&rdquo;</td> -<td>Optional. Define the training start day using <code>yyyy-mm-dd</code> format. If not defined then the training will start immediately.</td> -</tr> -<tr> -<td>&ldquo;duration&rdquo;</td> -<td>Optional. Number of days this training should be active. If not defined then the training will never expire.</td> -</tr> -<tr> -<td>&ldquo;user_roles&rdquo;</td> -<td>Optional. List of user roles that can access this training. If not defined then all users can access the training.</td> -</tr> -</tbody> -</table> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Users with an admin role can access training cards but they need to have a contact associated in the <code>org.couchdb.user:[user-name]</code> document from CouchDB. -</div> -<h1 id="step-4-add-multimedia-to-the-training-form">Step 4: Add multimedia to the training form</h1> -<p>If your training form has images, create a folder with the same name as the form and add <code>-media</code> suffix. In our example, the form name is <code>my_new_feature</code>, then the folder name should be <code>my_new_feature-media</code>.</p> -<p>Inside that new folder, make another one called images and put inside all the <code>images</code> that your form needs. See more about <a href="https://docs.communityhealthtoolkit.org/apps/guides/forms/multimedia/">multimedia in forms</a>.</p> -<h1 id="step-5-put-everything-in-the-right-place">Step 5: Put everything in the right place</h1> -<p>In your project configuration folder, place the XLS form, the properties file, and the media folder inside <code>/forms/training/</code>. The file structure should look like this:</p> -<figure class="left col-10"><a href="step-5-file-structure.png"> -<img src="https://docs.communityhealthtoolkit.org/apps/guides/training/training-cards/step-5-file-structure.png"/> </a> -</figure> -<br clear="all"> -<h1 id="step-6-convert-and-upload-the-training-form">Step 6: Convert and upload the training form</h1> -<p>Open the terminal and use the latest version of the <code>cht</code> command line tool to convert and upload the training form:</p> -<p>Convert the form by running this command:</p> -<pre tabindex="0"><code>cht --url=[instance_url] convert-training-forms -- [form_name] -</code></pre><p>Example:</p> -<pre tabindex="0"><code>cht --url=http://admin-user:secretpass@my-xyz-project.org convert-training-forms -- my_new_feature -</code></pre><p>Upload the form by running this command:</p> -<pre tabindex="0"><code>cht --url=[instance_url] upload-training-forms -- [form_name] -</code></pre><p>Example:</p> -<pre tabindex="0"><code>cht --url=http://admin-user:secretpass@my-xyz-project.org upload-training-forms -- my_new_feature -</code></pre><h1 id="step-7-verify-and-monitor">Step 7: Verify and monitor</h1> -<p>At this point the training cards are ready to use. You can verify by logging in with a user that has a valid role assigned for the training card, on the day that the training starts. Remember, to sync the app to see the results.</p> -<p>Using <a href="https://docs.communityhealthtoolkit.org/apps/guides/performance/telemetry/">telemetry</a> you can monitor user interaction with each training card &ldquo;set&rdquo;. Some key metrics you can view include:</p> -<ol> -<li>Number of times each user was presented with training cards</li> -<li>The date(s) each user was presented with training cards</li> -<li>Number of times each user completed a training card set*</li> -<li>The min/max amount of time each user spent viewing the training cards</li> -<li>The device_id of any device from which the user saw the training cards</li> -</ol> -<p><em>* Training Cards are designed to only be viewed once so this should normally only ever be 0 or 1. It is possible that a user deletes the training &ldquo;report&rdquo;, in which case they would be presented with training cards again.</em></p> -<p>Example SQL:</p> -<pre tabindex="0"><code>SELECT -doc#&gt;&gt;&#39;{metadata,user}&#39; AS cht_user, -doc#&gt;&gt;&#39;{metadata,deviceId}&#39; AS device_id, -concat(doc#&gt;&gt;&#39;{metadata,year}&#39;, &#39;-&#39;, doc#&gt;&gt;&#39;{metadata,month}&#39;, &#39;-&#39;,doc#&gt;&gt;&#39;{metadata,day}&#39;)::date AS telemetry_date, -COALESCE(doc#&gt;&gt;&#39;{metrics,enketo:training:&lt;my_new_feature&gt;:add:render,count}&#39;,&#39;0&#39;)::int AS count_render_training, -COALESCE(doc#&gt;&gt;&#39;{metrics,enketo:training:&lt;my_new_feature&gt;:add:user_edit_time,count}&#39;,&#39;0&#39;)::int AS count_submit_training, -COALESCE(doc#&gt;&gt;&#39;{metrics,enketo:training:&lt;my_new_feature&gt;:add:user_edit_time,max}&#39;,&#39;0&#39;)::int/1000 AS max_seconds_on_form, -COALESCE(doc#&gt;&gt;&#39;{metrics,enketo:training:&lt;my_new_feature&gt;:add:user_edit_time,min}&#39;,&#39;0&#39;)::int/1000 AS min_seconds_on_form -FROM -couchdb_users_meta -WHERE -doc-&gt;&gt;&#39;type&#39;=&#39;telemetry&#39; -AND doc#&gt;&#39;{metadata}&#39; ? &#39;day&#39; -AND doc#&gt;&#39;{metrics}&#39; ? &#39;enketo:training:&lt;my_new_feature&gt;:add:render&#39; -ORDER BY -telemetry_date DESC -</code></pre>Apps: Training Cards Resourceshttps://docs.communityhealthtoolkit.org/apps/guides/training/training-cards-resources/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/training/training-cards-resources/ -<p>This is a list of <a href="https://docs.communityhealthtoolkit.org/apps/features/training/">trainings cards</a> that you can use in your project to train users about new updates in CHT. Read the <a href="https://docs.communityhealthtoolkit.org/apps/guides/training/training-cards/">step by step guide</a> to deploy the training cards.</p> -<h2 id="floating-action-button">Floating Action Button</h2> -<p><em>Introduced in 4.2.0</em></p> -<p>The additive actions (creating reports, places, people, etc&hellip;) have moved from the bottom action bar to a Floating Action Button. Use this training to introduce the change to your users.</p> -<p>Get the training card files <a href="https://github.com/medic/cht-docs/tree/main/content/en/apps/guides/training/training-cards-resources/available-trainings/floating-action-button">here</a>.</p> -<figure class="left col-10"> -<img src="images/floating-action-button.png"/> -</figure> -<br clear="all"> -<h2 id="more-options-menu">More Options Menu</h2> -<p><em>Introduced in 4.2.0</em></p> -<p>The Edit, Delete and Export actions have been moved to the More Options menu. Use this training to introduce the change to your users.</p> -<p>Get the training card files <a href="https://github.com/medic/cht-docs/tree/main/content/en/apps/guides/training/training-cards-resources/available-trainings/more-options">here</a>.</p> -<figure class="left col-10"> -<img src="images/more-options.png"/> -</figure> -<br clear="all"> \ No newline at end of file +Training on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/guides/training/Recent content in Training on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file diff --git a/apps/guides/training/onboarding/index.html b/apps/guides/training/onboarding/index.html index be8d5ab6ab..0bf19573fb 100644 --- a/apps/guides/training/onboarding/index.html +++ b/apps/guides/training/onboarding/index.html @@ -1,9 +1,9 @@ -Onboarding Using a Training App | Community Health Toolkit +Onboarding Using a Training App | Community Health Toolkit

    Onboarding Using a Training App

    Best practices when using a Training App to keep training and production data apart

    When onboarding new users, having a dedicated CHT app and instance for training can be helpful; it allows new users to do training exercises with mock data to get familiar with the app, while not having the data from their training interfering with the future use of their CHT app. Different approaches are possible, such as only entering real patient data during training, or manually deleting all the training data, but these methods are less practical for large deployments.

    Setting up a training app

    A separate Android App can be created for training, which would point to a CHT instance dedicated to training. The training instance should have the same configuration as the production instance, and have users created for training. To differentiate the Android app used for training from the production one, create a duplicate of your flavor and modify the following aspects

    • CHT training app with border and message
      Border & Message: Consider adding a distinctive border and message when using the training app. This can be done by setting the build config field IS_TRAINING_APP to true, as seen in build.gradle for the training version of the Gamma app.
    • CHT Instance: In your flavor’s res/values/strings.xml file set the app_host string to be the URL of your training instance, as seen in the Gamma Training app. If left the same as the production app both training and production data will end up in your production instance.
    • Launcher icons: Consider using completely different icons, or at least change the color of the launcher icons.
    • App name: Provide a noticeably different name to the training app. Since app names are often cut short on Android devices, make the change at beginning of the text. For example, CHW App [TRAINING VERSION] may display as CHW App... so it would be better to use [TRAINING] CHW App. The app name is set in the flavor’s res/values/strings.xml file, as seen in the Gamma training app.
    • App ID: If you want to allow both apps to be on a device at once you will need to make sure your training app has a different applicationId, as seen in build.gradle for the Gamma training app.

    Switching from training to production app

    To avoid having production data in the training app, it is encouraged to remove the training app from the device once training is complete.

    If the production app can always be installed after the use of the training is complete, then using the same applicationId guarantees that only one of the apps is installed at any given time.

    Changing passwords for the training users in an attempt to lock them out is not recommended. In some circumstances a user would be able to continue to use the training app for production use and not have the data sync back to the server.

    It is preferable to remove the training app from devices, and monitor the training instance for unexpected activity that can be brought over to the production instance if needed.


    CHT Applications > + Create project issue

    Onboarding Using a Training App

    Best practices when using a Training App to keep training and production data apart

    When onboarding new users, having a dedicated CHT app and instance for training can be helpful; it allows new users to do training exercises with mock data to get familiar with the app, while not having the data from their training interfering with the future use of their CHT app. Different approaches are possible, such as only entering real patient data during training, or manually deleting all the training data, but these methods are less practical for large deployments.

    Setting up a training app

    A separate Android App can be created for training, which would point to a CHT instance dedicated to training. The training instance should have the same configuration as the production instance, and have users created for training. To differentiate the Android app used for training from the production one, create a duplicate of your flavor and modify the following aspects

    • CHT training app with border and message
      Border & Message: Consider adding a distinctive border and message when using the training app. This can be done by setting the build config field IS_TRAINING_APP to true, as seen in build.gradle for the training version of the Gamma app.
    • CHT Instance: In your flavor’s res/values/strings.xml file set the app_host string to be the URL of your training instance, as seen in the Gamma Training app. If left the same as the production app both training and production data will end up in your production instance.
    • Launcher icons: Consider using completely different icons, or at least change the color of the launcher icons.
    • App name: Provide a noticeably different name to the training app. Since app names are often cut short on Android devices, make the change at beginning of the text. For example, CHW App [TRAINING VERSION] may display as CHW App... so it would be better to use [TRAINING] CHW App. The app name is set in the flavor’s res/values/strings.xml file, as seen in the Gamma training app.
    • App ID: If you want to allow both apps to be on a device at once you will need to make sure your training app has a different applicationId, as seen in build.gradle for the Gamma training app.

    Switching from training to production app

    To avoid having production data in the training app, it is encouraged to remove the training app from the device once training is complete.

    If the production app can always be installed after the use of the training is complete, then using the same applicationId guarantees that only one of the apps is installed at any given time.

    Changing passwords for the training users in an attempt to lock them out is not recommended. In some circumstances a user would be able to continue to use the training app for production use and not have the data sync back to the server.

    It is preferable to remove the training app from devices, and monitor the training instance for unexpected activity that can be brought over to the production instance if needed.


    CHT Applications > Quick Guides > Android > CHT Android Flavors

    Branding the CHT Android applications

    CHT Applications > Examples > Remote Training

    App and care workflow training using remote capabilities.

    -

    Last modified 09.03.2023: Training card guide (#970) (fb07ed89)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/training/onboarding/index.xml b/apps/guides/training/onboarding/index.xml index b794b00698..276a1ae1cd 100644 --- a/apps/guides/training/onboarding/index.xml +++ b/apps/guides/training/onboarding/index.xml @@ -1 +1 @@ -Community Health Toolkit – Onboarding Using a Training Apphttps://docs.communityhealthtoolkit.org/apps/guides/training/onboarding/Recent content in Onboarding Using a Training App on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file +Onboarding Using a Training App on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/guides/training/onboarding/Recent content in Onboarding Using a Training App on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file diff --git a/apps/guides/training/training-cards-resources/index.html b/apps/guides/training/training-cards-resources/index.html index 6ad378466e..f5abf1ee98 100644 --- a/apps/guides/training/training-cards-resources/index.html +++ b/apps/guides/training/training-cards-resources/index.html @@ -1,9 +1,9 @@ -Training Cards Resources | Community Health Toolkit +Training Cards Resources | Community Health Toolkit

    Training Cards Resources

    Training cards for CHT

    This is a list of trainings cards that you can use in your project to train users about new updates in CHT. Read the step by step guide to deploy the training cards.

    Floating Action Button

    Introduced in 4.2.0

    The additive actions (creating reports, places, people, etc…) have moved from the bottom action bar to a Floating Action Button. Use this training to introduce the change to your users.

    Get the training card files here.


    More Options Menu

    Introduced in 4.2.0

    The Edit, Delete and Export actions have been moved to the More Options menu. Use this training to introduce the change to your users.

    Get the training card files here.



    CHT Applications > + Create project issue

    Training Cards Resources

    Training cards for CHT

    This is a list of trainings cards that you can use in your project to train users about new updates in CHT. Read the step by step guide to deploy the training cards.

    Floating Action Button

    Introduced in 4.2.0

    The additive actions (creating reports, places, people, etc…) have moved from the bottom action bar to a Floating Action Button. Use this training to introduce the change to your users.

    Get the training card files here.


    More Options Menu

    Introduced in 4.2.0

    The Edit, Delete and Export actions have been moved to the More Options menu. Use this training to introduce the change to your users.

    Get the training card files here.



    CHT Applications > Features > Training

    Remotely train health workers

    CHT Applications > Quick Guides > Training > Training Cards

    Deploy in-app training cards for remote training.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/training/training-cards-resources/index.xml b/apps/guides/training/training-cards-resources/index.xml index 7ab6b7c213..a767c6ac3f 100644 --- a/apps/guides/training/training-cards-resources/index.xml +++ b/apps/guides/training/training-cards-resources/index.xml @@ -1 +1 @@ -Community Health Toolkit – Training Cards Resourceshttps://docs.communityhealthtoolkit.org/apps/guides/training/training-cards-resources/Recent content in Training Cards Resources on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file +Training Cards Resources on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/guides/training/training-cards-resources/Recent content in Training Cards Resources on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file diff --git a/apps/guides/training/training-cards/index.html b/apps/guides/training/training-cards/index.html index ea3bfab64a..a437d09980 100644 --- a/apps/guides/training/training-cards/index.html +++ b/apps/guides/training/training-cards/index.html @@ -1,9 +1,9 @@ -Training Cards | Community Health Toolkit +Training Cards | Community Health Toolkit

    Training Cards

    Deploy in-app training cards for remote training.

    Introduced in 4.2.0

    Training Cards enable remote training from within the CHT by showing a sequence of “cards” containing content provided by App Developers. The content might include information about a newly deployed feature, changes to a care guide, or simply a reminder about an underused feature or workflow. Enketo forms are used to display the content, and App Developers can specify a start date, duration, and to which user roles the cards should be shown. Like app forms, forms used by training cards will automatically be downloaded to the user’s devices.

    Step 1: Create the training form

    Create an XLS Form. In the following example, the training form is called my_new_feature, it has some text in the label::en column, and some images in the column media::images to illustrate the feature.


    Step 2: Add the form’s ID

    Important, define the form_id located in the settings sheet with the prefix training: and add the form name, otherwise the training card won’t display. In our example, the form_id should be training:my_new_feature.


    Step 3: Configure the training form

    Create a properties file to define the starting date of the training, the number of days it will be active, and the user roles that can access the training. In our example, the file name is my_new_feature.properties.json and contains the following properties:

    {
    + Create project issue

    Training Cards

    Deploy in-app training cards for remote training.

    Introduced in 4.2.0

    Training Cards enable remote training from within the CHT by showing a sequence of “cards” containing content provided by App Developers. The content might include information about a newly deployed feature, changes to a care guide, or simply a reminder about an underused feature or workflow. Enketo forms are used to display the content, and App Developers can specify a start date, duration, and to which user roles the cards should be shown. Like app forms, forms used by training cards will automatically be downloaded to the user’s devices.

    Step 1: Create the training form

    Create an XLS Form. In the following example, the training form is called my_new_feature, it has some text in the label::en column, and some images in the column media::images to illustrate the feature.


    Step 2: Add the form’s ID

    Important, define the form_id located in the settings sheet with the prefix training: and add the form name, otherwise the training card won’t display. In our example, the form_id should be training:my_new_feature.


    Step 3: Configure the training form

    Create a properties file to define the starting date of the training, the number of days it will be active, and the user roles that can access the training. In our example, the file name is my_new_feature.properties.json and contains the following properties:

    {
       "title": "",
       "context": {
         "start_date": "2023-07-13",
    @@ -341,7 +341,8 @@
     Remote Training

    App and care workflow training using remote capabilities.

    CHT Applications > Examples > Learning & Care

    An integration built to pilot the integrated workflows focused on CHW remote learning and care support for COVID-19.

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/guides/training/training-cards/index.xml b/apps/guides/training/training-cards/index.xml index 2fb16e8d59..1584512258 100644 --- a/apps/guides/training/training-cards/index.xml +++ b/apps/guides/training/training-cards/index.xml @@ -1 +1 @@ -Community Health Toolkit – Training Cardshttps://docs.communityhealthtoolkit.org/apps/guides/training/training-cards/Recent content in Training Cards on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file +Training Cards on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/guides/training/training-cards/Recent content in Training Cards on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file diff --git a/apps/guides/updates/collect-forms-update/index.html b/apps/guides/updates/collect-forms-update/index.html index d9d6fe2d40..0f00d4fcac 100644 --- a/apps/guides/updates/collect-forms-update/index.html +++ b/apps/guides/updates/collect-forms-update/index.html @@ -1,9 +1,9 @@ -Update Collect Forms Remotely | Community Health Toolkit +Update Collect Forms Remotely | Community Health Toolkit

    Update Collect Forms Remotely

    How to do over-the-air updates of forms in Collect

    To do over the air Medic Collect form updates via HTTP rather than sending APKs which have a long manual install process, follow the steps below:

    1. Have your xls forms ready in the folder.
    • They should use underscore as name separators. e.g form_name.xlsx
    • They should have form_id and name properties in the settings

    Name property

    1. Upload the forms to the instance using cht-conf Using the upload-collect-forms action as shown below.
    cht --instance=user:pass@instancename.app.medicmobile.org upload-collect-forms
    + Create project issue

    Update Collect Forms Remotely

    How to do over-the-air updates of forms in Collect

    To do over the air Medic Collect form updates via HTTP rather than sending APKs which have a long manual install process, follow the steps below:

    1. Have your xls forms ready in the folder.
    • They should use underscore as name separators. e.g form_name.xlsx
    • They should have form_id and name properties in the settings

    Name property

    1. Upload the forms to the instance using cht-conf Using the upload-collect-forms action as shown below.
    cht --instance=user:pass@instancename.app.medicmobile.org upload-collect-forms
     
    1. Go to the Collect App. Delete All forms then go to Get Blank Form and select all the forms you need.

    Troubleshooting

    When you go to Get Blank Forms and instead of getting a list of the forms available, you get a pop-up error which has a portion of this message instead

    ...OpenRosa Version 1.0 standard: Forms list entry 1 is missing one or more tags: formId, name or downloadUrl
     

    This means you probably uploaded a XLS file without a name or form_id property. To find out which form is missing that, use this command:

    curl -vvvv -H "x-openrosa-version: 1" http://user:pass@host:port/api/v1/forms
     

    Should bring a list like this one

    Xform List

    Go through the list and see which form has a missing <name> or <formID> property. Add it and reupload the forms using cht-conf again.

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/guides/updates/feature-flags/index.html b/apps/guides/updates/feature-flags/index.html index 99a511ccca..3416e86107 100644 --- a/apps/guides/updates/feature-flags/index.html +++ b/apps/guides/updates/feature-flags/index.html @@ -1,9 +1,9 @@ -Feature Flags | Community Health Toolkit +Feature Flags | Community Health Toolkit

    Feature Flags

    How to roll out features to select users

    Some CHT Core features can be enabled for specific users only. This can be particularly helpful for features that require training. The updated or “new” version will generally be the system default, but users can be configured to see the “old” version. If you do nothing when you upgrade, users will automatically start seeing the new version.

    Configuration

    If the specific CHT Core feature supports feature flagging, users can be configured to see the “old” version by means of a permission. The permission can be added to existing Roles, or for more granular control and rolling out by cohorts, you can create a new Role with the one permission and add that Role to desired users. The permission will not be included in app_settings automatically so if you want it to be selectable from the Admin app user interface, you will need to add it to app_settings.

    Roll-out Scenarios

    The table below illustrates some potential rollout scenarios and recommended Actions.

    ScenarioActions
    [Default] All users see the “new” version once you upgradeNone, just upgrade.
    All users see the “old” version once you upgrade. All users see the “new” version at the same time in the future.Add the permission to all Roles before you upgrade. When you are ready to transition everyone to the the new version, just remove the permission form all Roles.
    All users see the “old” version once you upgrade. Roll out the “new” version in cohorts over time.Create a new Role with the one permission and add that role to all users. When you are ready to transition a cohort of users, remove the Role from that cohort.
    Some cohorts see the “old” version/some see the “new” version once you upgrade. All cohorts seeing the “old” version start seeing the “new” version at the same time.Create a new Role with the one permission and add it to selected users. When you are ready to transition all cohorts to the “new” version, remove the Role from all users.
    Some cohorts see the “old” version/some see the “new” version once you upgrade. Roll out the “new” version to applicable cohorts over time.Create a new Role with the one permission and add it to selected users. When you are ready to transition a cohort of users, remove the Role from those users.

    CHT Applications > + Create project issue

    Feature Flags

    How to roll out features to select users

    Some CHT Core features can be enabled for specific users only. This can be particularly helpful for features that require training. The updated or “new” version will generally be the system default, but users can be configured to see the “old” version. If you do nothing when you upgrade, users will automatically start seeing the new version.

    Configuration

    If the specific CHT Core feature supports feature flagging, users can be configured to see the “old” version by means of a permission. The permission can be added to existing Roles, or for more granular control and rolling out by cohorts, you can create a new Role with the one permission and add that Role to desired users. The permission will not be included in app_settings automatically so if you want it to be selectable from the Admin app user interface, you will need to add it to app_settings.

    Roll-out Scenarios

    The table below illustrates some potential rollout scenarios and recommended Actions.

    ScenarioActions
    [Default] All users see the “new” version once you upgradeNone, just upgrade.
    All users see the “old” version once you upgrade. All users see the “new” version at the same time in the future.Add the permission to all Roles before you upgrade. When you are ready to transition everyone to the the new version, just remove the permission form all Roles.
    All users see the “old” version once you upgrade. Roll out the “new” version in cohorts over time.Create a new Role with the one permission and add that role to all users. When you are ready to transition a cohort of users, remove the Role from that cohort.
    Some cohorts see the “old” version/some see the “new” version once you upgrade. All cohorts seeing the “old” version start seeing the “new” version at the same time.Create a new Role with the one permission and add it to selected users. When you are ready to transition all cohorts to the “new” version, remove the Role from all users.
    Some cohorts see the “old” version/some see the “new” version once you upgrade. Roll out the “new” version to applicable cohorts over time.Create a new Role with the one permission and add it to selected users. When you are ready to transition a cohort of users, remove the Role from those users.

    CHT Applications > Concepts > Users

    Defining the user roles and their permissions

    CHT Applications > Reference > @@ -309,7 +309,8 @@ Reference > app_settings.json > .roles

    User Roles: Defining the roles that can be assigned to users.

    -

    Last modified 04.05.2023: Update feature flags (#1032) (12c3a650)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/updates/index.html b/apps/guides/updates/index.html index 915497b114..524583af75 100644 --- a/apps/guides/updates/index.html +++ b/apps/guides/updates/index.html @@ -1,9 +1,9 @@ -Managing Updates to Applications and Content | Community Health Toolkit +Managing Updates to Applications and Content | Community Health Toolkit

    Managing Updates to Applications and Content

    Guides for managing updates to CHT applications, their content, and data

    Feature Flags

    How to roll out features to select users

    Moving Contacts within the Hierarchy

    How to safely move contacts

    Preparing to upgrade to CHT 4.0

    Steps to ensure your CHT App will run smoothly on CHT 4.0 and later

    Update Collect Forms Remotely

    How to do over-the-air updates of forms in Collect

    -

    Last modified 05.06.2020: Categorized guides (d26e182e)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/guides/updates/index.xml b/apps/guides/updates/index.xml index 9e12cd5f1f..a7fff47c35 100644 --- a/apps/guides/updates/index.xml +++ b/apps/guides/updates/index.xml @@ -1,448 +1,3 @@ -Community Health Toolkit – Managing Updates to Applications and Contenthttps://docs.communityhealthtoolkit.org/apps/guides/updates/Recent content in Managing Updates to Applications and Content on Community Health ToolkitHugo -- gohugo.ioenApps: Feature Flagshttps://docs.communityhealthtoolkit.org/apps/guides/updates/feature-flags/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/updates/feature-flags/ -<p>Some CHT Core features can be enabled for specific users only. This can be particularly helpful for features that require training. The updated or &ldquo;new&rdquo; version will generally be the system default, but users can be configured to see the &ldquo;old&rdquo; version. If you do nothing when you upgrade, users will automatically start seeing the new version.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Feature flags are used primarily as a way to phase in updates. The old version should be considered deprecated and will be completely removed in a future release. -</div> -<h2 id="configuration">Configuration</h2> -<p>If the specific CHT Core feature supports feature flagging, users can be configured to see the &ldquo;old&rdquo; version by means of a <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-permissions/">permission</a>. The permission can be added to existing Roles, or for more granular control and rolling out by cohorts, you can create a new Role with the one permission and add that Role to desired users. The permission will not be included in <code>app_settings</code> automatically so if you want it to be selectable from the Admin app user interface, you will need to add it to <code>app_settings</code>.</p> -<h2 id="roll-out-scenarios">Roll-out Scenarios</h2> -<p>The table below illustrates some potential rollout scenarios and recommended Actions.</p> -<table> -<thead> -<tr> -<th>Scenario</th> -<th>Actions</th> -</tr> -</thead> -<tbody> -<tr> -<td>[Default] All users see the &ldquo;new&rdquo; version once you upgrade</td> -<td>None, just upgrade.</td> -</tr> -<tr> -<td>All users see the &ldquo;old&rdquo; version once you upgrade. All users see the &ldquo;new&rdquo; version at the same time in the future.</td> -<td>Add the permission to all Roles before you upgrade. When you are ready to transition everyone to the the new version, just remove the permission form all Roles.</td> -</tr> -<tr> -<td>All users see the &ldquo;old&rdquo; version once you upgrade. Roll out the &ldquo;new&rdquo; version in cohorts over time.</td> -<td>Create a new Role with the one permission and add that role to all users. When you are ready to transition a cohort of users, remove the Role from that cohort.</td> -</tr> -<tr> -<td>Some cohorts see the &ldquo;old&rdquo; version/some see the &ldquo;new&rdquo; version once you upgrade. All cohorts seeing the &ldquo;old&rdquo; version start seeing the &ldquo;new&rdquo; version at the same time.</td> -<td>Create a new Role with the one permission and add it to selected users. When you are ready to transition all cohorts to the &ldquo;new&rdquo; version, remove the Role from all users.</td> -</tr> -<tr> -<td>Some cohorts see the &ldquo;old&rdquo; version/some see the &ldquo;new&rdquo; version once you upgrade. Roll out the &ldquo;new&rdquo; version to applicable cohorts over time.</td> -<td>Create a new Role with the one permission and add it to selected users. When you are ready to transition a cohort of users, remove the Role from those users.</td> -</tr> -</tbody> -</table>Apps: Moving Contacts within the Hierarchyhttps://docs.communityhealthtoolkit.org/apps/guides/updates/moving-contacts/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/updates/moving-contacts/ -<p>Contacts are organized into a hierarchy. It is not straight-forward to move contacts from one position in the hierarchy to another because many copies of this hierarchy exist. Use the <code>move-contacts</code> action in <a href="https://github.com/medic/cht-conf"><code>cht-conf</code></a> to assign a new parent to contacts. This command will move the specified contact, all the contacts under that contact, and all reports created by any of those contacts. This action will download all documents that need to be updated, update the lineages within those documents, and then save the updated documents on your local disk. To commit those changes to the database, run the <code>upload-docs</code> action.</p> -<p><strong>Offline users who have contacts removed from their visible hierarchy will not automatically see those contacts disappear. The contact remains on the user&rsquo;s device. Any updates made to the contact (or any reports created for that contact) will <a href="https://github.com/medic/cht-core/issues/5701">silently fail to sync</a>. These users must be encouraged to clear cache and resync!</strong></p> -<p>Also impactful, but less serious - this script can cause significant amounts of changes to the database and offline users who have contacts moved into their visible hierarchy may experience lengthy and bandwidth-intensive synchronizations.</p> -<table> -<thead> -<tr> -<th>Parameter</th> -<th>Description</th> -<th>Required</th> -</tr> -</thead> -<tbody> -<tr> -<td>contacts</td> -<td>Comma delimited list of contact IDs which will be moved</td> -<td>Yes</td> -</tr> -<tr> -<td>parent</td> -<td>ID of the new parent which will be assigned to the provided contacts</td> -<td>Yes</td> -</tr> -<tr> -<td>docDirectoryPath</td> -<td>This action outputs files to local disk at this destination</td> -<td>No. Default <code>json-docs</code></td> -</tr> -</tbody> -</table> -<p>Some constraints when moving contacts:</p> -<ul> -<li><strong>Allowed Parents</strong> - When moving contacts on WebApp &gt;v3.7, your chosen parent must be listed as a valid parent for the contact as defined in the <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/hierarchy/">configuration for place hierarchy</a>. For WebApp &lt;v3.7, the default hierarchy is enforced.</li> -<li><strong>Circular Hierarchy</strong> - Nobody&rsquo;s parent can ever be themself or their child.</li> -<li><strong>Primary Contacts</strong> - Primary contacts must be a descendant of the place for which they are the primary contact. You may need to select a new primary contact for a place through the WebApp if you&rsquo;d like to move a primary contact to a new place in the hierarchy.</li> -<li><strong>Minification</strong> - Due to contact &ldquo;minification&rdquo; (#2635) which was implemented in v2.13, this script should not be used for versions prior to v2.13.</li> -</ul> -<h3 id="examples">Examples</h3> -<p>Move the contacts with the id <code>contact_1</code> and <code>contact_2</code> to have the parent <code>parent_id</code>. The changes will be in the local folder <code>my_folder</code> only for review. Run the second command to upload the changes after review.</p> -<pre><code>cht --instance= move-contacts -- --contacts=contact_1,contact_2 --parent=parent_id --docDirectoryPath=my_folder -cht --local upload-docs -- --docDirectoryPath=my_folder -</code></pre> -<p>Move the contact with the id <code>contact_1</code> to the top of the hierarchy (no parent).</p> -<pre><code>cht --local move-contacts upload-docs -- --contacts=contact_1 --parent=root -</code></pre>Apps: Preparing to upgrade to CHT 4.0https://docs.communityhealthtoolkit.org/apps/guides/updates/preparing-for-4/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/updates/preparing-for-4/ -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -This guide applies to both self-hosted and Medic hosted deployments. -</div> -<h2 id="introduction">Introduction</h2> -<p>Medic uses <a href="https://en.wikipedia.org/wiki/Semver#Semantic_versioning">Semantic Versioning</a> (aka &ldquo;SemVer&rdquo;) which means that the CHT upgrade from the major 3.x version to the 4.x version denotes there are breaking changes. The key to a successful upgrade will be to understand and plan for these breaking changes. Aside from the Docker hosting infrastructure (out of scope for this prep document), the two breaking changes are around CHT Android and Enketo.</p> -<p>While CHT 4.0 has not been released yet, the effort to be prepared can be quite time consuming, especially for large deployments that may need to do handset upgrades in a worst case. The sooner deployments start preparing for the upgrade, the easier it will be when it comes to the upgrade itself. Conveniently, all device and Android app changes to prepare for 4.x are backwards compatible with 3.x. Prepare now, so you will be ready to upgrade sooner than later!</p> -<h2 id="cht-android-v100">CHT Android v1.0.0+</h2> -<p>This change is straightforward in that CHT 4.x no longer supports versions <em>before</em> <code>1.0.0</code>, so deployments need to update their Play Store app. As of this writing, <a href="https://github.com/medic/cht-android/">CHT Android</a> is at <code>1.0.4</code>. Please see the <a href="https://docs.communityhealthtoolkit.org/apps/guides/android/">Android docs</a> on how to update your app and release it. Note that Google&rsquo;s Play Store can often have delays which deployments have no control over. Again, the sooner you start, the better.</p> -<h3 id="versions-in-use">Versions in use</h3> -<p>After you have published your app, you need to instruct your users to check the Play Store for upgrades. You can then check <a href="https://docs.communityhealthtoolkit.org/apps/guides/performance/telemetry/">CHT Telemetry</a> to see what CHT Android versions are in use. Assuming you have <a href="https://github.com/medic/couch2pg">couch2pg set up</a> to pull in your CouchDB data to a PostgreSQL database, this query will list Android versions <code>count</code>s for the current year, broken out by <code>month</code>, <code>year</code> and <code>version</code>:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">SELECT</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">concat</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">doc</span><span style="color:#ce5c00;font-weight:bold">#&gt;&gt;</span><span style="color:#4e9a06">&#39;{metadata,year}&#39;</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06">&#39;-&#39;</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">lpad</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">doc</span><span style="color:#ce5c00;font-weight:bold">#&gt;&gt;</span><span style="color:#4e9a06">&#39;{metadata,month}&#39;</span><span style="color:#000;font-weight:bold">,</span><span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">,</span><span style="color:#4e9a06">&#39;0&#39;</span><span style="color:#000;font-weight:bold">))</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">as</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">telemetry_month</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">doc</span><span style="color:#ce5c00;font-weight:bold">#&gt;&gt;</span><span style="color:#4e9a06">&#39;{device,deviceInfo,app,version}&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">as</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">cht_android_version</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">count</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">distinct</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">doc</span><span style="color:#ce5c00;font-weight:bold">#&gt;&gt;</span><span style="color:#4e9a06">&#39;{metadata,user}&#39;</span><span style="color:#000;font-weight:bold">))</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">AS</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">count_distinct_users</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">count</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">distinct</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">doc</span><span style="color:#ce5c00;font-weight:bold">#&gt;&gt;</span><span style="color:#4e9a06">&#39;{metadata,deviceId}&#39;</span><span style="color:#000;font-weight:bold">))</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">AS</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">count_distinct_devices</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">count</span><span style="color:#000;font-weight:bold">(</span><span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000;font-weight:bold">)</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">AS</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">count_telemetry</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">FROM</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">couchdb_users_meta</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">WHERE</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">doc</span><span style="color:#ce5c00;font-weight:bold">#&gt;&gt;</span><span style="color:#4e9a06">&#39;{type}&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#39;telemetry&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">AND</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">doc</span><span style="color:#ce5c00;font-weight:bold">#&gt;&gt;</span><span style="color:#4e9a06">&#39;{metadata,year}&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">to_char</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">current_date</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#39;yyyy&#39;</span><span style="color:#000;font-weight:bold">)</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">GROUP</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">BY</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">telemetry_month</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">cht_android_version</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">ORDER</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">BY</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">telemetry_month</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">DESC</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">cht_android_version</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">DESC</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">NULLS</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">LAST</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span></code></pre></div><p>Note that each user can submit many telemetry docs (<code>count_telemetry</code>), so the query breaks out users (<code>count_distinct_users</code>) and devices (<code>count_distinct_devices</code>) for the given month. This means that telemetry counts will be higher than the number of active users. As well, early in the current month, many users may not have had a chance to synchronize their telemetry data yet. For example, this report was run on the 5th of October, so the counts for all three tables are low. Refer to prior months in this case.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -In some cases users are accessing the system via the <a href="https://docs.communityhealthtoolkit.org/core/overview/pwa/">progressive web app (PWA)</a> or are online users. We see the <code>cht_android_version</code> field is empty in this case. -</div> -<table> -<thead> -<tr> -<th>telemetry_month</th> -<th>cht_android_version</th> -<th>count_distinct_users</th> -<th>count_distinct_devices</th> -<th>count_telemetry</th> -</tr> -</thead> -<tbody> -<tr> -<td>2022-10</td> -<td>v0.8.0-xwalk</td> -<td>8</td> -<td>8</td> -<td>12</td> -</tr> -<tr> -<td>2022-10</td> -<td>v0.8.0-webview</td> -<td>140</td> -<td>140</td> -<td>244</td> -</tr> -<tr> -<td>2022-10</td> -<td>v0.4.34</td> -<td>2</td> -<td>2</td> -<td>2</td> -</tr> -<tr> -<td>2022-10</td> -<td></td> -<td>10</td> -<td>10</td> -<td>12</td> -</tr> -<tr> -<td>2022-09</td> -<td>v0.8.0-xwalk</td> -<td>17</td> -<td>18</td> -<td>139</td> -</tr> -<tr> -<td>2022-09</td> -<td>v0.8.0-webview</td> -<td>350</td> -<td>351</td> -<td>3255</td> -</tr> -<tr> -<td>2022-09</td> -<td>v0.5.0</td> -<td>1</td> -<td>1</td> -<td>8</td> -</tr> -<tr> -<td>2022-09</td> -<td>v0.4.34</td> -<td>24</td> -<td>24</td> -<td>69</td> -</tr> -<tr> -<td>2022-09</td> -<td></td> -<td>64</td> -<td>61</td> -<td>143</td> -</tr> -<tr> -<td>2022-08</td> -<td>v0.8.0-xwalk</td> -<td>27</td> -<td>30</td> -<td>181</td> -</tr> -<tr> -<td>2022-08</td> -<td>v0.8.0-webview</td> -<td>365</td> -<td>376</td> -<td>3169</td> -</tr> -<tr> -<td>2022-08</td> -<td>v0.5.0</td> -<td>1</td> -<td>1</td> -<td>12</td> -</tr> -<tr> -<td>2022-08</td> -<td>v0.4.34</td> -<td>25</td> -<td>25</td> -<td>92</td> -</tr> -<tr> -<td>2022-08</td> -<td></td> -<td>81</td> -<td>86</td> -<td>158</td> -</tr> -</tbody> -</table> -<h3 id="active-user-counts">Active user counts</h3> -<p>You can check the <a href="https://docs.communityhealthtoolkit.org/apps/reference/api/#monitoring">monitoring API</a> with <code>curl</code> to check active users for the last <code>30</code> days. Remember that the users can send a telemetry report once per day, so this active user count will be less than the counts from the query above.</p> -<p>We&rsquo;ll <a href="https://stedolan.github.io/jq/">use <code>jq</code> to filter</a> out the unrelated metrics. Be sure to replace <code>CHT-URL-HERE</code> with your production CHT URL:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>curl -s https://CHT-URL-HERE/api/v2/monitoring<span style="color:#4e9a06">\?</span>connected_user_interval<span style="color:#4e9a06">\=</span><span style="color:#0000cf;font-weight:bold">30</span> <span style="color:#000;font-weight:bold">|</span> <span style="color:#4e9a06">\ -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"></span> jq <span style="color:#4e9a06">&#39;. | {connected_users}&#39;</span> -</span></span></code></pre></div><p>This call will show the JSON with active users for the last 30 days:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;connected_users&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;count&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">80</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="users-in-need-of-upgrade">Users in need of upgrade</h3> -<p>In the above table we can see there&rsquo;s 4 users in the most recent month that are on version <code>v0.11.0-webview</code>. Let&rsquo;s find their username so we can follow up with them directly. This query is hard coded for the current year (<code>2022</code>), the current month (<code>3</code>) and the version <code>v0.11.0-webview</code>. Be sure to update the query to fit your needs according to which month, year and version you need to search for:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">select</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">distinct</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">doc</span><span style="color:#ce5c00;font-weight:bold">#&gt;&gt;</span><span style="color:#4e9a06">&#39;{metadata,user}&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">as</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">user</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">doc</span><span style="color:#ce5c00;font-weight:bold">#&gt;&gt;</span><span style="color:#4e9a06">&#39;{device,deviceInfo,app,version}&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">as</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">cht_android_version</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">from</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">couchdb_users_meta</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">where</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">doc</span><span style="color:#ce5c00;font-weight:bold">#&gt;&gt;</span><span style="color:#4e9a06">&#39;{type}&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#39;telemetry&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">and</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">doc</span><span style="color:#ce5c00;font-weight:bold">#&gt;&gt;</span><span style="color:#4e9a06">&#39;{device,deviceInfo,app,version}&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">is</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">not</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">null</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">and</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">doc</span><span style="color:#ce5c00;font-weight:bold">#&gt;&gt;</span><span style="color:#4e9a06">&#39;{metadata,year}&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">to_char</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">current_date</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#39;yyyy&#39;</span><span style="color:#000;font-weight:bold">)</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">and</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">doc</span><span style="color:#ce5c00;font-weight:bold">#&gt;&gt;</span><span style="color:#4e9a06">&#39;{metadata,month}&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">to_char</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">current_date</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#39;FMMM&#39;</span><span style="color:#000;font-weight:bold">)</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">and</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">doc</span><span style="color:#ce5c00;font-weight:bold">#&gt;&gt;</span><span style="color:#4e9a06">&#39;{device,deviceInfo,app,version}&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#39;v0.11.0-webview&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span></code></pre></div><p>In this case only one user is found:</p> -<table> -<thead> -<tr> -<th>user</th> -<th>cht_android_version</th> -</tr> -</thead> -<tbody> -<tr> -<td>adj</td> -<td>v0.11.0-webview</td> -</tr> -</tbody> -</table> -<p>A twist on this query is to remove the version filter. Do not remove the year and month filter as you will get duplicate values for users who have upgraded over time:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">select</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">distinct</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">doc</span><span style="color:#ce5c00;font-weight:bold">#&gt;&gt;</span><span style="color:#4e9a06">&#39;{metadata,user}&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">as</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">user</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">doc</span><span style="color:#ce5c00;font-weight:bold">#&gt;&gt;</span><span style="color:#4e9a06">&#39;{device,deviceInfo,app,version}&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">as</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">cht_android_version</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">from</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">couchdb_users_meta</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">where</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">doc</span><span style="color:#ce5c00;font-weight:bold">#&gt;&gt;</span><span style="color:#4e9a06">&#39;{type}&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#39;telemetry&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">and</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">doc</span><span style="color:#ce5c00;font-weight:bold">#&gt;&gt;</span><span style="color:#4e9a06">&#39;{device,deviceInfo,app,version}&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">is</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">not</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">null</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">and</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">doc</span><span style="color:#ce5c00;font-weight:bold">#&gt;&gt;</span><span style="color:#4e9a06">&#39;{metadata,year}&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">to_char</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">current_date</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#39;yyyy&#39;</span><span style="color:#000;font-weight:bold">)</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">and</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">doc</span><span style="color:#ce5c00;font-weight:bold">#&gt;&gt;</span><span style="color:#4e9a06">&#39;{metadata,month}&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">to_char</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">current_date</span><span style="color:#000;font-weight:bold">,</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#39;FMMM&#39;</span><span style="color:#000;font-weight:bold">)</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span></code></pre></div><p>This will give a list of every user and the version they&rsquo;re running as of the current month and year:</p> -<table> -<thead> -<tr> -<th>user</th> -<th>cht_android_version</th> -</tr> -</thead> -<tbody> -<tr> -<td>moh-west-chw-1</td> -<td>v1.0.1-4</td> -</tr> -<tr> -<td>moh-west-chw-12</td> -<td>v1.0.1-4</td> -</tr> -<tr> -<td>moh-west-chw-14</td> -<td>v1.0.1-4</td> -</tr> -<tr> -<td>moh-west-chw-3</td> -<td>v1.0.1-4</td> -</tr> -<tr> -<td>moh-west-chw-3</td> -<td>v1.0.1-5</td> -</tr> -<tr> -<td>moh-west-chw-4</td> -<td>v1.0.1-5</td> -</tr> -<tr> -<td>moh-west-chw-5</td> -<td>v1.0.1-4</td> -</tr> -<tr> -<td>moh-west-chw-6</td> -<td>v1.0.1-5</td> -</tr> -<tr> -<td>moh-west-chw-7</td> -<td>v1.0.1-5</td> -</tr> -<tr> -<td>moh-west-chw-8</td> -<td>v1.0.1-4</td> -</tr> -<tr> -<td>adj</td> -<td>v0.11.0-webview</td> -</tr> -</tbody> -</table> -<h2 id="cht-conf">CHT Conf</h2> -<p><a href="https://github.com/medic/cht-conf/">CHT Conf</a> has been upgraded to be aware of forms written to work in CHT 3.x that may not work in CHT 4.x. Upgrading, can help you identify forms in need of fixing when pushing to dev instances as outlined below.</p> -<p>To upgrade app, run <code>npm update cht-conf</code></p> -<h2 id="cht-conf-test-harness">cht-conf-test-harness</h2> -<p>The <a href="http://docs.communityhealthtoolkit.org/cht-conf-test-harness">cht-conf-test-harness</a> has <a href="https://forum.communityhealthtoolkit.org/t/announcing-release-of-cht-conf-test-harness-3-0/2393">been upgraded</a> (in version 3.x of the cht-conf-test-harness) to support testing CHT 4.x forms. When preparing to upgrade to CHT 4.x, it is important to use the <a href="https://www.npmjs.com/package/cht-conf-test-harness?activeTab=versions">latest version</a> of the cht-conf-test-harness for automated testing.</p> -<p><em>(Note that the 3.x version of cht-conf-test-harness only supports CHT 4.x. If you are still running CHT 3.x, you should continue using cht-conf-test-harness 2.x.)</em></p> -<p>The <a href="#enketo">breaking Enketo changes</a> included in CHT 4.x are reflected in cht-conf-test-harness 3.x. Running your automated tests with the latest test harness can help identify potential form issues.</p> -<h2 id="enketo">Enketo</h2> -<p>CHT 4.0 <a href="https://github.com/medic/cht-core/pull/7256">upgrades the version of Enketo</a> used to render forms. This upgrade provides a ton of bug fixes and enhancements (particularly around ODK spec compliance) which will make the forms experience in the CHT even better! (For example, we now have proper support for <code>repeat</code>s with a dynamic length, including the various XPath functions necessary to take full advantage of this functionality!) That being said, it does introduce a few changes which may affect the way your forms function (or even cause some forms to fail to load at all).</p> -<h3 id="manual-testing">Manual testing</h3> -<p>You can also manually test your forms on a non-prod CHT instance. It is possible to test your forms against the new Enekto changes without having to uplift your non-prod CHT instance to the new 4.0 architecture.</p> -<p>An easy way of doing this is to use the <a href="https://docs.communityhealthtoolkit.org/hosting/3.x/app-developer/#cht-docker-helper">CHT Docker Helper</a> to deploy a 3.x CHT instance. After you have your dev instance up and running, use <a href="https://github.com/medic/horticulturalist">Horticulturalist</a> to upgrade to the <code>3.17.0-FR-enketo-upgrade</code> <a href="https://docs.communityhealthtoolkit.org/contribute/code/releasing/feature_releases/">feature release</a>:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#000">COUCH_URL</span><span style="color:#ce5c00;font-weight:bold">=</span>https://medic:password@*your-my.local.ip.co-address*:8443/medic horti --local --install<span style="color:#ce5c00;font-weight:bold">=</span>3.17.0-FR-enketo-upgrade-beta.1 -</span></span></code></pre></div><p>After pushing your app config (see &ldquo;CHT Conf&rdquo; above), you can proceed to go through each of your forms in a browser and on a device to ensure there&rsquo;s no errors.</p> -<h3 id="notable-changes-to-form-behavior">Notable changes to form behavior</h3> -<h4 id="xpath-expressions">XPath expressions</h4> -<ul> -<li>Proper syntax in XPath expressions is more strictly enforced (e.g. parameters passed to the <code>concat</code> function must be separated by commas) -<ul> -<li>The <code>+</code> operator can no longer be used to concatenate string values in an expression. Although previous versions of Enketo supported this functionality, it was never part of the <a href="https://docs.getodk.org/form-operators-functions/#math-operators">ODK Specification</a>. The <a href="https://docs.getodk.org/form-operators-functions/#concat"><code>concat</code> function</a> should be used instead.</li> -</ul> -</li> -<li>The behavior of expressions referencing <em>invalid XPath paths</em> (both absolute and relative) has changed. Previously, an invalid XPath path (one pointing to a non-existent node) was evaluated as being equivalent to an empty string. So, <code>/invalid/xpath/path = ''</code> would evaluate to <code>true</code>. Now that expression will evaluate to <code>false</code> since invalid XPath paths are no longer considered equivalent to empty strings. -<ul> -<li>Validation has been added to <code>cht-conf</code> that can detect many invalid XPath paths and will provide an error when trying to upload a form.</li> -</ul> -</li> -<li>The value returned for an <em>unanswered</em> number question, when referenced from an XPath expression, has changed from <code>0</code> to <code>NaN</code>. This can affect existing logic comparing number values to <code>0</code>.</li> -</ul> -<h4 id="layout">Layout</h4> -<ul> -<li>The <code>horizontal</code> and <code>horizontal-compact</code> appearances are now deprecated (a warning will be displayed by cht-conf when uploading to the server). The <code>columns</code>, <code>columns-pack</code>, and <code>columns-n</code> appearances should be used instead. See <a href="https://docs.getodk.org/form-question-types/#select-widget-with-columns-pack-appearance">the documentation</a> for more details.</li> -<li><a href="https://docs.getodk.org/form-styling/#markdown">Markdown syntax</a> is now supported for all question labels (and not just <code>note</code> fields).</li> -</ul> -<h4 id="updated-xpath-functions">Updated XPath functions</h4> -<ul> -<li>The <code>format-date</code> and <code>format-date-time</code> functions no longer accept month values that are <code>&lt;= 0</code> (e.g. <code>1984-00-23</code>). This is notable because some patterns for calculating dates based on an offset of a certain amount of years/months relied on this functionality (e.g. birth date). -<ul> -<li>See the details below regarding the new <code>add-date</code> function for a cleaner way of calculating dates based on an offset.</li> -</ul> -</li> -<li>The behavior of the <code>today</code> function has changed to return the current date at <em>midnight</em> in the current timezone instead of at the <em>current time</em>. To get the current date <em>and current time</em> use the <code>now</code> function.</li> -<li><code>decimal-date-time</code>: -<ul> -<li>The behavior of this function has changed with regard to the default timezone used when calculating the decimal value of a date that does not include any timezone information. <em>(Note that the values from basic <code>date</code> questions do NOT include time zone information.)</em> Previously, the timezone used in the calculation for dates with no timezone information was UTC. Now, the user&rsquo;s current timezone will be used. -<ul> -<li>Practically speaking, this means it is no longer safe to assume that the output from <code>decimal-date-time</code>, for a value from a basic <code>date</code> question, will be a whole number. Now it is likely that a <em>decimal value</em> will be returned (with the numbers after the decimal point representing the offset of the user&rsquo;s timezone from UTC).</li> -</ul> -</li> -<li>Previously this function would accept various string parameters (e.g. date strings with various formats) as input. Now, the only string values it will only accept are ones formatted according to ISO 8601 (e.g. <code>2022-10-03</code>). -<ul> -<li>Strings containing date values should be parsed with the <a href="https://docs.getodk.org/form-operators-functions/#date"><code>date</code> function</a> before calling <code>decimal-date-time</code>.</li> -</ul> -</li> -</ul> -</li> -</ul> -<h4 id="new-xpath-functions">New XPath functions</h4> -<ul> -<li>Custom CHT functions: -<ul> -<li><a href="https://docs.communityhealthtoolkit.org/apps/reference/forms/app/#add-date"><code>add-date</code></a> - Adds the provided number of years/months/days/hours/minutes to a date value.</li> -</ul> -</li> -<li>ODK Functions: -<ul> -<li>Repeats and other node sets: -<ul> -<li><a href="https://docs.getodk.org/form-operators-functions/#position"><code>position</code></a> - Returns the current iteration index within a repeat group.</li> -<li><a href="https://docs.getodk.org/form-operators-functions/#count"><code>count</code>/<code>count-non-empty</code></a> - Returns the number of elements in a repeat group. -<ul> -<li>Now is re-calculated when the size of the <code>repeat</code> changes</li> -</ul> -</li> -</ul> -</li> -<li><a href="https://docs.getodk.org/form-operators-functions/#once"><code>once</code></a> - Returns the value of the given expression if the question&rsquo;s value is empty. Otherwise, returns the current value of the question.</li> -<li><a href="https://docs.getodk.org/form-operators-functions/#checklist"><code>checklist</code>/<code>weighted-checklist</code></a> - Returns <code>True</code> when the required number of affirmative responses is given.</li> -</ul> -</li> -</ul> -<h4 id="known-issues">Known issues</h4> -<ul> -<li>The answer to a non-relevant question <a href="https://github.com/medic/cht-core/issues/7674">is not immediately cleared</a> when the question becomes non-relevant and is still provided to XPath expressions that reference the question. When the form is submitted, the non-relevant answers will be cleared and any dependent XPath expressions will be re-evaluated. -<ul> -<li>This behavior applies both to questions that were answered and later became non-relevant as well as to questions which have configured default values.</li> -</ul> -</li> -<li>In <code>3.x</code> versions of the CHT you could simulate a <em>bulleted list</em> in a form label (e.g. a <code>note</code>) by using <code>-</code> or <code>*</code>. Now, in <code>4.x</code>, the bullets are no longer properly displayed in the label due to <a href="https://github.com/medic/cht-core/issues/8002">this bug</a>.</li> -</ul> -<h4 id="community-cht-upgrade-helper">Community cht-upgrade-helper</h4> -<p>An unofficial <a href="https://github.com/jkuester/cht-upgrade-helper">cht-upgrade-helper</a> utility has been created by a community member that can help flag form elements that should be manually reviewed before upgrading to <code>4.0.0</code>. It is not officially supported by Medic and is provided as-is.</p>Apps: Update Collect Forms Remotelyhttps://docs.communityhealthtoolkit.org/apps/guides/updates/collect-forms-update/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/updates/collect-forms-update/ -<p>To do over the air Medic Collect form updates via HTTP rather than sending APKs which have a long manual install process, follow the steps below:</p> -<ol> -<li>Have your xls forms ready in the folder.</li> -</ol> -<ul> -<li>They should use underscore as name separators. e.g form_name.xlsx</li> -<li>They should have <code>form_id</code> and <code>name</code> properties in the settings</li> -</ul> -<p><img src="xform_name_settings.png" alt="Name property"></p> -<ol> -<li>Upload the forms to the instance using <code>cht-conf</code> Using the <code>upload-collect-forms</code> <a href="https://github.com/medic/cht-conf/blob/master/src/cli/supported-actions.js">action</a> as shown below.</li> -</ol> -<pre tabindex="0"><code>cht --instance=user:pass@instancename.app.medicmobile.org upload-collect-forms -</code></pre><ol start="3"> -<li>Go to the Collect App. Delete All forms then go to <code>Get Blank Form</code> and select all the forms you need.</li> -</ol> -<h1 id="troubleshooting">Troubleshooting</h1> -<p>When you go to <code>Get Blank Forms</code> and instead of getting a list of the forms available, you get a pop-up error which has a portion of this message instead</p> -<pre tabindex="0"><code>...OpenRosa Version 1.0 standard: Forms list entry 1 is missing one or more tags: formId, name or downloadUrl -</code></pre><p>This means you probably uploaded a XLS file without a <code>name</code> or <code>form_id</code> property. To find out which form is missing that, use this command:</p> -<pre tabindex="0"><code>curl -vvvv -H &#34;x-openrosa-version: 1&#34; http://user:pass@host:port/api/v1/forms -</code></pre><p>Should bring a list like this one</p> -<p><img src="xform_list.png" alt="Xform List"></p> -<p>Go through the list and see which form has a missing <code>&lt;name&gt;</code> or <code>&lt;formID&gt;</code> property. Add it and reupload the forms using <code>cht-conf</code> again.</p> \ No newline at end of file +Managing Updates to Applications and Content on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/guides/updates/Recent content in Managing Updates to Applications and Content on Community Health ToolkitHugo -- gohugo.ioenFeature Flagshttps://docs.communityhealthtoolkit.org/apps/guides/updates/feature-flags/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/updates/feature-flags/Some CHT Core features can be enabled for specific users only. This can be particularly helpful for features that require training. The updated or &ldquo;new&rdquo; version will generally be the system default, but users can be configured to see the &ldquo;old&rdquo; version. If you do nothing when you upgrade, users will automatically start seeing the new version. +Note Feature flags are used primarily as a way to phase in updates. The old version should be considered deprecated and will be completely removed in a future release.Moving Contacts within the Hierarchyhttps://docs.communityhealthtoolkit.org/apps/guides/updates/moving-contacts/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/updates/moving-contacts/Contacts are organized into a hierarchy. It is not straight-forward to move contacts from one position in the hierarchy to another because many copies of this hierarchy exist. Use the move-contacts action in cht-conf to assign a new parent to contacts. This command will move the specified contact, all the contacts under that contact, and all reports created by any of those contacts. This action will download all documents that need to be updated, update the lineages within those documents, and then save the updated documents on your local disk.Preparing to upgrade to CHT 4.0https://docs.communityhealthtoolkit.org/apps/guides/updates/preparing-for-4/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/updates/preparing-for-4/Note This guide applies to both self-hosted and Medic hosted deployments. Introduction Medic uses Semantic Versioning (aka &ldquo;SemVer&rdquo;) which means that the CHT upgrade from the major 3.x version to the 4.x version denotes there are breaking changes. The key to a successful upgrade will be to understand and plan for these breaking changes. Aside from the Docker hosting infrastructure (out of scope for this prep document), the two breaking changes are around CHT Android and Enketo.Update Collect Forms Remotelyhttps://docs.communityhealthtoolkit.org/apps/guides/updates/collect-forms-update/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/updates/collect-forms-update/To do over the air Medic Collect form updates via HTTP rather than sending APKs which have a long manual install process, follow the steps below: +Have your xls forms ready in the folder. They should use underscore as name separators. e.g form_name.xlsx They should have form_id and name properties in the settings Upload the forms to the instance using cht-conf Using the upload-collect-forms action as shown below. cht --instance=user:pass@instancename.app.medicmobile.org upload-collect-forms Go to the Collect App. \ No newline at end of file diff --git a/apps/guides/updates/moving-contacts/index.html b/apps/guides/updates/moving-contacts/index.html index 3cfb53f5b4..04550c186d 100644 --- a/apps/guides/updates/moving-contacts/index.html +++ b/apps/guides/updates/moving-contacts/index.html @@ -1,9 +1,9 @@ -Moving Contacts within the Hierarchy | Community Health Toolkit +Moving Contacts within the Hierarchy | Community Health Toolkit

    Moving Contacts within the Hierarchy

    How to safely move contacts

    Contacts are organized into a hierarchy. It is not straight-forward to move contacts from one position in the hierarchy to another because many copies of this hierarchy exist. Use the move-contacts action in cht-conf to assign a new parent to contacts. This command will move the specified contact, all the contacts under that contact, and all reports created by any of those contacts. This action will download all documents that need to be updated, update the lineages within those documents, and then save the updated documents on your local disk. To commit those changes to the database, run the upload-docs action.

    Offline users who have contacts removed from their visible hierarchy will not automatically see those contacts disappear. The contact remains on the user’s device. Any updates made to the contact (or any reports created for that contact) will silently fail to sync. These users must be encouraged to clear cache and resync!

    Also impactful, but less serious - this script can cause significant amounts of changes to the database and offline users who have contacts moved into their visible hierarchy may experience lengthy and bandwidth-intensive synchronizations.

    ParameterDescriptionRequired
    contactsComma delimited list of contact IDs which will be movedYes
    parentID of the new parent which will be assigned to the provided contactsYes
    docDirectoryPathThis action outputs files to local disk at this destinationNo. Default json-docs

    Some constraints when moving contacts:

    • Allowed Parents - When moving contacts on WebApp >v3.7, your chosen parent must be listed as a valid parent for the contact as defined in the configuration for place hierarchy. For WebApp <v3.7, the default hierarchy is enforced.
    • Circular Hierarchy - Nobody’s parent can ever be themself or their child.
    • Primary Contacts - Primary contacts must be a descendant of the place for which they are the primary contact. You may need to select a new primary contact for a place through the WebApp if you’d like to move a primary contact to a new place in the hierarchy.
    • Minification - Due to contact “minification” (#2635) which was implemented in v2.13, this script should not be used for versions prior to v2.13.

    Examples

    Move the contacts with the id contact_1 and contact_2 to have the parent parent_id. The changes will be in the local folder my_folder only for review. Run the second command to upload the changes after review.

    cht --instance= move-contacts -- --contacts=contact_1,contact_2 --parent=parent_id --docDirectoryPath=my_folder
    + Create project issue

    Moving Contacts within the Hierarchy

    How to safely move contacts

    Contacts are organized into a hierarchy. It is not straight-forward to move contacts from one position in the hierarchy to another because many copies of this hierarchy exist. Use the move-contacts action in cht-conf to assign a new parent to contacts. This command will move the specified contact, all the contacts under that contact, and all reports created by any of those contacts. This action will download all documents that need to be updated, update the lineages within those documents, and then save the updated documents on your local disk. To commit those changes to the database, run the upload-docs action.

    Offline users who have contacts removed from their visible hierarchy will not automatically see those contacts disappear. The contact remains on the user’s device. Any updates made to the contact (or any reports created for that contact) will silently fail to sync. These users must be encouraged to clear cache and resync!

    Also impactful, but less serious - this script can cause significant amounts of changes to the database and offline users who have contacts moved into their visible hierarchy may experience lengthy and bandwidth-intensive synchronizations.

    ParameterDescriptionRequired
    contactsComma delimited list of contact IDs which will be movedYes
    parentID of the new parent which will be assigned to the provided contactsYes
    docDirectoryPathThis action outputs files to local disk at this destinationNo. Default json-docs

    Some constraints when moving contacts:

    • Allowed Parents - When moving contacts on WebApp >v3.7, your chosen parent must be listed as a valid parent for the contact as defined in the configuration for place hierarchy. For WebApp <v3.7, the default hierarchy is enforced.
    • Circular Hierarchy - Nobody’s parent can ever be themself or their child.
    • Primary Contacts - Primary contacts must be a descendant of the place for which they are the primary contact. You may need to select a new primary contact for a place through the WebApp if you’d like to move a primary contact to a new place in the hierarchy.
    • Minification - Due to contact “minification” (#2635) which was implemented in v2.13, this script should not be used for versions prior to v2.13.

    Examples

    Move the contacts with the id contact_1 and contact_2 to have the parent parent_id. The changes will be in the local folder my_folder only for review. Run the second command to upload the changes after review.

    cht --instance= move-contacts -- --contacts=contact_1,contact_2 --parent=parent_id --docDirectoryPath=my_folder
     cht --local upload-docs -- --docDirectoryPath=my_folder
     

    Move the contact with the id contact_1 to the top of the hierarchy (no parent).

    cht --local move-contacts upload-docs -- --contacts=contact_1 --parent=root
     

    CHT Applications > @@ -313,7 +313,8 @@ Contacts + Users 2

    Creating and editing contacts and users with cht-conf

    CHT Applications > Concepts > Hierarchies

    Organizing people and places, and their relationship to one-another

    -

    Last modified 22.10.2021: Updated link format (3f263020)
    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/guides/updates/preparing-for-4/index.html b/apps/guides/updates/preparing-for-4/index.html index bfd2b1b4c6..fd2d9689c0 100644 --- a/apps/guides/updates/preparing-for-4/index.html +++ b/apps/guides/updates/preparing-for-4/index.html @@ -1,9 +1,9 @@ -Preparing to upgrade to CHT 4.0 | Community Health Toolkit +Preparing to upgrade to CHT 4.0 | Community Health Toolkit

    Preparing to upgrade to CHT 4.0

    Steps to ensure your CHT App will run smoothly on CHT 4.0 and later

    Introduction

    Medic uses Semantic Versioning (aka “SemVer”) which means that the CHT upgrade from the major 3.x version to the 4.x version denotes there are breaking changes. The key to a successful upgrade will be to understand and plan for these breaking changes. Aside from the Docker hosting infrastructure (out of scope for this prep document), the two breaking changes are around CHT Android and Enketo.

    While CHT 4.0 has not been released yet, the effort to be prepared can be quite time consuming, especially for large deployments that may need to do handset upgrades in a worst case. The sooner deployments start preparing for the upgrade, the easier it will be when it comes to the upgrade itself. Conveniently, all device and Android app changes to prepare for 4.x are backwards compatible with 3.x. Prepare now, so you will be ready to upgrade sooner than later!

    CHT Android v1.0.0+

    This change is straightforward in that CHT 4.x no longer supports versions before 1.0.0, so deployments need to update their Play Store app. As of this writing, CHT Android is at 1.0.4. Please see the Android docs on how to update your app and release it. Note that Google’s Play Store can often have delays which deployments have no control over. Again, the sooner you start, the better.

    Versions in use

    After you have published your app, you need to instruct your users to check the Play Store for upgrades. You can then check CHT Telemetry to see what CHT Android versions are in use. Assuming you have couch2pg set up to pull in your CouchDB data to a PostgreSQL database, this query will list Android versions counts for the current year, broken out by month, year and version:

    Preparing to upgrade to CHT 4.0

    Steps to ensure your CHT App will run smoothly on CHT 4.0 and later

    Introduction

    Medic uses Semantic Versioning (aka “SemVer”) which means that the CHT upgrade from the major 3.x version to the 4.x version denotes there are breaking changes. The key to a successful upgrade will be to understand and plan for these breaking changes. Aside from the Docker hosting infrastructure (out of scope for this prep document), the two breaking changes are around CHT Android and Enketo.

    While CHT 4.0 has not been released yet, the effort to be prepared can be quite time consuming, especially for large deployments that may need to do handset upgrades in a worst case. The sooner deployments start preparing for the upgrade, the easier it will be when it comes to the upgrade itself. Conveniently, all device and Android app changes to prepare for 4.x are backwards compatible with 3.x. Prepare now, so you will be ready to upgrade sooner than later!

    CHT Android v1.0.0+

    This change is straightforward in that CHT 4.x no longer supports versions before 1.0.0, so deployments need to update their Play Store app. As of this writing, CHT Android is at 1.0.4. Please see the Android docs on how to update your app and release it. Note that Google’s Play Store can often have delays which deployments have no control over. Again, the sooner you start, the better.

    Versions in use

    After you have published your app, you need to instruct your users to check the Play Store for upgrades. You can then check CHT Telemetry to see what CHT Android versions are in use. Assuming you have couch2pg set up to pull in your CouchDB data to a PostgreSQL database, this query will list Android versions counts for the current year, broken out by month, year and version:

    SELECT
     	concat(doc#>>'{metadata,year}','-',lpad(doc#>>'{metadata,month}',2,'0')) as telemetry_month, 	
     	doc#>>'{device,deviceInfo,app,version}' as cht_android_version,
     	count(distinct(doc#>>'{metadata,user}')) AS count_distinct_users,
    @@ -346,7 +346,8 @@
     	and doc#>>'{metadata,month}' = to_char(current_date, 'FMMM')
     

    This will give a list of every user and the version they’re running as of the current month and year:

    usercht_android_version
    moh-west-chw-1v1.0.1-4
    moh-west-chw-12v1.0.1-4
    moh-west-chw-14v1.0.1-4
    moh-west-chw-3v1.0.1-4
    moh-west-chw-3v1.0.1-5
    moh-west-chw-4v1.0.1-5
    moh-west-chw-5v1.0.1-4
    moh-west-chw-6v1.0.1-5
    moh-west-chw-7v1.0.1-5
    moh-west-chw-8v1.0.1-4
    adjv0.11.0-webview

    CHT Conf

    CHT Conf has been upgraded to be aware of forms written to work in CHT 3.x that may not work in CHT 4.x. Upgrading, can help you identify forms in need of fixing when pushing to dev instances as outlined below.

    To upgrade app, run npm update cht-conf

    cht-conf-test-harness

    The cht-conf-test-harness has been upgraded (in version 3.x of the cht-conf-test-harness) to support testing CHT 4.x forms. When preparing to upgrade to CHT 4.x, it is important to use the latest version of the cht-conf-test-harness for automated testing.

    (Note that the 3.x version of cht-conf-test-harness only supports CHT 4.x. If you are still running CHT 3.x, you should continue using cht-conf-test-harness 2.x.)

    The breaking Enketo changes included in CHT 4.x are reflected in cht-conf-test-harness 3.x. Running your automated tests with the latest test harness can help identify potential form issues.

    Enketo

    CHT 4.0 upgrades the version of Enketo used to render forms. This upgrade provides a ton of bug fixes and enhancements (particularly around ODK spec compliance) which will make the forms experience in the CHT even better! (For example, we now have proper support for repeats with a dynamic length, including the various XPath functions necessary to take full advantage of this functionality!) That being said, it does introduce a few changes which may affect the way your forms function (or even cause some forms to fail to load at all).

    Manual testing

    You can also manually test your forms on a non-prod CHT instance. It is possible to test your forms against the new Enekto changes without having to uplift your non-prod CHT instance to the new 4.0 architecture.

    An easy way of doing this is to use the CHT Docker Helper to deploy a 3.x CHT instance. After you have your dev instance up and running, use Horticulturalist to upgrade to the 3.17.0-FR-enketo-upgrade feature release:

    COUCH_URL=https://medic:password@*your-my.local.ip.co-address*:8443/medic horti --local --install=3.17.0-FR-enketo-upgrade-beta.1
     

    After pushing your app config (see “CHT Conf” above), you can proceed to go through each of your forms in a browser and on a device to ensure there’s no errors.

    Notable changes to form behavior

    XPath expressions

    • Proper syntax in XPath expressions is more strictly enforced (e.g. parameters passed to the concat function must be separated by commas)
      • The + operator can no longer be used to concatenate string values in an expression. Although previous versions of Enketo supported this functionality, it was never part of the ODK Specification. The concat function should be used instead.
    • The behavior of expressions referencing invalid XPath paths (both absolute and relative) has changed. Previously, an invalid XPath path (one pointing to a non-existent node) was evaluated as being equivalent to an empty string. So, /invalid/xpath/path = '' would evaluate to true. Now that expression will evaluate to false since invalid XPath paths are no longer considered equivalent to empty strings.
      • Validation has been added to cht-conf that can detect many invalid XPath paths and will provide an error when trying to upload a form.
    • The value returned for an unanswered number question, when referenced from an XPath expression, has changed from 0 to NaN. This can affect existing logic comparing number values to 0.

    Layout

    • The horizontal and horizontal-compact appearances are now deprecated (a warning will be displayed by cht-conf when uploading to the server). The columns, columns-pack, and columns-n appearances should be used instead. See the documentation for more details.
    • Markdown syntax is now supported for all question labels (and not just note fields).

    Updated XPath functions

    • The format-date and format-date-time functions no longer accept month values that are <= 0 (e.g. 1984-00-23). This is notable because some patterns for calculating dates based on an offset of a certain amount of years/months relied on this functionality (e.g. birth date).
      • See the details below regarding the new add-date function for a cleaner way of calculating dates based on an offset.
    • The behavior of the today function has changed to return the current date at midnight in the current timezone instead of at the current time. To get the current date and current time use the now function.
    • decimal-date-time:
      • The behavior of this function has changed with regard to the default timezone used when calculating the decimal value of a date that does not include any timezone information. (Note that the values from basic date questions do NOT include time zone information.) Previously, the timezone used in the calculation for dates with no timezone information was UTC. Now, the user’s current timezone will be used.
        • Practically speaking, this means it is no longer safe to assume that the output from decimal-date-time, for a value from a basic date question, will be a whole number. Now it is likely that a decimal value will be returned (with the numbers after the decimal point representing the offset of the user’s timezone from UTC).
      • Previously this function would accept various string parameters (e.g. date strings with various formats) as input. Now, the only string values it will only accept are ones formatted according to ISO 8601 (e.g. 2022-10-03).
        • Strings containing date values should be parsed with the date function before calling decimal-date-time.

    New XPath functions

    • Custom CHT functions:
      • add-date - Adds the provided number of years/months/days/hours/minutes to a date value.
    • ODK Functions:
      • Repeats and other node sets:
        • position - Returns the current iteration index within a repeat group.
        • count/count-non-empty - Returns the number of elements in a repeat group.
          • Now is re-calculated when the size of the repeat changes
      • once - Returns the value of the given expression if the question’s value is empty. Otherwise, returns the current value of the question.
      • checklist/weighted-checklist - Returns True when the required number of affirmative responses is given.

    Known issues

    • The answer to a non-relevant question is not immediately cleared when the question becomes non-relevant and is still provided to XPath expressions that reference the question. When the form is submitted, the non-relevant answers will be cleared and any dependent XPath expressions will be re-evaluated.
      • This behavior applies both to questions that were answered and later became non-relevant as well as to questions which have configured default values.
    • In 3.x versions of the CHT you could simulate a bulleted list in a form label (e.g. a note) by using - or *. Now, in 4.x, the bullets are no longer properly displayed in the label due to this bug.

    Community cht-upgrade-helper

    An unofficial cht-upgrade-helper utility has been created by a community member that can help flag form elements that should be manually reviewed before upgrading to 4.0.0. It is not officially supported by Medic and is provided as-is.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/index.html b/apps/index.html index f9d55382c5..07908d55ba 100644 --- a/apps/index.html +++ b/apps/index.html @@ -1,9 +1,9 @@ -CHT Applications | Community Health Toolkit +CHT Applications | Community Health Toolkit
    \ No newline at end of file diff --git a/apps/index.xml b/apps/index.xml index b60dc31c30..3e0780161a 100644 --- a/apps/index.xml +++ b/apps/index.xml @@ -1 +1 @@ -Community Health Toolkit – CHT Applicationshttps://docs.communityhealthtoolkit.org/apps/Recent content in CHT Applications on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file +CHT Applications on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/Recent content in CHT Applications on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file diff --git a/apps/reference/_partial_cht_api/index.html b/apps/reference/_partial_cht_api/index.html index 7d9915ca32..79d7d631d4 100644 --- a/apps/reference/_partial_cht_api/index.html +++ b/apps/reference/_partial_cht_api/index.html @@ -1,4 +1,4 @@ -CHT API | Community Health Toolkit +CHT API | Community Health Toolkit

    CHT API

    Introduced in v3.12.0

    Provides CHT-Core Framework’s functions to contact summary, targets and tasks. The API is available in the cht reserved variable under the v1 version.

    FunctionArgumentsDescription
    hasPermissions(permissions, userRoles, chtPermissionsSettings)permissions: String or array of permission name(s).
    userRoles: (Optional) Array of user roles. Default to the current logged in user.
    chtPermissionsSettings: (Optional) Object of configured permissions in CHT-Core’s settings. Default to the current instance’s configured permissions.
    Returns true if the user has the permission(s), otherwise returns false.
    hasAnyPermission(permissionsGroups, userRoles, chtPermissionsSettings)permissionsGroups: Array of groups of permission name(s).
    userRoles: (Optional) Array of user roles. Default to the current logged in user.
    chtPermissionsSettings: (Optional) Object of configured permissions in CHT-Core’s settings. Default to the current instance’s configured permissions.
    Returns true if the user has all the permissions of any of the provided groups, otherwise returns false.
    getExtensionLib(name)name: String of script nameReturns an executable function identified by the given name configured as extension-libs.

    CHT API’s code samples

    const canEdit = cht.v1.hasPermissions('can_edit');
    + Create project issue

    CHT API

    Introduced in v3.12.0

    Provides CHT-Core Framework’s functions to contact summary, targets and tasks. The API is available in the cht reserved variable under the v1 version.

    FunctionArgumentsDescription
    hasPermissions(permissions, userRoles, chtPermissionsSettings)permissions: String or array of permission name(s).
    userRoles: (Optional) Array of user roles. Default to the current logged in user.
    chtPermissionsSettings: (Optional) Object of configured permissions in CHT-Core’s settings. Default to the current instance’s configured permissions.
    Returns true if the user has the permission(s), otherwise returns false.
    hasAnyPermission(permissionsGroups, userRoles, chtPermissionsSettings)permissionsGroups: Array of groups of permission name(s).
    userRoles: (Optional) Array of user roles. Default to the current logged in user.
    chtPermissionsSettings: (Optional) Object of configured permissions in CHT-Core’s settings. Default to the current instance’s configured permissions.
    Returns true if the user has all the permissions of any of the provided groups, otherwise returns false.
    getExtensionLib(name)name: String of script nameReturns an executable function identified by the given name configured as extension-libs.

    CHT API’s code samples

    const canEdit = cht.v1.hasPermissions('can_edit');
     const canManagePlaces = cht.v1.hasPermissions(['can_create_places', 'can_update_places']);
     const hasAnyGroup = cht.v1.hasAnyPermission([
         ['can_view_messages', 'can_view_message_action'], 
    @@ -320,7 +320,8 @@
     ]);
     const averageFn = cht.v1.getExtensionLib('average.js');
     
    -

    Last modified 06.04.2023: 1008 - Fix Partials (#1009) (4938b3e0)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/reference/_partial_utils/index.html b/apps/reference/_partial_utils/index.html index 73bb6e757d..cd45d974d7 100644 --- a/apps/reference/_partial_utils/index.html +++ b/apps/reference/_partial_utils/index.html @@ -1,9 +1,9 @@ -Utils Functions | Community Health Toolkit +Utils Functions | Community Health Toolkit

    Utils Functions

    Utility functions in the Core Framework can make common tasks much easier. These are available only for Tasks and Targets. To use the function call Utils.<function-name>(<params>), for example Utils.addDate(report.reported_date, 10).

    NameDescription
    isTimely(date, event)Returns true if the given date is after the start date and before the end date of the event.
    addDate(date, days)Returns a new Date set to midnight the given number of days after the given date. If no date is given the date defaults to today.
    getLmpDate(doc)Attempts to work out the LMP from the given doc. If no LMP is given it defaults to four weeks before the reported_date.
    getSchedule(name)Returns the task schedule with the given name from the configuration.
    getMostRecentTimestamp(reports, form)Returns the reported_date of the most recent of the reports with form ID matching the given form.
    getMostRecentReport(reports, form)Like getMostRecentTimestamp but returns the report, not just the reported_date. From CHT v3.14.0, it also accepts an array of forms.
    isFormSubmittedInWindow(reports, form, start, end)Returns true if any of the given reports are for the given form and were reported after start and before end.
    isFirstReportNewer(firstReport, secondReport)Returns true if the firstReport was reported before the secondReport.
    isDateValid(date)Returns true if the given date is a validate JavaScript Date.
    now()Returns the current Date.
    getField(report, fieldPath)Returns the value of the specified fieldPath. The fieldPath is a period separated json path.
    MS_IN_DAYA constant for the number of milliseconds in a day.

    Please open an issue if you’d like other functions included.

    -

    Last modified 06.04.2023: 1008 - Fix Partials (#1009) (4938b3e0)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/reference/api/index.html b/apps/reference/api/index.html index 12eaa300c0..0a61a85b01 100644 --- a/apps/reference/api/index.html +++ b/apps/reference/api/index.html @@ -1,9 +1,9 @@ -API to interact with CHT Applications | Community Health Toolkit +API to interact with CHT Applications | Community Health Toolkit

    API to interact with CHT Applications

    RESTful Application Programming Interfaces for integrating with CHT applications

    This page covers the endpoints to use when integrating with the CHT server. If there isn’t an endpoint that provides the function or data you need, direct access to the database is possible via the CouchDB API. Access to the PostgreSQL database may also prove useful for data analysis. If additional endpoints would be helpful, please make suggestions via a GitHub issue.

    API to interact with CHT Applications

    RESTful Application Programming Interfaces for integrating with CHT applications

    This page covers the endpoints to use when integrating with the CHT server. If there isn’t an endpoint that provides the function or data you need, direct access to the database is possible via the CouchDB API. Access to the PostgreSQL database may also prove useful for data analysis. If additional endpoints would be helpful, please make suggestions via a GitHub issue.

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/reference/app-settings/accept_case_reports/index.html b/apps/reference/app-settings/accept_case_reports/index.html index b5a0b2b520..a85803e18d 100644 --- a/apps/reference/app-settings/accept_case_reports/index.html +++ b/apps/reference/app-settings/accept_case_reports/index.html @@ -1,9 +1,9 @@ -.accept_case_reports | Community Health Toolkit +.accept_case_reports | Community Health Toolkit

    .accept_case_reports

    Case Reports: Defining SMS workflows with schedules and registration of case reports.

    The accept_case_reports key contains the actions to take when reports about cases are received.

    app_settings.json .accept_case_reports[]

    propertydescriptionrequired
    formForm ID of the case form.yes
    silence_typeA comma separated list of schedules to mute.no
    silence_forDuration from when the report was submitted for which messages should be muted. It is structured as a string with an integer value followed by a space and the time unit. For instance 8 weeks or 2 days. The units available are seconds, minutes, hours, days, weeks, months, years, and their singular forms as well. When a message is muted all messages belonging to the same group will be muted, even if it falls outside of this time period. See messages[].group in Schedules for related info.no
    validationsA set of validations to perform on incoming reports. Read more information about configuring validation rules.no
    validations.join_responsesA boolean specifying whether validation messages should be combined into one message.no
    validations.list[]An array of validation rules a report should pass to be considered valid.no
    validations.list[].propertyReport field for which this validation rule will be applied.no
    validations.list[].ruleValidation condition to be applied to the property field.no
    validations.list[].translation_keyTranslation key for the message reply to be sent if a report fails this rule.no
    messagesAn array of automated responses to incoming reports.no
    messages[].translation_keyTranslation key for the message text associated with this eventno
    messages[].event_typeAn event that will trigger sending of this message. Typical values are: report_accepted when the report has been successfully validated, registration_not_found when the case ID supplied in the report doesn’t match any case ID issued by Medic. Read the documentation on muting.no
    messages[].recipientWho the message should be sent to. Use reporting_unit for the sender of the report, clinic for clinic contact, and parent for the parent contact.no

    Code sample

    "accept_case_reports": [{
    + Create project issue

    .accept_case_reports

    Case Reports: Defining SMS workflows with schedules and registration of case reports.

    The accept_case_reports key contains the actions to take when reports about cases are received.

    app_settings.json .accept_case_reports[]

    propertydescriptionrequired
    formForm ID of the case form.yes
    silence_typeA comma separated list of schedules to mute.no
    silence_forDuration from when the report was submitted for which messages should be muted. It is structured as a string with an integer value followed by a space and the time unit. For instance 8 weeks or 2 days. The units available are seconds, minutes, hours, days, weeks, months, years, and their singular forms as well. When a message is muted all messages belonging to the same group will be muted, even if it falls outside of this time period. See messages[].group in Schedules for related info.no
    validationsA set of validations to perform on incoming reports. Read more information about configuring validation rules.no
    validations.join_responsesA boolean specifying whether validation messages should be combined into one message.no
    validations.list[]An array of validation rules a report should pass to be considered valid.no
    validations.list[].propertyReport field for which this validation rule will be applied.no
    validations.list[].ruleValidation condition to be applied to the property field.no
    validations.list[].translation_keyTranslation key for the message reply to be sent if a report fails this rule.no
    messagesAn array of automated responses to incoming reports.no
    messages[].translation_keyTranslation key for the message text associated with this eventno
    messages[].event_typeAn event that will trigger sending of this message. Typical values are: report_accepted when the report has been successfully validated, registration_not_found when the case ID supplied in the report doesn’t match any case ID issued by Medic. Read the documentation on muting.no
    messages[].recipientWho the message should be sent to. Use reporting_unit for the sender of the report, clinic for clinic contact, and parent for the parent contact.no

    Code sample

    "accept_case_reports": [{
       "form": "SIGNOFF",
       "validations": {},
       "messages": [{
    @@ -311,7 +311,8 @@
       }]
     }]
     
    -

    Last modified 27.06.2020: Fix fixed links (827403c4)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/reference/app-settings/assetlinks/index.html b/apps/reference/app-settings/assetlinks/index.html index 65053a401d..3bbb1ee68a 100644 --- a/apps/reference/app-settings/assetlinks/index.html +++ b/apps/reference/app-settings/assetlinks/index.html @@ -1,9 +1,9 @@ -.assetlinks | Community Health Toolkit +.assetlinks | Community Health Toolkit

    .assetlinks

    Assetlinks: Defining the Digital Asset Links JSON file associating your domain with your Android app.

    Requires CHT Core 4.7.0+, CHT Conf 3.22.0+, and CHT Android 1.3.0+

    When using a custom flavor of cht-android to connect to your CHT instance, the ecosystem supports using deep links to open specific content in the app. (E.g. token login links). Security measures in Android require these deep links be verified either automatically or manually. This assetlinks configuration enables auto-verification for your CHT links in your Android app. The provided JSON file will be served at https://<your CHT instance>/.well-known/assetlinks.json. If you do not provide this configuration, users will be prompted to manually associate the CHT domain with your app.

    For more information, see the docs for building a new CHT Android flavor.

    Specify your digital asset links in the app_settings/assetlinks.json file. The compile-app-settings action in the cht-conf will automatically include this configuration in your app_settings.json file. Then, running the upload-app-settings action will deploy it to the server.

    propertytypedescriptionrequired
    relation[]Array<string>The array should contain only one element: the string delegate_permission/common.handle_all_urls.yes
    targetobjectContains fields to identify associated apps.yes
    target.namespacestringMust be set to android_app.yes
    target.package_namestringThe application ID declared in the app’s build.gradle file.yes
    target.sha256_cert_fingerprintsArray<string>The SHA256 fingerprints of your app’s signing certificate. You can get it with the Java utility keytool, see how exactly in our Android guide.yes

    Code Sample

    This sample associates the Android app org.medicmobile.webapp.mobile to a CHT instance and grants it link-opening rights to the Android app:

    .assetlinks

    Assetlinks: Defining the Digital Asset Links JSON file associating your domain with your Android app.

    Requires CHT Core 4.7.0+, CHT Conf 3.22.0+, and CHT Android 1.3.0+

    When using a custom flavor of cht-android to connect to your CHT instance, the ecosystem supports using deep links to open specific content in the app. (E.g. token login links). Security measures in Android require these deep links be verified either automatically or manually. This assetlinks configuration enables auto-verification for your CHT links in your Android app. The provided JSON file will be served at https://<your CHT instance>/.well-known/assetlinks.json. If you do not provide this configuration, users will be prompted to manually associate the CHT domain with your app.

    For more information, see the docs for building a new CHT Android flavor.

    Specify your digital asset links in the app_settings/assetlinks.json file. The compile-app-settings action in the cht-conf will automatically include this configuration in your app_settings.json file. Then, running the upload-app-settings action will deploy it to the server.

    propertytypedescriptionrequired
    relation[]Array<string>The array should contain only one element: the string delegate_permission/common.handle_all_urls.yes
    targetobjectContains fields to identify associated apps.yes
    target.namespacestringMust be set to android_app.yes
    target.package_namestringThe application ID declared in the app’s build.gradle file.yes
    target.sha256_cert_fingerprintsArray<string>The SHA256 fingerprints of your app’s signing certificate. You can get it with the Java utility keytool, see how exactly in our Android guide.yes

    Code Sample

    This sample associates the Android app org.medicmobile.webapp.mobile to a CHT instance and grants it link-opening rights to the Android app:

    [{
     	"relation": ["delegate_permission/common.handle_all_urls"],
     	"target": {
     		"namespace": "android_app",
    @@ -315,7 +315,8 @@
     Quick Guides >
     Android >
     CHT Android Flavors

    Branding the CHT Android applications

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/reference/app-settings/dhis2/index.html b/apps/reference/app-settings/dhis2/index.html index e29a960a6c..8221574683 100644 --- a/apps/reference/app-settings/dhis2/index.html +++ b/apps/reference/app-settings/dhis2/index.html @@ -1,9 +1,9 @@ -.dhis_data_sets | Community Health Toolkit +.dhis_data_sets | Community Health Toolkit

    .dhis_data_sets

    DHIS2 Integration: Instructions and schema for defining DHIS2 integrations

    From 3.9.0 it is possible to integrate with DHIS2 by modifying the dhis_data_sets property in app_settings.json.

    See Also: DHIS2 Integration

    app_settings.js .dhis_data_sets[]

    PropertyTypeDescriptionRequired
    idstringThe data set id from DHIS2 with which to integrateYes
    translation_keystringThe translation key of the DHIS2 data set name to be displayedYes

    Code samples

    Configure the id and translation_key of the DHIS2 data set. The id corresponds to the id of the data set in the DHIS2 instance you want to integrate with. The translation_key corresponds to the name of the DHIS2 data set as it appears in the dropdown in the App Management app.

    app_settings.js

    "dhis_data_sets": [
    + Create project issue

    .dhis_data_sets

    DHIS2 Integration: Instructions and schema for defining DHIS2 integrations

    From 3.9.0 it is possible to integrate with DHIS2 by modifying the dhis_data_sets property in app_settings.json.

    See Also: DHIS2 Integration

    app_settings.js .dhis_data_sets[]

    PropertyTypeDescriptionRequired
    idstringThe data set id from DHIS2 with which to integrateYes
    translation_keystringThe translation key of the DHIS2 data set name to be displayedYes

    Code samples

    Configure the id and translation_key of the DHIS2 data set. The id corresponds to the id of the data set in the DHIS2 instance you want to integrate with. The translation_key corresponds to the name of the DHIS2 data set as it appears in the dropdown in the App Management app.

    app_settings.js

    "dhis_data_sets": [
       {
         "id": "VMuFODsyWaO",
         "translation_key": "dhis.dataset.moh515"
       }
     ],
     
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/reference/app-settings/forms/index.html b/apps/reference/app-settings/forms/index.html index f45f3fdefd..a6d4faad61 100644 --- a/apps/reference/app-settings/forms/index.html +++ b/apps/reference/app-settings/forms/index.html @@ -1,9 +1,9 @@ -.forms | Community Health Toolkit +.forms | Community Health Toolkit

    .forms

    JSON Forms: Instructions and schema for defining JSON forms used for handling reports from SMS and external tools

    JSON forms are defined in either the base_settings.json or the app_settings/forms.json file and compiled in to the app_settings.json file with the compile-app-settings action in the cht-conf tool. JSON Forms are used for parsing reports from formatted SMS, SIM applications, and Medic Collect. JSON form definitions are also used for interoperability with third-party systems. Each form is defined as an JSON form object according to the following schema. The key for each object must be unique and all characters must be uppercase.

    app_settings.json .forms{}

    propertytypedescriptionrequired
    metaobjectInformation about the report.yes
    meta.codestringThe unique form identifier, which will be sent with all reports of this form. Must be the same as the form’s key.yes
    meta.iconstringName of the icon resource shown in the app.no
    meta.translation_keystringName of the form shown in the app.no
    meta.subject_keystringA translation key to display in the report list instead of the subject name (default). The translation is provided with a summary of the report which can be used for evaluation, eg: Case registration {{case_id}}. Useful properties available in the summary include: from (the phone number of the sender), phone (the phone number of the report contact), form (the form code), subject.name (the name of the subject), and case_id (the generated case id). Added in 3.9.0no
    fieldsobjectCollection of field objects included in the form.yes
    fields.${field}objectField that is part of the form.yes
    fields.${field}.typestringData type of field:
    - "integer": a whole number
    - "string": any collection of characters
    - "date": a date in the format YYYY-mm-dd, for example “2019-01-28”
    - "bsDate": a Bikram Sambat date in the form YYYY-mm-dd, for example “2078-02-32”
    - "bsYear": a Bikram Sambat year in the form YYYY, for example “2078”
    - "bsMonth": a Bikram Sambat month, for example “9”
    - "bsDay": a Bikram Sambat day, for example “8”
    - "boolean": true or false, represented by the digit 1 and 0 respectively (native JSON booleans are also supported if sending via JSON)
    - "custom": Only possible for JSON forms that are passed as actual JSON (not an SMS that gets parsed into JSON). Effectively any non-specific data structure, which will be taken without validation.
    yes
    fields.${field}.labelsobjectno
    fields.${field}.labels.shortstring, object with translation_key field, or translation objectLabel shown for field in the app, seen when report is viewed in Reports tab. If missing, label will default to a translation key of report.${form_name}.${field_name} (eg report.f.patient_id) which can be translated in the app languages.no
    fields.${field}.labels.tinystringUnique identifier within the form for this field. Used in form submission to bind values to fields. Not required for all submission formats.no
    fields.${field}.positionintegerZero based order of this field for incoming reports.no
    fields.${field}.flagsobjectAdditional instructions that could be used by form renderers. For instance { "input_digits_only": true } indicated to SIM applications to show the number keyboard. Obsolete.no
    fields.${field}.lengtharray with two integersInclusive range accepted for length of the field.no
    fields.${field}.requiredbooleanDetermines if a report without this field is considered valid.no
    public_formbooleanDetermines if reports will be accepted from phone numbers not associated to a contact. Set to false if you want to reject reports from unknown senders. Default: true.no
    facility_referencestringThe form field whose value is to be used to match the incoming report to a contact’s rc_code. Useful when reports are sent on behalf of a facility by unknown or various phone numbers. Requires the update_clinics transition.no

    Code Sample

    The following form has two fields, one for the patient ID, another for notes.

    app_settings.json

    .forms

    JSON Forms: Instructions and schema for defining JSON forms used for handling reports from SMS and external tools

    JSON forms are defined in either the base_settings.json or the app_settings/forms.json file and compiled in to the app_settings.json file with the compile-app-settings action in the cht-conf tool. JSON Forms are used for parsing reports from formatted SMS, SIM applications, and Medic Collect. JSON form definitions are also used for interoperability with third-party systems. Each form is defined as an JSON form object according to the following schema. The key for each object must be unique and all characters must be uppercase.

    app_settings.json .forms{}

    propertytypedescriptionrequired
    metaobjectInformation about the report.yes
    meta.codestringThe unique form identifier, which will be sent with all reports of this form. Must be the same as the form’s key.yes
    meta.iconstringName of the icon resource shown in the app.no
    meta.translation_keystringName of the form shown in the app.no
    meta.subject_keystringA translation key to display in the report list instead of the subject name (default). The translation is provided with a summary of the report which can be used for evaluation, eg: Case registration {{case_id}}. Useful properties available in the summary include: from (the phone number of the sender), phone (the phone number of the report contact), form (the form code), subject.name (the name of the subject), and case_id (the generated case id). Added in 3.9.0no
    fieldsobjectCollection of field objects included in the form.yes
    fields.${field}objectField that is part of the form.yes
    fields.${field}.typestringData type of field:
    - "integer": a whole number
    - "string": any collection of characters
    - "date": a date in the format YYYY-mm-dd, for example “2019-01-28”
    - "bsDate": a Bikram Sambat date in the form YYYY-mm-dd, for example “2078-02-32”
    - "bsYear": a Bikram Sambat year in the form YYYY, for example “2078”
    - "bsMonth": a Bikram Sambat month, for example “9”
    - "bsDay": a Bikram Sambat day, for example “8”
    - "boolean": true or false, represented by the digit 1 and 0 respectively (native JSON booleans are also supported if sending via JSON)
    - "custom": Only possible for JSON forms that are passed as actual JSON (not an SMS that gets parsed into JSON). Effectively any non-specific data structure, which will be taken without validation.
    yes
    fields.${field}.labelsobjectno
    fields.${field}.labels.shortstring, object with translation_key field, or translation objectLabel shown for field in the app, seen when report is viewed in Reports tab. If missing, label will default to a translation key of report.${form_name}.${field_name} (eg report.f.patient_id) which can be translated in the app languages.no
    fields.${field}.labels.tinystringUnique identifier within the form for this field. Used in form submission to bind values to fields. Not required for all submission formats.no
    fields.${field}.positionintegerZero based order of this field for incoming reports.no
    fields.${field}.flagsobjectAdditional instructions that could be used by form renderers. For instance { "input_digits_only": true } indicated to SIM applications to show the number keyboard. Obsolete.no
    fields.${field}.lengtharray with two integersInclusive range accepted for length of the field.no
    fields.${field}.requiredbooleanDetermines if a report without this field is considered valid.no
    public_formbooleanDetermines if reports will be accepted from phone numbers not associated to a contact. Set to false if you want to reject reports from unknown senders. Default: true.no
    facility_referencestringThe form field whose value is to be used to match the incoming report to a contact’s rc_code. Useful when reports are sent on behalf of a facility by unknown or various phone numbers. Requires the update_clinics transition.no

    Code Sample

    The following form has two fields, one for the patient ID, another for notes.

    app_settings.json

    "forms": {
       "F": {
         "meta": {
           "code": "F",
    @@ -343,7 +343,8 @@
       }
     }
     
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/reference/app-settings/header_tabs/index.html b/apps/reference/app-settings/header_tabs/index.html index 26b9d3cd0b..3c0d30b7e2 100644 --- a/apps/reference/app-settings/header_tabs/index.html +++ b/apps/reference/app-settings/header_tabs/index.html @@ -1,9 +1,9 @@ -.header_tabs | Community Health Toolkit +.header_tabs | Community Health Toolkit

    .header_tabs

    Configuring app header tab icons

    app_settings.js .header_tabs

    As of 3.10.0, app header tabs icons can be configured by modifying the header_tabs section in the app settings. + Create project issue

    .header_tabs

    Configuring app header tab icons

    app_settings.js .header_tabs

    As of 3.10.0, app header tabs icons can be configured by modifying the header_tabs section in the app settings. The header_tabs section consists of key:value pairs, where the key is the name of the tab to configure. These values can also be changed from the Admin console, on the Images page under the “Header tabs icons” tab.

    {
       "messages": {
    @@ -315,7 +315,8 @@
       }
     }
     

    Available tabs

    tab namedefault FontAwesome icon
    messagesfa-envelope
    tasksfa-flag
    reportsfa-list-alt
    contactsfa-user
    analyticsfa-bar-chart-o

    app_settings.js .header_tabs.<tab_name>

    propertytyperequireddescription
    iconstringnoFontAwesome icon name
    resource_iconstringnoResource icon name.
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/reference/app-settings/hierarchy/index.html b/apps/reference/app-settings/hierarchy/index.html index d8e8468f09..c68b97f62e 100644 --- a/apps/reference/app-settings/hierarchy/index.html +++ b/apps/reference/app-settings/hierarchy/index.html @@ -1,9 +1,9 @@ -.contact_types | Community Health Toolkit +.contact_types | Community Health Toolkit

    .contact_types

    Hierarchy: Setting the types of places and people in the hierarchy

    From 3.7.0 it is possible to configure what types of places and people are available by modifying the contact_types array in the app settings. Each type has the following properties.

    app_settings.json .contact_types[]

    PropertyDescriptionRequired
    idString identifier for the type. At times this will be used to sort the contacts in the UI so it is recommended to using a number prefix with gaps between numbers, eg: 10-district, 20-region, etc.Yes.
    name_keyThe translation key used for the title for the contact profile.No, defaults to ‘contact.profile’.
    group_keyThe translation key used for the title of a list of contacts of this type.Yes.
    create_keyThe translation key used on the button for creating new contacts of this type.Yes.
    edit_keyThe translation key used on the button for editing contacts of this type.Yes.
    primary_contact_keyThe translation key used to identify a person as the primary contact of contacts of this type.No, defaults to ‘Primary contact’.
    parentsAn array of strings of IDs of parent types. If more than one then this type can appear in different places in the hierarchy. If more than one type lists the same type as a parent then a user will get a dropdown of places to create. If no parents then contacts of this type will be at the top of the hierarchy and cannot be added as a child of any contact.No, defaults to no parents.
    iconThe string ID for the icon to show beside contacts of this type.Yes.
    create_formThe string ID for the xform used to create contacts of this type.Yes.
    edit_formThe string ID for the xform used to edit contacts of this type.No, defaults to the create_form.
    count_visitsWhether or not to show a count of visits for contacts of this type. Requires UHC to be enabled.No, defaults to false.
    sort_by_dobWhether or not to sort contacts by date of birth in the contact detail page. By default, contacts are sorted alphabetically.No, defaults to false.
    personWhether this is a person type or a place type.No, defaults to false.

    Forms

    When creating contacts the type will be automatically assigned based on the button the user clicked. However if the form also creates sibling or child contacts these nested sections must specify a type field with a hardcoded value of “contact” and a contact_type field with the ID of the desired contact type.

    Changing the configuration

    You can change any contact type configuration easily except for the IDs. To change the ID of a contact type in configuration of a project which already has contact data the contact docs will also have be updated to have a type of “contact” and a contact_type with the new ID of the contact type.

    Migration

    If you already have person and place documents, switching from using the fixed hierarchy requires that you also update all the existing docs. Each contact and report holds the IDs of ancestors in their hierarchy so they will all need to be updated to be consistent with the changes you’ve made. You can use the cht-conf move-contacts command to help with this migration.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/reference/app-settings/index.html b/apps/reference/app-settings/index.html index 25e2f5dc83..391297d589 100644 --- a/apps/reference/app-settings/index.html +++ b/apps/reference/app-settings/index.html @@ -1,9 +1,9 @@ -app_settings.json | Community Health Toolkit +app_settings.json | Community Health Toolkit

    app_settings.json

    Settings: The primary location of settings for CHT applications

    The settings which control CHT apps are defined in the app_settings.json file, and stored in the settings doc in the database. Some settings can be modified in the App Management app, which updates the same settings file in the database.

    The settings get compiled into the app_settings.json file with the compile-app-settings action in the cht-conf tool. + Create project issue

    app_settings.json

    Settings: The primary location of settings for CHT applications

    The settings which control CHT apps are defined in the app_settings.json file, and stored in the settings doc in the database. Some settings can be modified in the App Management app, which updates the same settings file in the database.

    The settings get compiled into the app_settings.json file with the compile-app-settings action in the cht-conf tool. Manually configurable settings are added to the app_settings folder at the root of the config folder. The app_settings/base_settings.json file can be manually edited to modify individual settings. forms, schedules, @@ -341,7 +341,8 @@ And you can use || for logical OR. Find documentation on these rules in the Pupil documentation.

    So you can do this : rule: 'iEquals("mary") || iEquals("john")' matches “mary”, “Mary”, “john”, “John”, “JOhN”, etc. Not “maryjohn”

    CHT validation functions

    FunctionDescription
    unique(*fields)Returns true if no existing valid report has the same value for all of the listed fields. Fields are compared to those at the top level and within fields for every report doc. To include the form type use 'form' as one of the fields. Eg unique('form', 'patient_id') checks that this form was never submitted for this patient.
    uniquePhone(field)Returns true if contact with the phone number already exists. Added in 4.3.0
    validPhone(field)Returns true if the field is a valid phone number else returns false. Added in 4.3.1
    uniqueWithin(*fields, time_period)Same as unique but the last argument is the time period in which to search. Eg uniqueWithin('form', 'patient_id', '1 week') checks that the same form wasn’t submitted for this patient in the past week.
    exists(form_id, field)Returns true if a report matches the form_id and value for field. This is useful to check that a patient was registered for a service before reporting about it. Eg exists('REG', 'patient_id') checks that a REG form was already submitted for a patient. As of 2.12 most uses of this function are obsolete because checking for a valid patient_id is done automatically by the accept_patient_report transition using registration_not_found in the messages.event_type.
    isISOWeek(weekFieldName[, yearFieldName])Returns true if the current report has a week value that is less or equal to the number of ISO weeks of the current year or the year value of the same report. The first parameter is the field name for the week and the second parameter is the field name for the year: isISOWeek('week', 'year'). If the second parameter is not specified, then the current year is used: isISOWeek('week').
    isAfter(duration)Returns true if the date property comes after today plus the duration. For this to work, the property should be a date type. e.g. isAfter('2 weeks') checks that the date property is at least two weeks in the future from today. Negative values are also supported.
    isBefore(duration)Returns true if the date property comes before today minus the duration. For this to work, the property should be a date type. e.g. isBefore('2 weeks') checks that the date property is at least two weeks in the past from today. Negative values are also supported.

    .accept_case_reports

    Case Reports: Defining SMS workflows with schedules and registration of case reports.

    .assetlinks

    Assetlinks: Defining the Digital Asset Links JSON file associating your domain with your Android app.

    .contact_types

    Hierarchy: Setting the types of places and people in the hierarchy

    .dhis_data_sets

    DHIS2 Integration: Instructions and schema for defining DHIS2 integrations

    .forms

    JSON Forms: Instructions and schema for defining JSON forms used for handling reports from SMS and external tools

    .header_tabs

    Configuring app header tab icons

    .outbound

    Outbound Push: Exchanging data between CHT applications and other tools

    .patient_reports

    Patient Reports: Defining SMS workflows with schedules, registration, and patient reports.

    .permissions

    User Permissions: Assigning fine grained settings for user roles

    .registrations

    Registrations: Defining SMS workflows with schedules, registration, and patient reports.

    .reminders

    Configure SMS reminders to users

    .replication_depth

    Replications: Instructions for replication depth

    .replications [deprecated]

    Replications [deprecated]: Configure replication of databases to a main meta database.

    .roles

    User Roles: Defining the roles that can be assigned to users.

    .schedules

    SMS Schedules: Defining SMS workflows with schedules, registration, and patient reports.

    .sms

    SMS Settings: Instructions and schema for defining SMS settings

    .token_login

    Token login: Instructions and schema for Login by SMS

    .transitions

    Sentinel Transitions: functions executed when database documents change

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/reference/app-settings/index.xml b/apps/reference/app-settings/index.xml index 637dd18431..d6c8aa4c97 100644 --- a/apps/reference/app-settings/index.xml +++ b/apps/reference/app-settings/index.xml @@ -1,2330 +1,21 @@ -Community Health Toolkit – app_settings.jsonhttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/Recent content in app_settings.json on Community Health ToolkitHugo -- gohugo.ioenApps: .accept_case_reportshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/accept_case_reports/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/accept_case_reports/ -<p>The <code>accept_case_reports</code> key contains the actions to take when reports about cases are received.</p> -<h2 id="app_settingsjson-accept_case_reports"><code>app_settings.json .accept_case_reports[]</code></h2> -<table> -<thead> -<tr> -<th>property</th> -<th>description</th> -<th>required</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>form</code></td> -<td>Form ID of the case form.</td> -<td>yes</td> -</tr> -<tr> -<td><code>silence_type</code></td> -<td>A comma separated list of schedules to mute.</td> -<td>no</td> -</tr> -<tr> -<td><code>silence_for</code></td> -<td>Duration from when the report was submitted for which messages should be muted. It is structured as a string with an integer value followed by a space and the time unit. For instance <code>8 weeks</code> or <code>2 days</code>. The units available are <code>seconds</code>, <code>minutes</code>, <code>hours</code>, <code>days</code>, <code>weeks</code>, <code>months</code>, <code>years</code>, and their singular forms as well. When a message is muted all messages belonging to the same group will be muted, even if it falls outside of this time period. See <code>messages[].group</code> in <em>Schedules</em> for related info.</td> -<td>no</td> -</tr> -<tr> -<td><code>validations</code></td> -<td>A set of validations to perform on incoming reports. Read more information about <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/#validations">configuring validation rules</a>.</td> -<td>no</td> -</tr> -<tr> -<td><code>validations.join_responses</code></td> -<td>A boolean specifying whether validation messages should be combined into one message.</td> -<td>no</td> -</tr> -<tr> -<td><code>validations.list[]</code></td> -<td>An array of validation rules a report should pass to be considered valid.</td> -<td>no</td> -</tr> -<tr> -<td><code>validations.list[].property</code></td> -<td>Report field for which this validation rule will be applied.</td> -<td>no</td> -</tr> -<tr> -<td><code>validations.list[].rule</code></td> -<td>Validation condition to be applied to the property field.</td> -<td>no</td> -</tr> -<tr> -<td><code>validations.list[].translation_key</code></td> -<td>Translation key for the message reply to be sent if a report fails this rule.</td> -<td>no</td> -</tr> -<tr> -<td><code>messages</code></td> -<td>An array of automated responses to incoming reports.</td> -<td>no</td> -</tr> -<tr> -<td><code>messages[].translation_key</code></td> -<td>Translation key for the message text associated with this event</td> -<td>no</td> -</tr> -<tr> -<td><code>messages[].event_type</code></td> -<td>An event that will trigger sending of this message. Typical values are: <code>report_accepted</code> when the report has been successfully validated, <code>registration_not_found</code> when the case ID supplied in the report doesn&rsquo;t match any case ID issued by Medic. Read the <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/transitions/#muting">documentation on muting</a>.</td> -<td>no</td> -</tr> -<tr> -<td><code>messages[].recipient</code></td> -<td>Who the message should be sent to. Use <code>reporting_unit</code> for the sender of the report, <code>clinic</code> for clinic contact, and <code>parent</code> for the parent contact.</td> -<td>no</td> -</tr> -</tbody> -</table> -<h2 id="code-sample">Code sample</h2> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#4e9a06">&#34;accept_case_reports&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">[{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;SIGNOFF&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;validations&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;messages&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;event_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;report_accepted&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;bool_expr&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;some expression&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;message&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;some message&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;recipient&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;some recipients&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}]</span> -</span></span></code></pre></div>Apps: .assetlinkshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/assetlinks/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/assetlinks/ -<p><em>Requires CHT Core 4.7.0+, CHT Conf 3.22.0+, and CHT Android 1.3.0+</em></p> -<p>When using a <a href="https://docs.communityhealthtoolkit.org/apps/guides/android/branding/">custom flavor of cht-android</a> to connect to your CHT instance, the ecosystem supports using <a href="https://docs.communityhealthtoolkit.org/apps/features/integrations/android/#sending-a-url">deep links</a> to open specific content in the app. (E.g. <a href="https://docs.communityhealthtoolkit.org/apps/concepts/access/#magic-links-for-logging-in-token-login">token login links</a>). Security measures in Android require these deep links <a href="https://developer.android.com/training/app-links/verify-android-applinks">be verified</a> either automatically or manually. This <code>assetlinks</code> configuration enables auto-verification for your CHT links in your Android app. The provided JSON file will be served at <code>https://&lt;your CHT instance&gt;/.well-known/assetlinks.json</code>. If you do not provide this configuration, users will be prompted to manually associate the CHT domain with your app.</p> -<p>For more information, see the <a href="https://docs.communityhealthtoolkit.org/apps/guides/android/branding/#android-app-links-verification">docs for building a new CHT Android flavor</a>.</p> -<p>Specify your digital asset links in the <code>app_settings/assetlinks.json</code> file. The <code>compile-app-settings</code> action in the <code>cht-conf</code> will automatically include this configuration in your <code>app_settings.json</code> file. Then, running the <code>upload-app-settings</code> action will deploy it to the server.</p> -<h2 id="app_settingsjson-assetlinks"><code>app_settings.json .assetlinks[]</code></h2> -<table> -<thead> -<tr> -<th>property</th> -<th>type</th> -<th>description</th> -<th>required</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>relation[]</code></td> -<td><code>Array&lt;string&gt;</code></td> -<td>The array should contain only one element: the string <code>delegate_permission/common.handle_all_urls</code>.</td> -<td>yes</td> -</tr> -<tr> -<td><code>target</code></td> -<td><code>object</code></td> -<td>Contains fields to identify associated apps.</td> -<td>yes</td> -</tr> -<tr> -<td><code>target.namespace</code></td> -<td><code>string</code></td> -<td>Must be set to <code>android_app</code>.</td> -<td>yes</td> -</tr> -<tr> -<td><code>target.package_name</code></td> -<td><code>string</code></td> -<td>The <a href="https://docs.communityhealthtoolkit.org/apps/guides/android/branding/#2-new-brand">application ID</a> declared in the app&rsquo;s <code>build.gradle</code> file.</td> -<td>yes</td> -</tr> -<tr> -<td><code>target.sha256_cert_fingerprints</code></td> -<td><code>Array&lt;string&gt;</code></td> -<td>The SHA256 fingerprints of your app’s signing certificate. You can get it with the Java utility <code>keytool</code>, see how exactly in our <a href="https://docs.communityhealthtoolkit.org/apps/guides/android/branding/#hosting-assetlinksjson-with-the-cht">Android guide</a>.</td> -<td>yes</td> -</tr> -</tbody> -</table> -<h2 id="code-sample">Code Sample</h2> -<p>This sample associates the Android app <code>org.medicmobile.webapp.mobile</code> to a CHT instance and grants it link-opening rights to the Android app:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">[{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;relation&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#34;delegate_permission/common.handle_all_urls&#34;</span><span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;target&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;namespace&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;android_app&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;package_name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;org.medicmobile.webapp.mobile&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;sha256_cert_fingerprints&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#34;62:BF:C1:78:24:D8:4D:5C:B4:E1:8B:66:98:EA:14:16:57:6F:A4:E5:96:CD:93:81:B2:65:19:71:A7:80:EA:4D&#34;</span><span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}]</span> -</span></span></code></pre></div>Apps: .contact_typeshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/hierarchy/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/hierarchy/ -<p>From 3.7.0 it is possible to configure what types of places and people are available by modifying the <code>contact_types</code> array in the app settings. Each type has the following properties.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Prior to version 3.7.0, CHT Core supported 4 contact types - 3 place types (<code>clinic</code>, <code>health_center</code>, <code>district_hospital</code>) and one person type (<code>person</code>). -</div> -<h3 id="app_settingsjson-contact_types"><code>app_settings.json .contact_types[]</code></h3> -<table> -<thead> -<tr> -<th>Property</th> -<th>Description</th> -<th>Required</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>id</code></td> -<td>String identifier for the type. At times this will be used to sort the contacts in the UI so it is recommended to using a number prefix with gaps between numbers, eg: <code>10-district</code>, <code>20-region</code>, etc.</td> -<td>Yes.</td> -</tr> -<tr> -<td><code>name_key</code></td> -<td>The translation key used for the title for the contact profile.</td> -<td>No, defaults to &lsquo;contact.profile&rsquo;.</td> -</tr> -<tr> -<td><code>group_key</code></td> -<td>The translation key used for the title of a list of contacts of this type.</td> -<td>Yes.</td> -</tr> -<tr> -<td><code>create_key</code></td> -<td>The translation key used on the button for creating new contacts of this type.</td> -<td>Yes.</td> -</tr> -<tr> -<td><code>edit_key</code></td> -<td>The translation key used on the button for editing contacts of this type.</td> -<td>Yes.</td> -</tr> -<tr> -<td><code>primary_contact_key</code></td> -<td>The translation key used to identify a person as the primary contact of contacts of this type.</td> -<td>No, defaults to &lsquo;Primary contact&rsquo;.</td> -</tr> -<tr> -<td><code>parents</code></td> -<td>An array of strings of IDs of parent types. If more than one then this type can appear in different places in the hierarchy. If more than one type lists the same type as a parent then a user will get a dropdown of places to create. If no parents then contacts of this type will be at the top of the hierarchy and cannot be added as a child of any contact.</td> -<td>No, defaults to no parents.</td> -</tr> -<tr> -<td><code>icon</code></td> -<td>The string ID for the icon to show beside contacts of this type.</td> -<td>Yes.</td> -</tr> -<tr> -<td><code>create_form</code></td> -<td>The string ID for the xform used to create contacts of this type.</td> -<td>Yes.</td> -</tr> -<tr> -<td><code>edit_form</code></td> -<td>The string ID for the xform used to edit contacts of this type.</td> -<td>No, defaults to the create_form.</td> -</tr> -<tr> -<td><code>count_visits</code></td> -<td>Whether or not to show a count of visits for contacts of this type. Requires UHC to be enabled.</td> -<td>No, defaults to <code>false</code>.</td> -</tr> -<tr> -<td><code>sort_by_dob</code></td> -<td>Whether or not to sort contacts by date of birth in the contact detail page. By default, contacts are sorted alphabetically.</td> -<td>No, defaults to <code>false</code>.</td> -</tr> -<tr> -<td><code>person</code></td> -<td>Whether this is a person type or a place type.</td> -<td>No, defaults to <code>false</code>.</td> -</tr> -</tbody> -</table> -<h3 id="forms">Forms</h3> -<p>When creating contacts the type will be automatically assigned based on the button the user clicked. However if the form also creates sibling or child contacts these nested sections must specify a <code>type</code> field with a hardcoded value of &ldquo;contact&rdquo; and a <code>contact_type</code> field with the ID of the desired contact type.</p> -<h3 id="changing-the-configuration">Changing the configuration</h3> -<p>You can change any contact type configuration easily except for the IDs. To change the ID of a contact type in configuration of a project which already has contact data the contact docs will also have be updated to have a <code>type</code> of &ldquo;contact&rdquo; and a <code>contact_type</code> with the new ID of the contact type.</p> -<h3 id="migration">Migration</h3> -<p>If you already have person and place documents, switching from using the fixed hierarchy requires that you also update all the existing docs. Each contact and report holds the IDs of ancestors in their hierarchy so they will all need to be updated to be consistent with the changes you&rsquo;ve made. You can use the cht-conf <code>move-contacts</code> command to help with this migration.</p>Apps: .dhis_data_setshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/dhis2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/dhis2/ -<p>From 3.9.0 it is possible to integrate with DHIS2 by modifying the <code>dhis_data_sets</code> property in <code>app_settings.json</code>.</p> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/features/integrations/dhis2/">DHIS2 Integration</a></p> -<h2 id="app_settingsjs-dhis_data_sets"><code>app_settings.js .dhis_data_sets[]</code></h2> -<table> -<thead> -<tr> -<th>Property</th> -<th>Type</th> -<th>Description</th> -<th>Required</th> -</tr> -</thead> -<tbody> -<tr> -<td>id</td> -<td><code>string</code></td> -<td>The data set id from DHIS2 with which to integrate</td> -<td>Yes</td> -</tr> -<tr> -<td>translation_key</td> -<td><code>string</code></td> -<td>The translation key of the DHIS2 data set name to be displayed</td> -<td>Yes</td> -</tr> -</tbody> -</table> -<h2 id="code-samples">Code samples</h2> -<p>Configure the <code>id</code> and <code>translation_key</code> of the DHIS2 data set. The <code>id</code> corresponds to the <code>id</code> of the data set in the DHIS2 instance you want to integrate with. The <code>translation_key</code> corresponds to the name of the DHIS2 data set as it appears in the dropdown in the App Management app.</p> -<h3 id="app_settingsjs"><code>app_settings.js</code></h3> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#4e9a06">&#34;dhis_data_sets&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;VMuFODsyWaO&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;dhis.dataset.moh515&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">]</span><span style="color:#a40000">,</span> -</span></span></code></pre></div>Apps: .formshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/forms/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/forms/ -<p>JSON forms are defined in either the <code>base_settings.json</code> or the <code>app_settings/forms.json</code> file and compiled in to the <code>app_settings.json</code> file with the <code>compile-app-settings</code> action in the <code>cht-conf</code> tool. JSON Forms are used for parsing reports from formatted SMS, SIM applications, and Medic Collect. JSON form definitions are also used for interoperability with third-party systems. Each form is defined as an JSON form object according to the following schema. The key for each object must be unique and all characters must be uppercase.</p> -<h2 id="app_settingsjson-forms"><code>app_settings.json .forms{}</code></h2> -<table> -<thead> -<tr> -<th>property</th> -<th>type</th> -<th>description</th> -<th>required</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>meta</code></td> -<td>object</td> -<td>Information about the report.</td> -<td>yes</td> -</tr> -<tr> -<td><code>meta.code</code></td> -<td>string</td> -<td>The unique form identifier, which will be sent with all reports of this form. Must be the same as the form&rsquo;s key.</td> -<td>yes</td> -</tr> -<tr> -<td><code>meta.icon</code></td> -<td>string</td> -<td>Name of the icon resource shown in the app.</td> -<td>no</td> -</tr> -<tr> -<td><code>meta.translation_key</code></td> -<td>string</td> -<td>Name of the form shown in the app.</td> -<td>no</td> -</tr> -<tr> -<td><code>meta.subject_key</code></td> -<td>string</td> -<td>A translation key to display in the report list instead of the subject name (default). The translation is provided with a summary of the report which can be used for evaluation, eg: <code>Case registration {{case_id}}</code>. Useful properties available in the summary include: <code>from</code> (the phone number of the sender), <code>phone</code> (the phone number of the report contact), <code>form</code> (the form code), <code>subject.name</code> (the name of the subject), and <code>case_id</code> (the generated case id). Added in 3.9.0</td> -<td>no</td> -</tr> -<tr> -<td><code>fields</code></td> -<td>object</td> -<td>Collection of field objects included in the form.</td> -<td>yes</td> -</tr> -<tr> -<td><code>fields.${field}</code></td> -<td>object</td> -<td>Field that is part of the form.</td> -<td>yes</td> -</tr> -<tr> -<td><code>fields.${field}.type</code></td> -<td>string</td> -<td>Data type of field:<br> - <code>&quot;integer&quot;</code>: a whole number<br> - <code>&quot;string&quot;</code>: any collection of characters<br> - <code>&quot;date&quot;</code>: a date in the format <code>YYYY-mm-dd</code>, for example &ldquo;2019-01-28&rdquo;<br> - <code>&quot;bsDate&quot;</code>: a Bikram Sambat date in the form <code>YYYY-mm-dd</code>, for example &ldquo;2078-02-32&rdquo;<br> - <code>&quot;bsYear&quot;</code>: a Bikram Sambat year in the form <code>YYYY</code>, for example &ldquo;2078&rdquo;<br> - <code>&quot;bsMonth&quot;</code>: a Bikram Sambat month, for example &ldquo;9&rdquo;<br> - <code>&quot;bsDay&quot;</code>: a Bikram Sambat day, for example &ldquo;8&rdquo;<br> - <code>&quot;boolean&quot;</code>: true or false, represented by the digit <code>1</code> and <code>0</code> respectively (native JSON booleans are also supported if sending via JSON)<br> - <code>&quot;custom&quot;</code>: Only possible for JSON forms that are passed as actual JSON (not an SMS that gets parsed into JSON). Effectively any non-specific data structure, which will be taken without validation.</td> -<td>yes</td> -</tr> -<tr> -<td><code>fields.${field}.labels</code></td> -<td>object</td> -<td></td> -<td>no</td> -</tr> -<tr> -<td><code>fields.${field}.labels.short</code></td> -<td>string, object with <code>translation_key</code> field, or translation object</td> -<td>Label shown for field in the app, seen when report is viewed in Reports tab. If missing, label will default to a translation key of <code>report.${form_name}.${field_name}</code> (eg <code>report.f.patient_id</code>) which can be translated in the app languages.</td> -<td>no</td> -</tr> -<tr> -<td><code>fields.${field}.labels.tiny</code></td> -<td>string</td> -<td>Unique identifier within the form for this field. Used in form submission to bind values to fields. Not required for all submission formats.</td> -<td>no</td> -</tr> -<tr> -<td><code>fields.${field}.position</code></td> -<td>integer</td> -<td>Zero based order of this field for incoming reports.</td> -<td>no</td> -</tr> -<tr> -<td><code>fields.${field}.flags</code></td> -<td>object</td> -<td>Additional instructions that could be used by form renderers. For instance <code>{ &quot;input_digits_only&quot;: true }</code> indicated to SIM applications to show the number keyboard. Obsolete.</td> -<td>no</td> -</tr> -<tr> -<td><code>fields.${field}.length</code></td> -<td>array with two integers</td> -<td>Inclusive range accepted for length of the field.</td> -<td>no</td> -</tr> -<tr> -<td><code>fields.${field}.required</code></td> -<td>boolean</td> -<td>Determines if a report without this field is considered valid.</td> -<td>no</td> -</tr> -<tr> -<td><code>public_form</code></td> -<td>boolean</td> -<td>Determines if reports will be accepted from phone numbers not associated to a contact. Set to false if you want to reject reports from unknown senders. Default: true.</td> -<td>no</td> -</tr> -<tr> -<td><code>facility_reference</code></td> -<td>string</td> -<td>The form field whose value is to be used to match the incoming report to a contact&rsquo;s <code>rc_code</code>. Useful when reports are sent on behalf of a facility by unknown or various phone numbers. Requires the <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/transitions/#available-transitions"><code>update_clinics</code> transition</a>.</td> -<td>no</td> -</tr> -</tbody> -</table> -<h2 id="code-sample">Code Sample</h2> -<p>The following form has two fields, one for the patient ID, another for notes.</p> -<h3 id="app_settingsjson"><code>app_settings.json</code></h3> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#4e9a06">&#34;forms&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;F&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;meta&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;code&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;F&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;icon&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;risk&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;form.flag.title&#34;</span> <span style="color:#8f5902;font-style:italic">// displayed in the webapp -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;fields&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;patient_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#8f5902;font-style:italic">// this is used for the property name when the report doc is created -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">&#34;labels&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;short&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;form.flag.patient_id.short&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> <span style="color:#8f5902;font-style:italic">// displayed in the webapp -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">&#34;tiny&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;pid&#34;</span> <span style="color:#8f5902;font-style:italic">// used in form submission to bind values to fields - not required for all submission formats -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;position&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#8f5902;font-style:italic">// specifies where in the SMS this value should be -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;string&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;length&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#0000cf;font-weight:bold">5</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#0000cf;font-weight:bold">13</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;required&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;notes&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;labels&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;short&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;form.flag.notes.short&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;tiny&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;form.flag.notes.tiny&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;position&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;string&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;length&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#0000cf;font-weight:bold">100</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;required&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;public_form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div>Apps: .header_tabshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/header_tabs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/header_tabs/ -<h2 id="app_settingsjs-header_tabs"><code>app_settings.js .header_tabs</code></h2> -<p>As of <code>3.10.0</code>, app header tabs icons can be configured by modifying the <code>header_tabs</code> section in the app settings. -The <code>header_tabs</code> section consists of key:value pairs, where the key is the name of the tab to configure. -These values can also be changed from the Admin console, on the Images page under the &ldquo;Header tabs icons&rdquo; tab.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;messages&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;icon&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;fa-user&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;tasks&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;resource_icon&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;medic-health-center&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;analytics&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;icon&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;fa-flag&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;resource_icon&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;icon-treatment&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h4 id="available-tabs">Available tabs</h4> -<table> -<thead> -<tr> -<th>tab name</th> -<th>default <a href="https://fontawesome.com/v4.7.0/">FontAwesome</a> icon</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>messages</code></td> -<td><code>fa-envelope</code></td> -</tr> -<tr> -<td><code>tasks</code></td> -<td><code>fa-flag</code></td> -</tr> -<tr> -<td><code>reports</code></td> -<td><code>fa-list-alt</code></td> -</tr> -<tr> -<td><code>contacts</code></td> -<td><code>fa-user</code></td> -</tr> -<tr> -<td><code>analytics</code></td> -<td><code>fa-bar-chart-o</code></td> -</tr> -</tbody> -</table> -<h4 id="app_settingsjs-header_tabstab_name"><code>app_settings.js .header_tabs.&lt;tab_name&gt;</code></h4> -<table> -<thead> -<tr> -<th>property</th> -<th>type</th> -<th>required</th> -<th>description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>icon</code></td> -<td>string</td> -<td>no</td> -<td><a href="https://fontawesome.com/v4.7.0/">FontAwesome</a> icon name</td> -</tr> -<tr> -<td><code>resource_icon</code></td> -<td>string</td> -<td>no</td> -<td><a href="https://docs.communityhealthtoolkit.org/apps/reference/resources/">Resource icon</a> name.</td> -</tr> -</tbody> -</table> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Selected Resource icons take precedence over selected FontAwesome icons.<br> -If the selected Resource icon does not exist, the default icon will be displayed.<br> -If the selected FontAwesome icon does not exist, the default icon will be displayed. <br> -If the selected Resource icon is not an <code>svg</code> that supports css color filling, the icon will have a static color for every state (inactive, active and hovered). Colors will change only for FontAwesome icons and css fillable <code>svg</code> icons. <br> -The Admin console Resource icon dropdowns only supports selecting <code>svg</code> icons. If mismatched Resource Icons are selected, they will be deselected upon saving. <br> -Resource icons images will be sized to <code>24 x 24px</code> when displayed. -</div>Apps: .outboundhttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/outbound/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/outbound/ -<p><em>Outbound is only available in CHT Core 3.5.0 and above</em></p> -<p>Outbound push allows configurers to have the creation or editing of CouchDB documents trigger outbound REST requests using the data in that document. For example, upon receiving a referral report you could send that referral to an external facility system that will manage and process that event.</p> -<p>These triggers can apply to all document types (not just common types such as reports or contacts) and as such care should be taken to only send the documents you intend (see configuration of <code>relevant_to</code> below).</p> -<h2 id="configuration">Configuration</h2> -<p>For outbound pushes to occur, you must <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/transitions/#available-transitions">enable the <code>mark_for_outbound</code> transition in config</a>:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;transitions&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;mark_for_outbound&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>The rest of the configuration is against the <code>outbound</code> configuration property, which allows you to configure as many outbound pushes as you like, keyed off a unique name:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;outbound&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;first config&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;relevant_to&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;...&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;destination&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;mapping&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;cron&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;...&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;second config&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>Each outbound push configuration contains the following properties: <code>relevant_to</code>, <code>destination</code> and <code>mapping</code>.</p> -<h3 id="relevant_to">relevant_to</h3> -<p>An &ldquo;expression&rdquo; (some JS code that resolves to a truthy or falsy value) that determines whether this configuration is relevant to a document. The document is passed to the expression as <code>doc</code>, and if relevant is fully hydrated before being passed (i.e. the attached contact, its parents etc are fully attached instead of just being stubs).</p> -<p>Example: you want to send a referral to a facility&rsquo;s EMR system when a CHW refers a patient:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;relevant_to&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;doc.type === &#39;data_record&#39; &amp;&amp; doc.form === &#39;referral&#39;&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p><strong>Note:</strong> all documents that Sentinel processes can be picked up by your configuration, so it&rsquo;s important to correctly configure <code>relevant_to</code>. Checking the document type as shown above is probably a good start.</p> -<h3 id="destination">destination</h3> -<p>A complex property that defines the details of the connection to the external service. It currently supports several authentication types: basic authentication, HTTP authorization request header, and a custom authentication mode for Muso SIH.</p> -<p>Basic auth example:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;destination&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;base_url&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;https://example.com&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;auth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;basic&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;username&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;admin&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;password_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;example.com&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;path&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;/api/v1/referral&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>Here <code>password_key</code> is a key used to find the password in CHT credentials. For more information about how to store credentials, refer to the <a href="https://docs.communityhealthtoolkit.org/apps/reference/api#put-apiv1credentials">API documentation</a>.</p> -<p>If you don&rsquo;t provide an authentication parameter then the request will be sent without authentication.</p> -<p>As of 3.9, the <code>header</code> type is also supported, which sends authentication credentials via a HTTP request header: <code>Authorization: '&lt;value&gt;'</code>. The value is set in CHT credentials configuration, and referred to by the <code>value_key</code>, similarly to the <code>password_key</code>. The value must match the credentials needed for the third party tool, and is generally formatted as <code>&lt;type&gt; &lt;credentials&gt;</code>. For instance, to send data to RapidPro, the value in the configuration would be set to the complete RapidPro API Token: eg <code>Token 123456789abcdef</code>.</p> -<p>Header auth example:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;destination&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;base_url&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;https://example.com&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;auth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;header&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Authorization&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;value_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;example.com&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;path&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;/api/v1/referral&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="mapping">mapping</h3> -<p>A complex property that declares how the payload to be sent to the <code>destination</code> should be created.</p> -<p>Each key is a string <a href="https://github.com/mariocasciaro/object-path#user-content-usage">object path</a> to a location in the payload, and each value is either:</p> -<ul> -<li>a string object path to the location of the source data in the report being processed, where the value is required to exist</li> -<li>an object with either a <code>path</code> property that represents a string object path as above, or an <code>expr</code> property which is an expression similar to <code>relevant_to</code> to determine the resulting value. If you wish for the value to be optional (that is to say it&rsquo;s OK if <code>path</code> or <code>expr</code> evaluate to <code>undefined</code>) you may also set <code>optional</code> to <code>true</code></li> -</ul> -<h4 id="a-mapping-example">A mapping example</h4> -<p>Given the following example (incomplete) report:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;abc-1234&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;data_record&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;referral&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;reported_date&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1555069530966</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;fields&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;patient_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;12345&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;patient_temp_deg_F&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">100</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;danger_signs&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#34;V&#34;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#34;BL&#34;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#34;D&#34;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#34;IG&#34;</span><span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;contact&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;def-5678&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Jane CHW&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;hij-9012&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;...&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>The following mapping configuration:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;mapping&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;patient_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;doc.fields.patient_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;chw_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;doc.contact._id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;event.temp_c&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;expr&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;((doc.fields.patient_temp_deg_F - 32) * (5.0/9.0)).toFixed(2)&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;event.in_danger&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;expr&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;doc.fields.danger_signs.length &gt;= 3 ? true : undefined&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;optional&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;event.additional_notes&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;path&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;doc.fields.notes&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;optional&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>Would create the following JSON payload to send:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;patient_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;12345&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;chw_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;def-5678&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;event&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;temp_c&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">37.78</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;in_danger&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>This example makes a few points:</p> -<ul> -<li>The report that is being used to generate the outbound push is referenced as <code>doc</code> in both <code>path</code> and <code>expr</code> properties</li> -<li>To define a property that is itself an object, you can make the mapping key a JSON path.</li> -<li>If you define a property as <code>optional</code>, it won&rsquo;t exist at all in the payload if the resulting value is <code>undefined</code>, either because that is the result of executing the <code>expr</code>, or the <code>path</code> doesn&rsquo;t exist. Note that if the <code>event.in_danger</code> expression was instead <code>doc.fields.danger_signs.length &gt;= 3</code> the property <code>in_danger</code> would always exist and would either be <code>true</code> or <code>false</code></li> -</ul> -<h3 id="cron">cron</h3> -<p>An optional cron expression for setting the cron rule in the outbound object. -A cron expression is a string consisting of five fields that describe individual details of the schedule:</p> -<p><code>&lt;minute&gt; &lt;hour&gt; &lt;day-of-month&gt; &lt;month&gt; &lt;day-of-week&gt;</code></p> -<p>The transition verifies if a cron field exists in the configuration. If a cron field is present, it checks if the document is due for push. If it is due, the new document is pushed. If it is not yet due for push, the document is added to the outbound queue. If the cron field isn’t present, then it goes ahead with the previous flow.</p> -<p>Example: you want the system to send outbound pushes based on a cron schedule every day at 1:05.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;cron&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;5 1 * * *&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p><strong>Note:</strong> Schedule Outbound is only available in CHT Core 4.5.0 and above.</p> -<h4 id="other-notes">Other Notes</h4> -<ul> -<li>Your report will be hydrated before being passed to the mapper. This gives you access to the contact and its parents</li> -<li>object paths that may have undefined properties need to be dealt with differently depending on if you are using a <code>path</code> or an <code>expr</code>. Given <code>doc.foo.bar.smang</code> as a path where any of those properties may not exist in the doc:</li> -<li>If you&rsquo;re using <code>path</code> just use the path as is, if any part of the path is <code>undefined</code> the resulting value will safely be <code>undefined</code></li> -<li>However, in <code>expr</code> you <strong>do</strong> need to handle this situation: <code>doc.foo &amp;&amp; doc.foo.bar &amp;&amp; doc.foo.bar.smang</code></li> -<li>If any of your <code>expr</code> expressions throw an exception (for example because you didn&rsquo;t handle potentially <code>undefined</code> properties as noted above) your push will fail</li> -<li>If any of your <code>path</code> declarations result in an <code>undefined</code> value and you have not also declared that property optional your push will fail</li> -</ul> -<h4 id="troubleshooting">Troubleshooting</h4> -<p>By default, Sentinel will log a message each time an outbound request is sent indicating if the request was successful or not. If you are having trouble getting your outbound requests to work, you can <a href="https://docs.communityhealthtoolkit.org/hosting/4.x/logs/#setting-log-level">enable debug logging</a> to get more information about the exact contents of the request/response.</p> -<h2 id="how-outbound-messages-are-sent">How Outbound messages are sent</h2> -<p>Send semantics have changed over the course of developing this feature, and are important to understand for your deployment to be successful.</p> -<h3 id="in-310-and-above">In 3.10 and above</h3> -<ul> -<li>Outbound messages are sent immediately</li> -<li>If there is an error in sending it will be added to a send queue to be retried every 5 minutes. -<ul> -<li>If you update your outbound configuration while messages are present in the send queue, only the messages that match with <em>the new configuration</em> will be retried.</li> -</ul> -</li> -<li>When it does finally send it will include any new changes to the document that have occurred in that time.</li> -<li><strong>Documents can be sent again</strong>, as long as the resulting payload (as defined in the configuration&rsquo;s <code>mapping</code> property) is different from the most recent outbound push performed for this document and configuration.</li> -</ul> -<p>It&rsquo;s important to understand that if your <code>mapping</code> configuration produces a different result every time it&rsquo;s run you may experience Outbound sending the &ldquo;same&rdquo; data many more times than you&rsquo;d expect.</p> -<p>For example, if you want a timestamp in your payload you could configure it like this:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;timestamp&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span><span style="color:#204a87;font-weight:bold">&#34;expr&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;new Date()&#34;</span><span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>Outbound may send your request multiple times even if the user just hit save once. If this is a problem for your deployment, consider using values on the document itself that will not change every time your mapping is executed:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;timestamp&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;doc.timestamp&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="in-39">In 3.9</h3> -<ul> -<li>Outbound messages are sent immediately</li> -<li>If there is an error in sending it will be added to a send queue to be retried every 5 minutes.</li> -<li>When it does finally send it will include any new changes to the document that have occurred in that time.</li> -<li><strong>Documents are only ever sent once for configuration</strong></li> -</ul> -<h3 id="in-34-38">In 3.4-3.8</h3> -<ul> -<li>Outbound messages are added to a send queue that is executed once every 5 minutes or so.</li> -<li>If there is an error in sending it will be kept in the queue to be retried in another 5 minutes.</li> -<li>When it does finally send it will include any new changes to the document that have occurred in that time.</li> -<li>Once the document has successfully sent if the document is changed again it will be sent again, using the same rules as above.</li> -</ul> -<h2 id="inbound">Inbound?</h2> -<p>The outbound feature is used for sending data to an external service. If you are looking to <strong>receive</strong> data from an external service, take a look at <a href="https://docs.communityhealthtoolkit.org/apps/reference/api/#records">the records api</a>.</p>Apps: .patient_reportshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/patient_reports/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/patient_reports/ -<p>The <code>patient_reports</code> key contains the actions to take when reports about people are received.</p> -<h2 id="app_settingsjson-patient_reports"><code>app_settings.json .patient_reports[]</code></h2> -<table> -<thead> -<tr> -<th>property</th> -<th>description</th> -<th>required</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>form</code></td> -<td>Form ID of the form.</td> -<td>yes</td> -</tr> -<tr> -<td><code>name</code></td> -<td>Descriptive name of the form. This is not currently used in the app, but can be a helpful annotation.</td> -<td>no</td> -</tr> -<tr> -<td><code>format</code></td> -<td>Guide of how the form can be used. This is not currently used in the app, but can be a helpful annotation.</td> -<td>no</td> -</tr> -<tr> -<td><code>silence_type</code></td> -<td>A comma separated list of schedules to mute.</td> -<td>no</td> -</tr> -<tr> -<td><code>silence_for</code></td> -<td>Duration from when the report was submitted for which messages should be muted. It is structured as a string with an integer value followed by a space and the time unit. For instance <code>8 weeks</code> or <code>2 days</code>. The units available are <code>seconds</code>, <code>minutes</code>, <code>hours</code>, <code>days</code>, <code>weeks</code>, <code>months</code>, <code>years</code>, and their singular forms as well. When a message is muted all messages belonging to the same group will be muted, even if it falls outside of this time period. See <code>messages[].group</code> in <em>Schedules</em> for related info.</td> -<td>no</td> -</tr> -<tr> -<td><code>fields</code></td> -<td>Descriptive list of form fields. This is not currently used in the app, but can be a helpful annotation.</td> -<td>no</td> -</tr> -<tr> -<td><code>validations</code></td> -<td>A set of validations to perform on incoming reports. More information about validation rules can be found <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/#validations">here</a>.</td> -<td>no</td> -</tr> -<tr> -<td><code>validations.join_responses</code></td> -<td>A boolean specifying whether validation messages should be combined into one message.</td> -<td>no</td> -</tr> -<tr> -<td><code>validations.list[]</code></td> -<td>An array of validation rules a report should pass to be considered valid.</td> -<td>no</td> -</tr> -<tr> -<td><code>validations.list[].property</code></td> -<td>Report field for which this validation rule will be applied.</td> -<td>no</td> -</tr> -<tr> -<td><code>validations.list[].rule</code></td> -<td>Validation condition to be applied to the property field. More information about rules can be found <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/#rules">here</a>.</td> -<td>no</td> -</tr> -<tr> -<td><code>validations.list[].translation_key</code></td> -<td>Translation key for the message reply to be sent if a report fails this rule.</td> -<td>no</td> -</tr> -<tr> -<td><code>messages</code></td> -<td>An array of automated responses to incoming reports.</td> -<td>no</td> -</tr> -<tr> -<td><code>messages[].translation_key</code></td> -<td>Translation key for the message text associated with this event</td> -<td>no</td> -</tr> -<tr> -<td><code>messages[].event_type</code></td> -<td>An event that will trigger sending of this message. Typical values are: <code>report_accepted</code> when the report has been successfully validated, <code>registration_not_found</code> when the shortcode (patient ID or place ID) supplied in the report doesn&rsquo;t match any shortcode issued by Medic. <code>on_mute</code> and <code>on_unmute</code> are used in the context of muting as described <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/transitions/#muting">here</a></td> -<td>no</td> -</tr> -<tr> -<td><code>messages[].recipient</code></td> -<td>Who the message should be sent to. Use <code>reporting_unit</code> for the sender of the report, <code>clinic</code> for clinic contact, and <code>parent</code> for the parent contact. <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/#sms-recipient-resolution">See SMS Recipients</a></td> -<td>no</td> -</tr> -</tbody> -</table> -<h2 id="code-sample">Code sample</h2> -<p>This sample shows a <code>V</code> report clearing schedules that have messages within 8 days of the report being received. The sample also defines the response messages if the report is accepted or if the patient is not found.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;patient_reports&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;V&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Visit (SMS)&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;format&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;V &lt;patientid&gt;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;silence_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;ANC Reminders, ANC Reminders LMP, ANC Reminders LMP from App&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;silence_for&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;8 days&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;fields&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;field_name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;title&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;validations&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;join_responses&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;list&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;property&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;patient_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rule&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;regex(&#39;^[0-9]{5,13}$&#39;)&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;messages.generic.validation.patient_id&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;messages&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;messages.v.report_accepted&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;event_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;report_accepted&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;recipient&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;reporting_unit&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;messages.generic.registration_not_found&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;event_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;registration_not_found&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;recipient&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;reporting_unit&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span></code></pre></div>Apps: .permissionshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-permissions/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-permissions/ -<p>Permissions are defined by the <code>permissions</code> object in the <code>base_settings.json</code> file. The list below illustrates the available system defined permissions. To utilize a permission, you will need to first add the permission as a property of the <code>permissions</code> object, and then associate the permission to user role(s).</p> -<p>Permissions can be assigned to user roles either directly in <code>base_settings.json</code> as an array of user role identifiers, or configured in the App Management app.</p> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-roles/">User roles</a></p> -<h3 id="system-defined-permissions">System defined permissions</h3> -<table> -<thead> -<tr> -<th>Property</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>can_edit</code></td> -<td>This is probably one of the most important permissions in CHT Framework. It allows creating, editing and deleting documents in CouchDB&rsquo;s <code>medic</code> database. This permission overrides any other permission in this list.</td> -</tr> -<tr> -<td><code>can_access_gateway_api</code></td> -<td>Allows access to gateway API</td> -</tr> -<tr> -<td><code>can_aggregate_targets</code></td> -<td>Allows access to Target Aggregates page</td> -</tr> -<tr> -<td><code>can_bulk_delete_reports</code></td> -<td>Allows users to select multiple reports and delete</td> -</tr> -<tr> -<td><code>can_configure</code></td> -<td>Allows update of configuration parameters</td> -</tr> -<tr> -<td><code>can_upgrade</code></td> -<td>Allows upgrades of the CHT Core Framework version via the API or admin interface</td> -</tr> -<tr> -<td><code>can_create_people</code></td> -<td>Allows creation &amp; editing of person contacts</td> -</tr> -<tr> -<td><code>can_create_places</code></td> -<td>Allows creation &amp; editing of place contacts</td> -</tr> -<tr> -<td><code>can_create_records</code></td> -<td>Allows creation of reports</td> -</tr> -<tr> -<td><code>can_create_users</code></td> -<td>Allows creation of user logins</td> -</tr> -<tr> -<td><code>can_delete_contacts</code></td> -<td>Allows deletion of people and places</td> -</tr> -<tr> -<td><code>can_delete_messages</code></td> -<td>Allows deletion of messages</td> -</tr> -<tr> -<td><code>can_delete_reports</code></td> -<td>Allows deletion of reports</td> -</tr> -<tr> -<td><code>can_delete_users</code></td> -<td>Allows deletion of users</td> -</tr> -<tr> -<td><code>can_edit_profile</code></td> -<td>Allows editing of their own user profile</td> -</tr> -<tr> -<td><code>can_edit_verification</code></td> -<td>Allows setting and editing of report verification status. To block the user from updating the existing status, use <code>can_verify_reports</code> instead.</td> -</tr> -<tr> -<td><code>can_export_all</code></td> -<td>Allows export of data including data they do not have access to</td> -</tr> -<tr> -<td><code>can_export_contacts</code></td> -<td>Allows export of contacts</td> -</tr> -<tr> -<td><code>can_export_dhis</code></td> -<td>Allows export of DHIS2 metrics</td> -</tr> -<tr> -<td><code>can_export_feedback</code></td> -<td>Allows export of user feedback</td> -</tr> -<tr> -<td><code>can_export_messages</code></td> -<td>Allows export of reports and messages</td> -</tr> -<tr> -<td><code>can_log_out_on_android</code></td> -<td>Displays logout menu item in hamburger menu for Android users and can be used to log out from the application</td> -</tr> -<tr> -<td><code>can_update_places</code></td> -<td>Allows editing of place documents</td> -</tr> -<tr> -<td><code>can_update_reports</code></td> -<td>Allows editing of report documents</td> -</tr> -<tr> -<td><code>can_update_users</code></td> -<td>Allows editing of user documents</td> -</tr> -<tr> -<td><code>can_verify_reports</code></td> -<td>Allows setting report verification status if no status is currently set. To allow the user to update the existing status, use <code>can_edit_verification</code> instead.</td> -</tr> -<tr> -<td><code>can_view_analytics</code></td> -<td>Allows access to in-app analytics</td> -</tr> -<tr> -<td><code>can_view_analytics_tab</code></td> -<td>Displays analytics tab on the application</td> -</tr> -<tr> -<td><code>can_view_call_action</code></td> -<td>Displays a button to call the selected person</td> -</tr> -<tr> -<td><code>can_view_contacts</code></td> -<td>Allows viewing contacts</td> -</tr> -<tr> -<td><code>can_view_contacts_tab</code></td> -<td>Displays the contacts tab in the application</td> -</tr> -<tr> -<td><code>can_view_last_visited_date</code></td> -<td>Enable display of the date a family was last visited</td> -</tr> -<tr> -<td><code>can_view_message_action</code></td> -<td>Displays a button to send a message to the selected contact</td> -</tr> -<tr> -<td><code>can_view_messages</code></td> -<td>Allows viewing messages</td> -</tr> -<tr> -<td><code>can_view_messages_tab</code></td> -<td>Displays the messages tab in the application</td> -</tr> -<tr> -<td><code>can_view_outgoing_messages</code></td> -<td>Allows viewing outgoing messages when logged in as an administrator</td> -</tr> -<tr> -<td><code>can_view_reports</code></td> -<td>Allows viewing reports</td> -</tr> -<tr> -<td><code>can_view_reports_tab</code></td> -<td>Displays the reports tab in the application</td> -</tr> -<tr> -<td><code>can_view_tasks</code></td> -<td>Allows viewing tasks</td> -</tr> -<tr> -<td><code>can_view_tasks_tab</code></td> -<td>Displays tasks tab in the application</td> -</tr> -<tr> -<td><code>can_view_tasks_group</code></td> -<td>Displays all available tasks within same place after submitting</td> -</tr> -<tr> -<td><code>can_view_uhc_stats</code></td> -<td>Allows users to view UHC metrics</td> -</tr> -<tr> -<td><code>can_view_unallocated_data_records</code></td> -<td>Allows viewing reports that have no associated contact</td> -</tr> -<tr> -<td><code>can_view_users</code></td> -<td>Allows viewing all user accounts</td> -</tr> -<tr> -<td><code>can_write_wealth_quintiles</code></td> -<td>Allows updating contacts with wealth quintile information</td> -</tr> -<tr> -<td><code>can_view_old_filter_and_search</code></td> -<td>Allows users to see the old filter and search in Reports Tab and Contact Tab which is considered deprecated and will be completely removed in a future release. Admin user will always see the new redesigned filter. See <a href="https://docs.communityhealthtoolkit.org/apps/guides/updates/feature-flags/">Feature Flags</a> for more info.</td> -</tr> -<tr> -<td><code>can_view_old_action_bar</code></td> -<td>Allows users to see the old action bar in Message Tab, Reports Tab and Contact Tab which is considered deprecated and will be completely removed in a future release. The More Options menu will be hidden when this permission is enabled. The Admin user will always see the new More Options menu. See <a href="https://docs.communityhealthtoolkit.org/apps/guides/updates/feature-flags/">Feature Flags</a> for more info.</td> -</tr> -<tr> -<td><code>can_default_facility_filter</code></td> -<td>Defaults the Place Filter in Reports tab to the user&rsquo;s associated facility. The user should have a contact associated that belongs to a facility. This feature is not available for Admin and Offline type of users. Use with caution, online users that can access thousands of reports can experience slow performance especially where the network is slow. Added in 4.3.</td> -</tr> -<tr> -<td><code>can_have_multiple_places</code></td> -<td>Allows users to be assigned more than one <code>facility_id</code>. Helps support health systems where offline Supervisors manage CHWs from different geographical areas. Each <code>facility_id</code> must be at the same level in the hierarchy. Added in 4.9.0</td> -</tr> -</tbody> -</table> -<h3 id="code-sample">Code sample</h3> -<p>This sample shows how to define the <code>permissions</code> object in the <code>base_settings.json</code> file. Observe how <code>can_edit</code> permission has been associated to <code>supervisor_role</code> and <code>chw_role</code> user roles.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#4e9a06">&#34;permissions&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;can_edit&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> <span style="color:#4e9a06">&#34;supervisor_role&#34;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#34;chw_role&#34;</span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;can_access_gateway_api&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> <span style="color:#4e9a06">&#34;supervisor_role&#34;</span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;can_aggregate_targets&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> <span style="color:#4e9a06">&#34;supervisor_role&#34;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#34;chw_role&#34;</span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#a40000">...</span> -</span></span><span style="display:flex;"><span> <span style="color:#a40000">...</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div>Apps: .registrationshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/registrations/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/registrations/ -<p>The <code>registrations</code> key contains actions that need to be performed for incoming reports of the specified form.</p> -<h2 id="app_settingsjson-registrations"><code>app_settings.json .registrations[]</code></h2> -<table> -<thead> -<tr> -<th>property</th> -<th>description</th> -<th>required</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>form</code></td> -<td>Form ID that should trigger the schedule.</td> -<td>yes</td> -</tr> -<tr> -<td><code>events</code></td> -<td>An array of event object definitions of what should happen when this form is received.</td> -<td>yes</td> -</tr> -<tr> -<td><code>event[].name</code></td> -<td>Name of the event that has happened. The only supported event is <code>on_create</code> which happens when a form is received.</td> -<td>yes</td> -</tr> -<tr> -<td><code>event[].trigger</code></td> -<td>What should happen after the named event. <code>assign_schedule</code> will assign the schedule named in <code>params</code> to this report. Similarly <code>clear_schedule</code> will permanently clear all messages for a patient or place that are part of schedules listed in the <code>params</code> field. The full set of trigger configuration directives are described <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/transitions/#triggers">here</a>.</td> -<td>yes</td> -</tr> -<tr> -<td><code>event[].params</code></td> -<td>Any useful information for the event. In our case, it holds the name of the schedule to be triggered.</td> -<td>no</td> -</tr> -<tr> -<td><code>event[].bool_expr</code></td> -<td>A JavaScript expression that will be cast to boolean to qualify execution of the event. Leaving blank will default to always true. CouchDB document fields can be accessed using <code>doc.key.subkey</code>. Regular expressions can be tested using <code>pattern.test(value)</code> e.g. /^[0-9]+$/.test(doc.fields.last_menstrual_period). In our example above, we&rsquo;re making sure the form has an LMP date.</td> -<td>no</td> -</tr> -<tr> -<td><code>validations</code></td> -<td>A set of validations to perform on incoming reports. More information about validation rules can be found <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/#validations">here</a>.</td> -<td>no</td> -</tr> -<tr> -<td><code>validations.join_responses</code></td> -<td>A boolean specifying whether validation messages should be combined into one message.</td> -<td>no</td> -</tr> -<tr> -<td><code>validations.list[]</code></td> -<td>An array of validation rules a report should pass to be considered valid.</td> -<td>no</td> -</tr> -<tr> -<td><code>validations.list[].property</code></td> -<td>Report field for which this validation rule will be applied.</td> -<td>no</td> -</tr> -<tr> -<td><code>validations.list[].rule</code></td> -<td>Validation condition to be applied to the property field. More information about rules can be found <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/#rules">here</a>.</td> -<td>no</td> -</tr> -<tr> -<td><code>validations.list[].translation_key</code></td> -<td>Translation key for the message reply to be sent if a report fails this rule.</td> -<td>no</td> -</tr> -<tr> -<td><code>messages</code></td> -<td>An array of automated responses to incoming reports.</td> -<td>no</td> -</tr> -<tr> -<td><code>messages[].translation_key</code></td> -<td>Translation key for the message text associated with this event.</td> -<td>no</td> -</tr> -<tr> -<td><code>messages[].event_type</code></td> -<td>An event that will trigger sending of this message. Typical values are: <code>report_accepted</code> when the report has been successfully validated and <code>registration_not_found</code> when the shortcode (patient ID or place ID) supplied in the report doesn&rsquo;t match any shortcode issued by Medic.</td> -<td>no</td> -</tr> -<tr> -<td><code>messages[].recipient</code></td> -<td>Who the message should be sent to. Use <code>reporting_unit</code> for the sender of the report, <code>clinic</code> for clinic contact, and <code>parent</code> for the parent contact. <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/#sms-recipient-resolution">See SMS Recipients</a></td> -<td>no</td> -</tr> -</tbody> -</table> -<h2 id="code-sample">Code sample</h2> -<p>This sample shows how a schedule would be triggered by a <code>pregnancy</code> report if the <code>last_menstrual_period</code> value is set.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#4e9a06">&#34;registrations&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;pregnancy&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;events&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;on_create&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;trigger&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;assign_schedule&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;params&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;ANC Visit Reminders&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;bool_expr&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;doc.fields.last_menstrual_period&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;validations&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;messages&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">]</span> -</span></span></code></pre></div>Apps: .remindershttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/reminders/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/reminders/ -<p>Configure SMS reminders to notify primary contacts to submit reports for their places.</p> -<h2 id="app_settingsjs-reminders"><code>app_settings.js .reminders[]</code></h2> -<table> -<thead> -<tr> -<th>Property</th> -<th>Type</th> -<th>Description</th> -<th>Required</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>form</code></td> -<td><code>string</code></td> -<td>If a report with this ID is submitted for this place then the SMS will not be sent.</td> -<td>Yes</td> -</tr> -<tr> -<td><code>translation_key</code></td> -<td><code>string</code></td> -<td>The translation key to use to look up the SMS message content.</td> -<td>Yes</td> -</tr> -<tr> -<td><code>message</code></td> -<td><code>array</code> or <code>string</code></td> -<td><em>Deprecated</em>. The SMS content. Use translation_key instead.</td> -<td>No</td> -</tr> -<tr> -<td><code>text_expression</code></td> -<td><code>string</code></td> -<td>The <a href="http://bunkat.github.io/later/parsers.html#text">later text expression</a> to use to set the frequency of this reminder.</td> -<td>Yes (unless cron is defined)</td> -</tr> -<tr> -<td><code>cron</code></td> -<td><code>string</code></td> -<td>The <a href="https://en.wikipedia.org/wiki/Cron">cron expression</a> to use to set the frequency of this reminder</td> -<td>Yes (unless text_expression is defined)</td> -</tr> -<tr> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td><code>contact_types</code></td> -<td><code>array</code></td> -<td>All contact type IDs that should receive the SMS. Defaults to the lowest level places. <em>Added in 3.10.0</em></td> -<td>No</td> -</tr> -</tbody> -</table> -<h2 id="code-samples">Code samples</h2> -<h3 id="app_settingsjs"><code>app_settings.js</code></h3> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#4e9a06">&#34;reminders&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;stock&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;sms.reminder.stock&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;text_expression&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;on the first day of the week&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;contact_types&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> <span style="color:#4e9a06">&#34;health_center&#34;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#34;hospital&#34;</span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">]</span><span style="color:#a40000">,</span> -</span></span></code></pre></div>Apps: .replication_depthhttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/replication_depth/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/replication_depth/ -<p>Replication depth is defined under <code>replication_depth</code> as an array of objects. It grants the ability to limit document replication depending on user roles.</p> -<h2 id="app_settingsjson-replication_depth"><code>app_settings.json .replication_depth</code></h2> -<table> -<thead> -<tr> -<th>property</th> -<th>description</th> -<th>required</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>role</code></td> -<td>The configured user role the depth applies to.</td> -<td>yes</td> -</tr> -<tr> -<td><code>depth</code></td> -<td>The replication depth value. Must be a positive integer or 0.</td> -<td>yes</td> -</tr> -<tr> -<td><code>report_depth</code></td> -<td><strong>As of 3.10</strong>. Replication depth applied to reports submitted by other users</td> -<td>no</td> -</tr> -</tbody> -</table> -<h5 id="code-sample">Code sample:</h5> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;replication_depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">&#34;role&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;district_manager&#34;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">&#34;depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">&#34;report_depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">&#34;role&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;national_manager&#34;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">&#34;depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">2</span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div>Apps: .replications [deprecated]https://docs.communityhealthtoolkit.org/apps/reference/app-settings/replications/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/replications/ -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Deprecated</h4> -The <code>replications</code> field is only available in versions 3.5.0 to 3.9.0. As of 3.10.0 this field is ignored, and replication happens nightly for user meta databases to a central meta data database. -</div> -<p>Replications are defined under the <code>app_settings.replications</code> key as an array of replication objects. The definition takes the typical form below:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#4e9a06">&#34;replications&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;fromSuffix&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;user-[^\\-]+-meta&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;toSuffix&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;users-meta&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;text_expression&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;cron&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;0 2 * * *&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">]</span> -</span></span></code></pre></div><table> -<thead> -<tr> -<th>property</th> -<th>description</th> -<th>required</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>fromSuffix</code></td> -<td>The suffix of the source table(s). Regular expression may be used.</td> -<td>yes</td> -</tr> -<tr> -<td><code>toSuffix</code></td> -<td>The suffix of the target table.</td> -<td>yes</td> -</tr> -<tr> -<td><code>text_expression</code></td> -<td>Any valid text expression. For more information, see <a href="https://bunkat.github.io/later/parsers.html#text">LaterJS</a></td> -<td>no if <code>cron</code> provided</td> -</tr> -<tr> -<td><code>cron</code></td> -<td>Any valid Cron expression. For more information, see <a href="https://bunkat.github.io/later/parsers.html#cron">LaterJS</a></td> -<td>no if <code>text_expression</code> provided</td> -</tr> -</tbody> -</table>Apps: .roleshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-roles/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-roles/ -<p>Each user is assigned one of the defined roles. Roles can be defined using the App Management app, which is represented by the <code>roles</code> object of the <code>app-settings.json</code> file. Each role is defined by an identifier as the key, and an object with the following properties:</p> -<h3 id="app_settingsjson-roles"><code>app_settings.json .roles{}</code></h3> -<table> -<thead> -<tr> -<th>Property</th> -<th>Description</th> -<th>Required</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>name</code></td> -<td>The translation key for this role</td> -<td>Yes</td> -</tr> -<tr> -<td><code>offline</code></td> -<td>Determines if user will be an online or offline user. Set to <code>false</code> for users to be &ldquo;online&rdquo; users.</td> -<td>No, default <code>true</code></td> -</tr> -</tbody> -</table>Apps: .scheduleshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/schedules/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/schedules/ -<p>Schedules are defined in either the <code>base_settings.json</code> or the <code>app_settings/schedules.json</code> file and compiled in to the <code>app_settings.json</code> file with the <code>compile-app-settings</code> action in the <code>cht-conf</code> tool.</p> -<p>The <code>schedules</code> key contains an array of schedule objects, each representing the messages to send based on a registration.</p> -<h2 id="app_settingsjson-schedules"><code>app_settings.json .schedules[]</code></h2> -<table> -<thead> -<tr> -<th>property</th> -<th>description</th> -<th>required</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>name</code></td> -<td>A unique string label that is used to identify the schedule. Spaces are allowed.</td> -<td>yes</td> -</tr> -<tr> -<td><code>summary</code></td> -<td>Short description of the of the schedule.</td> -<td>no</td> -</tr> -<tr> -<td><code>description</code></td> -<td>A narrative for the schedule.</td> -<td>no</td> -</tr> -<tr> -<td><code>start_from</code></td> -<td>The base date from which the <code>messages[].offset</code> is added to determine when to send individual messages. You could specify any property on the report that contains a date value. Starting from 4.5.0, an array of property names is also supported; in this case, the first defined field is used. The default is <code>reported_date</code>, which is when the report was submitted.</td> -<td>no</td> -</tr> -<tr> -<td><code>start_mid_group</code></td> -<td>Whether or not a schedule can start mid-group. If not present, the schedule will not start mid-group. In other terms, the default value is <code>false</code></td> -<td>no</td> -</tr> -<tr> -<td><code>messages</code></td> -<td>Array of objects, each containing a message to send out and its properties.</td> -<td>yes</td> -</tr> -<tr> -<td><code>messages[].translation_key</code></td> -<td>The translation key of the message to send out. Available in 2.15+.</td> -<td>yes</td> -</tr> -<tr> -<td><code>messages[].messages</code></td> -<td>Array of message objects, each with <code>content</code> and <code>locale</code> properties. From 2.15 on use <code>translation_key</code> instead.</td> -<td>no</td> -</tr> -<tr> -<td><code>messages[].group</code></td> -<td>Integer identifier to group messages that belong together so that they can be cleared together as a group by future reports. For instance a series of messages announcing a visit, and following up for a missed visit could be grouped together and cleared by a single visit report.</td> -<td>yes</td> -</tr> -<tr> -<td><code>messages[].offset</code></td> -<td>Time interval from the <code>start_from</code> date for when the message should be sent. It is structured as a string with an integer value followed by a space and the time unit. For instance <code>8 weeks</code> or <code>2 days</code>. The units available are <code>seconds</code>, <code>minutes</code>, <code>hours</code>, <code>days</code>, <code>weeks</code>, <code>months</code>, <code>years</code>, and their singular forms as well. Note that although you can specify <code>seconds</code>, the accuracy of the sending time will be determined by delays in the processing the message on the server and on the gateway.</td> -<td>yes</td> -</tr> -<tr> -<td><code>messages[].send_day</code></td> -<td>String value of the day of the week on which the message should be sent. For instance, to send a message at the beginning of the week setting it to <code>&quot;Monday&quot;</code> will make sure the message goes out on the closest Monday <em>after</em> the <code>start_date</code> + <code>offset</code>.</td> -<td>no</td> -</tr> -<tr> -<td><code>messages[].send_time</code></td> -<td>Time of day that the message should be sent in 24 hour format.</td> -<td>no</td> -</tr> -<tr> -<td><code>messages[].recipient</code></td> -<td>Recipient of the message. It can be set to <code>reporting_unit</code> (sender of the form), <code>clinic</code> (clinic that the sender of the form is attached to), <code>parent</code> (parent of the sender of the form), or a specific phone number. <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/#sms-recipient-resolution">See SMS Recipients</a></td> -<td>no</td> -</tr> -</tbody> -</table> -<h2 id="code-sample">Code Sample</h2> -<p>This sample shows a schedule with a single message, which will be sent on Monday 9am 4 weeks after the LMP date on the report that triggers this schedule:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;schedules&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;ANC Visit Reminders&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;summary&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;description&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;start_from&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;lmp_date&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;start_mid_group&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;messages&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;messages.schedule.registration.followup_anc_pnc&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;group&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;offset&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;4 weeks&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;send_day&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;monday&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;send_time&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;09:00&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;recipient&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;reporting_unit&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">]</span> -</span></span></code></pre></div><p>The following sample schedules a message for 270 days from <code>lmp_date</code>. If <code>lmp_date</code> doesn&rsquo;t exist on report, it will schedule a message for 270 days from <code>fields.lmp_date</code>. If neither field exists, it will not create a schedule. Using an array for <code>start_from</code> is supported from CHT 4.5.0+.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;schedules&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Delivery Reminder&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;summary&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;description&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;start_from&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#34;lmp_date&#34;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#34;fields.lmp_date&#34;</span><span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;start_mid_group&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;messages&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;messages.schedule.deliery&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;group&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;offset&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;270 days&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;recipient&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;reporting_unit&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">]</span> -</span></span></code></pre></div>Apps: .smshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/sms/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/sms/ -<p>SMS settings are defined under the <code>sms</code> key, as an object supporting the following properties:</p> -<h2 id="app_settingsjson-sms"><code>app_settings.json .sms</code></h2> -<table> -<thead> -<tr> -<th>property</th> -<th>default</th> -<th>description</th> -</tr> -</thead> -<tbody> -<tr> -<td>outgoing_service</td> -<td>medic-gateway</td> -<td>Defines the service to use to send SMS messages. Currently supports &ldquo;medic-gateway&rdquo;, &ldquo;africas-talking&rdquo; or &ldquo;rapidpro&rdquo;. For more information read the documentation on <a href="https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/africas-talking/">&ldquo;africas-talking&rdquo; configuration</a> and <a href="https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/rapidpro/">&ldquo;rapidpro&rdquo; configuration</a>.</td> -</tr> -<tr> -<td>duplicate_limit</td> -<td>5</td> -<td>The number of identical sms message allowed to be sent to the same recipient.</td> -</tr> -</tbody> -</table> -<h2 id="code-sample">Code sample</h2> -<p>The definition takes the typical form below:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#4e9a06">&#34;sms&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;outgoing_service&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;medic-gateway&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;duplicate_limit&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h4 id="duplicate-sms-messages-handling">Duplicate SMS messages handling</h4> -<p>Every time a service (API or Sentinel) creates an SMS, we cache the recipient, and the message content, along with the current timestamp. -When more than <code>duplicate_limit</code> messages have been created for the same pair of recipient+content, within the cache time limit, we mark the new message with a &ldquo;duplicate&rdquo; status. Such messages are never sent. -The cache is cleared 30 minutes after the last SMS message for a specific pair was generated.</p>Apps: .token_loginhttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/token_login/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/token_login/ -<p>Login via SMS settings are defined under the <code>token_login</code> key, as an object supporting the following properties:</p> -<h2 id="app_settingsjson-token_login"><code>app_settings.json .token_login</code></h2> -<table> -<thead> -<tr> -<th>property</th> -<th>type</th> -<th>required</th> -<th>description</th> -</tr> -</thead> -<tbody> -<tr> -<td>enabled</td> -<td>Boolean</td> -<td>yes</td> -<td>Enables or disables token_login deployment-wide. When this is false, users can&rsquo;t be updated to use token_login and any requests to login with a token link will fail.</td> -</tr> -<tr> -<td>translation_key</td> -<td>String</td> -<td>yes</td> -<td>Translation key for the information (helper) sms message that the user receives, along with their token-login link</td> -</tr> -</tbody> -</table> -<h2 id="code-sample">Code sample</h2> -<p>The definition takes the typical form below:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#4e9a06">&#34;token_login&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;enabled&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;sms.token.login.help&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div>Apps: .transitionshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/transitions/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/transitions/ -<p>When sentinel detects a document has changed it runs transitions against the doc. These transitions can be used to generate a short form patient id or assign a report to a facility.</p> -<h2 id="configuration">Configuration</h2> -<p>By default all transitions are disabled. They can be enabled by configuring the <code>transitions</code> property to have a key with the transitions name and a <code>truthy</code> value. -As of version 3.12.0 some transitions will partially run on the client for offline users. To opt out from client-side transitions, add a <code>&quot;client_side: false&quot;</code> property to the transition configuration.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;transitions&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;a&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;b&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;c&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">&#34;disable&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;d&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">&#34;disable&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;e&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">&#34;client_side&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>In this example the <code>d</code> transition will not be applied, <code>a</code>, <code>b</code> <code>c</code> will be applied on the server and on the client, while <code>e</code> will only be applied on the server.</p> -<h2 id="available-transitions">Available transitions</h2> -<p>The following transitions are available and executed in order.</p> -<table> -<thead> -<tr> -<th>Key</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>maintain_info_document</td> -<td>Records metadata about the document such as when it was replicated. Enabled by default.</td> -</tr> -<tr> -<td><a href="#update_clinics">update_clinics</a></td> -<td>Adds a contact&rsquo;s info to a new data record. This is used to attribute an incoming SMS message or report to the appropriate contact. The <code>rc_code</code> value on the contact is used to match to the value of the form field set as the <code>facility_reference</code> in the <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/forms/#app_settingsjson-forms">JSON form definition</a>. This matching is useful when reports are sent on behalf of a facility by unknown or various phone numbers. If <code>facility_reference</code> is not set for a form, the contact match is attempted using the sender&rsquo;s phone number. If a form is not public and a match is not found then the <code>sys.facility_not_found</code> error is raised. A message will be sent out whenever this error is raised.</td> -</tr> -<tr> -<td><a href="#registration">registration</a></td> -<td>For registering a patient or place to a schedule. Performs some validation and creates the patient document if the patient does not already exist. Can create places (as of 3.8.x). Can assign schedules to places (as of 3.11.x)</td> -</tr> -<tr> -<td><a href="#accept-patient-reports">accept_patient_reports</a></td> -<td>Validates reports about a patient or place and silences relevant reminders.</td> -</tr> -<tr> -<td><a href="#accept-case-reports">accept_case_reports</a></td> -<td>Validates reports about a case, assigns the associated place_uuid, and silences relevant reminders. Available since 3.9.0</td> -</tr> -<tr> -<td><a href="#generate-shortcode-on-contacts">generate_shortcode_on_contacts</a></td> -<td>Automatically generates the <code>patient_id</code> on all person documents and the <code>place_id</code> on all place documents. Available since 3.8.x.</td> -</tr> -<tr> -<td><a href="#generate-patient-id-on-people">generate_patient_id_on_people</a></td> -<td><strong>Deprecated in 3.8.x</strong> Automatically generates the <code>patient_id</code> on all person documents. As of 3.8.x, also generates the <code>place_id</code> on all place documents and is an alias for <code>generate_shortcode_on_contacts</code>.</td> -</tr> -<tr> -<td>default_responses</td> -<td>Responds to the message with a confirmation or validation error.</td> -</tr> -<tr> -<td>update_sent_by</td> -<td>Sets the sent_by field of the report based on the sender&rsquo;s phone number.</td> -</tr> -<tr> -<td>update_sent_forms</td> -<td><strong>Deprecated in 3.7.x</strong> Update sent_forms property on facilities so we can setup reminders for specific forms. <em>As of 3.7.x, reminders no longer require this transition</em></td> -</tr> -<tr> -<td><a href="#death_reporting">death_reporting</a></td> -<td>Updates the deceased status of patients.</td> -</tr> -<tr> -<td>conditional_alerts</td> -<td>Executes the configured condition and sends an alert if the condition is met.</td> -</tr> -<tr> -<td><a href="#multi_report_alerts">multi_report_alerts</a></td> -<td>Similar to conditional_alerts, with more flexible configuration, including using different form types for the same alert.</td> -</tr> -<tr> -<td><a href="#update_notifications">update_notifications</a></td> -<td><strong>Deprecated in 3.2.x</strong> Mutes or unmutes scheduled messages based on configuration.</td> -</tr> -<tr> -<td>update_scheduled_reports</td> -<td>If a report has a month/week/week_number, year and clinic then look for duplicates and update those instead.</td> -</tr> -<tr> -<td>resolve_pending</td> -<td>Sets the state of pending messages to sent. It is useful during builds where we don&rsquo;t want any outgoing messages queued for sending.</td> -</tr> -<tr> -<td><a href="#muting">muting</a></td> -<td>Implements muting/unmuting actions of people and places. Available since 3.2.x. Is partially applied on the client, as of 3.12.0.</td> -</tr> -<tr> -<td><a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/outbound/">mark_for_outbound</a></td> -<td>Enables outbound pushes. Available since 3.5.x</td> -</tr> -<tr> -<td><a href="#self_report">self_report</a></td> -<td>Maps patient to sender. Available since 3.9.x</td> -</tr> -<tr> -<td><a href="#create_user_for_contacts">create_user_for_contacts</a></td> -<td>Allows for automatically creating or replacing users based on data from their associated contact. Available since 4.1.x</td> -</tr> -</tbody> -</table> -<h2 id="transition-configuration-guide">Transition Configuration Guide</h2> -<p>Guides for how to setup specific transitions.</p> -<h3 id="multi_report_alerts">multi_report_alerts</h3> -<p>Send alert messages by SMS when specific conditions are received through reports. More flexible than simple Alerts.</p> -<p>Example: send SMS to the district manager when 2 CHWs within the same district report cholera or diarrhea symptoms within the last week.</p> -<p>Understanding the different types of reports used in the configuration:</p> -<pre tabindex="0"><code> previous suspected_cholera alert was sent -| -| latest_report comes in, suspected_cholera alert is sent -| | -v v ----[---*-o---*--*--o-o---*]-------&gt; time -1 0 -</code></pre><p><code>[]</code> : time window</p> -<p><code>*</code> and <code>o</code> : <code>reports</code> : any report that came in to the server.</p> -<p><code>*</code> : <code>counted_reports</code> : reports that came in that passed the <code>is_report_counted</code> filter function.</p> -<p><code>0</code>, <code>1</code> : <code>new_reports</code> : <code>counted_reports</code> that came in since the previous alert was sent. They haven&rsquo;t been messaged about yet.</p> -<h4 id="configuration-1">Configuration</h4> -<pre tabindex="0"><code>&#34;multi_report_alerts&#34; : [ -{ -&#34;name&#34;: &#34;suspected_cholera&#34;, -&#34;is_report_counted&#34;: &#34;function(report, latest_report) { return latest_report.contact.parent.parent._id === report.contact.parent.parent._id; }&#34;, -&#34;num_reports_threshold&#34;: 2, -&#34;message&#34;: &#34;{{num_counted_reports}} patients with {{alert_name}} in {{time_window_in_days}} days reported at {{new_reports.0.contact.parent.name}}. New reports from: {{new_reports.0.contact.name}}, {{new_reports.1.contact.name}}, {{new_reports.2.contact.name}}.&#34;, -&#34;recipients&#34;: [ -&#34;+123456&#34;, -&#34;new_report.contact.phone&#34;, // sender of each report in new_reports -&#34;new_report.contact.parent.parent.contact.phone&#34;, // contact person for the parent place of the sender of each report in new_reports. -// If it&#39;s the same for several reports, only one message will be sent (recipient phone numbers are deduplicated before generating messages). -], -&#34;time_window_in_days&#34;: 7, -&#34;forms&#34;: [&#34;C&#34;, &#34;D&#34;] // Only Cholera and Diarrhea forms. -} -] -</code></pre><p>Note that we are using Mustache templates for our message templates (anything with <code>{{}}</code>), and they use a <code>.</code> notation to access items in an array (e.g. <code>new_reports.1</code>) rather than a <code>[]</code> notation as in conventional javascript (<code>new_reports[1]</code>).</p> -<p>For performance reasons the <code>num_reports_threshold</code> cannot exceed 100.</p> -<h3 id="death_reporting">death_reporting</h3> -<p>Updates patient documents with a <code>date_of_death</code> field which updates how the patient is displayed in the UI.</p> -<h4 id="configuration-2">Configuration</h4> -<p>Configuration is stored in the <code>death_reporting</code> field of the settings.</p> -<table> -<thead> -<tr> -<th>Property</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>mark_deceased_forms</code></td> -<td>An array of form codes which will cause patients to be recorded as deceased.</td> -</tr> -<tr> -<td><code>undo_deceased_forms</code></td> -<td>An array of form codes which will remove the deceased date from the patient. Optional.</td> -</tr> -<tr> -<td><code>date_field</code></td> -<td>The path to the field in the report document which has the date the patient died. Optional: if not configured it defaults to the reported_date of the report.</td> -</tr> -</tbody> -</table> -<h5 id="example">Example</h5> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#4e9a06">&#34;death_reporting&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;mark_deceased_forms&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> <span style="color:#4e9a06">&#34;death&#34;</span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;undo_deceased_forms&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> <span style="color:#4e9a06">&#34;undo-death&#34;</span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;date_field&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;fields.death_date&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="registration">Registration</h3> -<p>Configuration is held at <code>app_settings.registrations</code>, as a list of objects connecting forms to validations, events and messages.</p> -<h4 id="events">Events</h4> -<p>Lists different events.</p> -<h5 id="on_create"><code>on_create</code></h5> -<p>This is the only supported event.</p> -<h4 id="triggers">Triggers</h4> -<h5 id="add_patient"><code>add_patient</code></h5> -<p>Sets the <code>patient_id</code> on the root of the registration document and creates the person doc if required. Can be configured to either use a provided ID or generate a new unique one.</p> -<h6 id="external-patient-id">External Patient ID</h6> -<p>If you are providing the patient ID instead of having Sentinel generate you one, name the field in a <code>patient_id_field</code> key in <code>&quot;params&quot;</code>:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;on_create&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;trigger&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;add_patient&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;params&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;{\&#34;patient_id_field\&#34;: \&#34;external_id\&#34;}&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;bool_expr&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>In this example the provided ID would be in <code>fields.external_id</code> on the registration document. This field <strong>must not</strong> be called <code>patient_id</code>.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -<p>The JSON passed in <code>&quot;params&quot;</code> should be a string. Support for raw JSON as shown below exists, but is in beta and may not always work correctly in all situations, because kanso.json does not support it:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;params&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span><span style="color:#204a87;font-weight:bold">&#34;patient_id_field&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;external_id&#34;</span><span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div> -</div> -<h6 id="alternative-name-location">Alternative Name Location</h6> -<p>To provide an alternative location for the patient name, either provide a <code>patient_name_field</code> in <code>&quot;params&quot;</code> or provide it directly into the <code>&quot;params&quot;</code> field as a String:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;params&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;{\&#34;patient_name_field\&#34;: \&#34;full_name\&#34;}&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;params&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;full_name&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>The first format is required if you wish to also provide other params:</p> -<h6 id="contact-type">Contact Type</h6> -<p>If you have changed from the default contact hierarchy you will need to specify which type of contact the registration should create.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;params&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;{ \&#34;contact_type\&#34;: \&#34;patient\&#34; }&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h6 id="specific-parent-as-of-380">Specific Parent <em>as of 3.8.0</em></h6> -<p>By default, the newly created person will have the same parent as the report submitter. -A different parent may be selected by providing a location for the parent id. This field should -contain the <code>place_id</code> of the place in question. -If the selected parent is invalid - does not exist or does not respect the configured hierarchy</p> -<ul> -<li>the report is rejected as invalid and the person document is not created. As such -, <code>report_accepted</code> event should check if the report has a <code>patient</code> property (or similar).</li> -</ul> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;params&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;{ \&#34;parent_id\&#34;: \&#34;parent_id\&#34; }&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h6 id="events-1">Events</h6> -<ul> -<li><code>parent_field_not_provided</code> - triggered when the report does not have the required parent_id field</li> -<li><code>parent_invalid</code> - triggered when selected parent is invalid (parent document is found and -either does not have a configured type or its type is not configured to be a parent to the -person type to be created)</li> -<li><code>parent_not_found</code> - triggered when selected parent is not found</li> -</ul> -<p>The selected parent (if found) can be accessed by using the <code>parent</code> path in error messages: -<code>Cannot create a person type &quot;patient&quot; under parent {{parent.place_id}}({{parent.contact_type}})</code></p> -<h5 id="add_patient_id"><code>add_patient_id</code></h5> -<p><strong>Deprecated in favour of <code>add_patient</code>.</strong> Previously this only added a <code>patient_id</code> to the root of the registration form. This functionality has been merged into <code>add_patient</code>. Now, using this event will result in the same functionality as described in <code>add_patient</code> above.</p> -<h5 id="add_expected_date"><code>add_expected_date</code></h5> -<h5 id="add_birth_date"><code>add_birth_date</code></h5> -<h5 id="assign_schedule"><code>assign_schedule</code></h5> -<h5 id="clear_schedule"><code>clear_schedule</code></h5> -<h5 id="add_place-as-of-380"><code>add_place</code> <em>as of 3.8.0</em></h5> -<p>Sets the <code>place_id</code> on the root of the registration document and creates the place doc with the -provided type.</p> -<p>By default, the created place would have the same parent as the submitter. If such a combination is -invalid - for example a contact under a &ldquo;clinic&rdquo; attempts to create a new &ldquo;health_center&rdquo; - the -report will be rejected as errored and the place document will not be created. As such, -<code>report_accepted</code> event should check if the report has a <code>place</code> property (or similar).</p> -<p>The created place does not have a primary contact. -The created place can be accessed by the <code>place</code> path in messages: -<code>Place {{place.name}}({{place.place_id}}) added to {{place.parent.name}}({{place.parent.place_id}})</code></p> -<h6 id="contact-type-1">Contact Type</h6> -<p>Specifying the contact type is required, even if not using configurable hierarchies. -The selected contact type must be a configured place type.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;params&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;{ \&#34;contact_type\&#34;: \&#34;clinic\&#34; }&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h6 id="specific-parent">Specific Parent</h6> -<p>By default, the newly created place will have the same parent as the report submitter. -A different parent may be selected by providing a location for the parent id. This field should -contain the <code>place_id</code> of the place in question. -If the selected parent is invalid - does not exist or is not an acceptable parent to the -selected type in the configured hierarchy - the report will be rejected as errored.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;params&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;{ \&#34;parent_id\&#34;: \&#34;parent_id\&#34; }&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h6 id="events-2">Events</h6> -<ul> -<li><code>parent_field_not_provided</code> - triggered when the report does not have the required parent_id field</li> -<li><code>parent_invalid</code> - triggered when selected parent is invalid (parent document is found and -either does not have a configured type or its type is not configured to be a parent to the -place type to be created)</li> -<li><code>parent_not_found</code> - triggered when selected parent is not found</li> -</ul> -<p>The selected parent (if found) can be accessed by using the <code>parent</code> path in error messages: -<code>'Cannot create a place type &quot;health_center&quot; under parent {{parent.place_id}}({{parent.contact_type}})'</code></p> -<h6 id="alternative-name-location-1">Alternative Name Location</h6> -<p>The created place&rsquo;s name is provided in the <code>place_name</code> field by default. -To provide an alternative location for the place name, provide a <code>place_name_field</code> in -<code>&quot;params&quot;</code>:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;params&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;{\&#34;place_name_field\&#34;: \&#34;clinic_name\&#34;}&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h5 id="add_case-as-of-390"><code>add_case</code> <em>as of 3.9.0</em></h5> -<p>Sets the <code>case_id</code> on the root of the registration document.</p> -<h3 id="generate-shortcode-on-contacts">Generate Shortcode on Contacts</h3> -<p>No custom configuration for <code>generate_shortcode_on_contacts</code>.</p> -<h3 id="generate-patient-id-on-people">Generate Patient ID On People</h3> -<p><strong>Deprecated since 3.8.x</strong> in favor of <code>generate_shortcode_on_contacts</code></p> -<p>No custom configuration for <code>generate_patient_id_on_people</code>.</p> -<h3 id="update_notifications">update_notifications</h3> -<p><strong>Deprecated in favor of <a href="#muting">Muting</a></strong></p> -<h4 id="configuration-3">Configuration</h4> -<pre tabindex="0"><code>&#34;notifications&#34;: { -&#34;off_form&#34;: &#34;OFF&#34;, -&#34;on_form&#34;: &#34;ON&#34;, -&#34;validations&#34;: { -&#34;join_responses&#34;: true, -&#34;list&#34;: [] -}, -&#34;messages&#34;: [ -{ -&#34;translation_key&#34;: &#34;&#34;, -&#34;event_type&#34;: &#34;on_mute&#34;, -&#34;recipient&#34;: &#34;reporting_unit&#34; -}, -{ -&#34;translation_key&#34;: &#34;&#34;, -&#34;event_type&#34;: &#34;on_unmute&#34;, -&#34;recipient&#34;: &#34;reporting_unit&#34; -}, -{ -&#34;translation_key&#34;: &#34;&#34;, -&#34;event_type&#34;: &#34;patient_not_found&#34;, -&#34;recipient&#34;: &#34;reporting_unit&#34; -} -] -} -</code></pre><h3 id="muting">Muting</h3> -<p>Implements muting/unmuting of persons and places. Supports multiple forms for each action, for webapp and sms workflows.</p> -<h5 id="muting-action">Muting action</h5> -<p>As of 3.12.0, client-side muting only runs on new reports or new contacts, before they are saved in the local database:</p> -<ul> -<li>updates the target contact and all its descendants<sup>[10]</sup>, setting the <code>muted</code> property equal to the device&rsquo;s current <code>date</code> in ISO format<sup>[8]</sup>.</li> -<li>adds/updates the <code>muting_history</code><sup>[11]</sup> property on every updated contact, to keep track of all the updates that have been processed client-side, as well as the last known server-side state of the contact and sets the <code>last_update</code> property to <code>client_side</code></li> -<li>updates the report doc to add a <code>client_side_transitions</code> property to track which transitions have run client-side</li> -</ul> -<p>Server-side:</p> -<ul> -<li>updates the target contact and all its descendants<sup>[1]</sup>, setting the <code>muted</code> property equal to the current <code>date</code> in ISO format<sup>[2]</sup>. If the contact was already muted by a client, the <code>muted</code> date will be overwritten. The client-side <code>muting_history</code> will have a copy of the client-side muting date.</li> -<li>adds a <code>muting_history</code> entry to Sentinel <code>info</code> docs for every updated contact<sup>[7]</sup></li> -<li>updates all connected registrations<sup>[3]</sup>, changing the state of all unsent<sup>[4]</sup> <code>scheduled_tasks</code> to <code>muted</code></li> -<li>as of 3.12.0, updates the contact&rsquo;s client-side <code>muting_history</code> to set the <code>last_update</code> property to <code>server_side</code> and update the <code>server_side</code> section with the current date and muted state.</li> -<li>as of 3.12.0, if the report was processed client-side, all &ldquo;following&rdquo; muting/unmuting events that have affected the same contacts will be replayed. This means the transition <em>could</em> end up running multiple times over the same report<sup>[9]</sup>.</li> -</ul> -<h5 id="unmuting-action">Unmuting action:</h5> -<p>As of 3.12.0, client-side unmuting only runs on new reports before they are saved in the local database:</p> -<ul> -<li>updates the target contact&rsquo;s topmost muted ancestor<sup>[10][5]</sup> and all its descendants, removing the <code>muted</code> property.</li> -<li>adds/updates the <code>muting_history</code><sup>[9]</sup> property on every updated contact, sets the last known server-side state of the contact and sets the <code>last_update</code> property to <code>client_side</code></li> -<li>updates the report doc to add a <code>client_side_transitions</code> property to track which transitions have run client-side</li> -</ul> -<p>Server-side:</p> -<ul> -<li>updates the target contact&rsquo;s topmost muted ancestor<sup>[1][5]</sup> and all its descendants, removing the <code>muted</code> property</li> -<li>adds a <code>muting_history</code> entry to Sentinel <code>info</code> docs for every updated contact<sup>[7]</sup></li> -<li>updates all connected registrations<sup>[3]</sup>, changing the state of all present/future<sup>[6]</sup> <code>muted</code> <code>scheduled_tasks</code> to <code>scheduled</code></li> -<li>as of 3.12.0, updates the contact&rsquo;s client-side <code>muting_history</code> to set the <code>last_update</code> property to <code>server_side</code> and update the <code>server_side</code> section with the current date and muted state.</li> -<li>as of 3.12.0, if the report was processed client-side, all &ldquo;following&rdquo; muting/unmuting events that have affected the same contacts will be replayed. This means the transition <em>could</em> end up running multiple times over the same report<sup>[10]</sup>.</li> -</ul> -<p>[1] Contacts that are already in the correct state are skipped. This applies to updates to the contact itself, updates to the Sentinel <code>muting_history</code> and to the connected registrations (registrations of a contact that is already in the correct state will not be updated).<br> -[2] The date represents the moment Sentinel has processed the muting action<br> -[3] target contact and descendants&rsquo; registrations<br> -[4] <code>scheduled_tasks</code> being in either <code>scheduled</code> or <code>pending</code> state<br> -[5] Because the muted state is inherited, unmuting cascades upwards to the highest level muted ancestor. If none of the ancestors is muted, unmuting cascades downwards only.<br> -[6] <code>scheduled_tasks</code> which are due today or in the future. All <code>scheduled_tasks</code> with a due date in the past will remain unchanged.<br> -[8] The date represents the device&rsquo;s date when the report is processed.<br> -[9] Replaying is required due to how PouchDB &lt;-&gt; CouchDB synchronization does not respect the order in which the documents have been created, to ensure that contacts end up in the correct muted state. <br> -[10] The updated contacts are limited to the contacts available on the device.</p> -<h5 id="7-muting-history">[7] Muting history</h5> -<p>Each time the <code>muted</code> state of a contact changes, an entry is added to a <code>muting_history</code> list saved in Sentinel <code>info</code> docs (stored as an array property with the same name). -Entries in <code>muting_history</code> contain the following information:</p> -<table> -<thead> -<tr> -<th>Property</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>muted</td> -<td>Boolean representing the muted state</td> -</tr> -<tr> -<td>date</td> -<td>Date in ISO Format</td> -</tr> -<tr> -<td>report_id</td> -<td>An <code>_id</code> reference to the report that triggered the action</td> -</tr> -</tbody> -</table> -<h5 id="11-client-side-muting-history-as-of-3120">[11] Client-side Muting history as of 3.12.0</h5> -<p>Each time the client changes the <code>muted</code> state of a contact, an entry is added to a <code>muting_history</code> property on the contact&rsquo;s doc. The <code>last_update</code> entry is also changed to <code>client_side</code>. -The <code>muting_history</code> property contains the following information:</p> -<table> -<thead> -<tr> -<th>Property</th> -<th>Values</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>last_update</td> -<td><code>server_side</code> or <code>client_side</code></td> -<td>Updated every time a service updates the contact, with the corresponding value</td> -</tr> -<tr> -<td>server_side</td> -<td>Object</td> -<td></td> -</tr> -<tr> -<td>server_side.muted</td> -<td><code>true</code> or <code>false</code></td> -<td>Last known server-side muting state</td> -</tr> -<tr> -<td>server_side.date</td> -<td>Date in ISO format</td> -<td>Last known server-side muting/unmuting date</td> -</tr> -<tr> -<td>client_side</td> -<td>Array</td> -<td>Client-side muting/unmuting events list. <br> New events are pushed at the end of this list and it should never be re-ordered. <br>The list represents the &ldquo;chronological&rdquo; order in which the reports that triggered muting were created.</td> -</tr> -<tr> -<td>client_side[].muted</td> -<td><code>true</code> or <code>false</code></td> -<td>Client-side muting state</td> -</tr> -<tr> -<td>client_side[].date</td> -<td>Date in ISO format</td> -<td>Client-side muting/unmuting date</td> -</tr> -<tr> -<td>client_side[].report_id</td> -<td>uuid</td> -<td>The uuid of the muting/unmuting report that triggered the update</td> -</tr> -</tbody> -</table> -<h4 id="configuration-4">Configuration</h4> -<p>Configuration is stored in the <code>muting</code> field of <code>app_settings.json</code>.</p> -<table> -<thead> -<tr> -<th>Property</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>mute_forms</code></td> -<td>An array of form codes which will trigger muting. <strong>Required</strong></td> -</tr> -<tr> -<td><code>unmute_forms</code></td> -<td>An array of form codes which will trigger unmuting. Optional.</td> -</tr> -<tr> -<td><code>validations</code></td> -<td>List of form fields validations. All mute &amp; unmute forms will be subjected to these validation rules. Invalid forms will not trigger muting/unmuting actions. Optional.</td> -</tr> -<tr> -<td><code>messages</code></td> -<td>List of tasks/errors that will be created, determined by <code>event_type</code>. Optional.</td> -</tr> -</tbody> -</table> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Contact forms cannot trigger muting or unmuting, but any <code>data_record</code> that has a <code>form</code> property (typically a <a href="https://docs.communityhealthtoolkit.org/core/overview/db-schema/#reports">Report</a>) can. -</div> -<p>Supported <code>events_types</code> are:</p> -<table> -<thead> -<tr> -<th>Event Type</th> -<th>Trigger</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>mute</code></td> -<td>On successful <code>mute</code> action</td> -</tr> -<tr> -<td><code>unmute</code></td> -<td>On successful <code>unmute</code> action</td> -</tr> -<tr> -<td><code>already_muted</code></td> -<td>On <code>mute</code> action, when target contact is already muted</td> -</tr> -<tr> -<td><code>already_unmuted</code></td> -<td>On <code>unmute</code> action, when target contact is already unmuted</td> -</tr> -<tr> -<td><code>contact_not_found</code></td> -<td>Either mute or unmute actions when target contact is not found</td> -</tr> -</tbody> -</table> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -<p>When muting events are processed both client-side and server-side, there is no guarantee that the state of the database will be the same between the two processing events. Some possible cases where the data is changed in significant ways, that will affect the final muting state are:</p> -<ul> -<li>updated muting settings between client and server processing of the same report / contact</li> -<li>editing the muting/unmuting reports before they are synced, but after the transition ran locally, that either change the target contact or change the validity of the report</li> -<li>deleting muting/unmuting reports before they are synced</li> -<li>validation rules that depend on database data (for example using the &ldquo;exists&rdquo; rule, which will run over different data sets, a report could be valid for the client but invalid for the server and the other way around).</li> -<li>conflicts that overwrite <code>muting_history</code> for contacts</li> -<li>delayed sync for some docs (either contacts or reports) could exacerbate the above because of the &ldquo;replay&rdquo; behavior.</li> -</ul> -</div> -<h5 id="example-1">Example</h5> -<pre tabindex="0"><code>&#34;muting&#34;: { -&#34;mute_forms&#34;: [&#34;mute_person&#34;, &#34;mute_clinic&#34;], -&#34;unmute_forms&#34;: [&#34;unmute_person&#34;, &#34;unmute_clinic&#34;], -&#34;validations&#34;: { -&#34;join_responses&#34;: true, -&#34;list&#34;: [] -}, -&#34;messages&#34;: [ -{ -&#34;translation_key&#34;: &#34;&#34;, -&#34;event_type&#34;: &#34;mute&#34;, -&#34;recipient&#34;: &#34;reporting_unit&#34; -}, -{ -&#34;translation_key&#34;: &#34;&#34;, -&#34;event_type&#34;: &#34;unmute&#34;, -&#34;recipient&#34;: &#34;reporting_unit&#34; -}, -{ -&#34;translation_key&#34;: &#34;&#34;, -&#34;event_type&#34;: &#34;already_muted&#34;, -&#34;recipient&#34;: &#34;reporting_unit&#34; -}, -{ -&#34;translation_key&#34;: &#34;&#34;, -&#34;event_type&#34;: &#34;already_unmuted&#34;, -&#34;recipient&#34;: &#34;reporting_unit&#34; -}, -{ -&#34;translation_key&#34;: &#34;&#34;, -&#34;event_type&#34;: &#34;contact_not_found&#34;, -&#34;recipient&#34;: &#34;reporting_unit&#34; -} -] -} -</code></pre><h3 id="accept-patient-reports">Accept patient reports</h3> -<p>Allow reporting about patient and place centric workflows by</p> -<ul> -<li>validating the report,</li> -<li>assigning the relevant subject (patient or place) to the report if a registration with the given shortcode (patient_id or place_id) exists,</li> -<li>clearing messages on the registration, and</li> -<li>generating response messages on the report.</li> -</ul> -<h4 id="example-2">Example</h4> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#4e9a06">&#34;transitions&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;accept_patient_reports&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span><span style="color:#a40000">,</span> -</span></span><span style="display:flex;"><span><span style="color:#4e9a06">&#34;registrations&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">[{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;P&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;events&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;on_create&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;trigger&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;assign_schedule&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;params&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;ANC Reminders&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;bool_expr&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;!doc.fields.last_menstrual_period || !(/^[0-9]+$/.test(doc.fields.last_menstrual_period))&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}]</span><span style="color:#a40000">,</span> -</span></span><span style="display:flex;"><span><span style="color:#4e9a06">&#34;schedules&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">[{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;ANC Reminders&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;schedule.anc_no_lmp&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;summary&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;description&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;start_from&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;reported_date&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;messages&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}]</span><span style="color:#a40000">,</span> -</span></span><span style="display:flex;"><span><span style="color:#4e9a06">&#34;patient_reports&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">[{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;pregnancy_visit&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;silence_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;ANC Reminders&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;silence_for&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;8 days&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;fields&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;validations&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;messages&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;messages.pregnancy_visit&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;event_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;report_accepted&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;recipient&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;clinic&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}]</span> -</span></span></code></pre></div><h3 id="accept-case-reports">Accept case reports</h3> -<p>Allow reporting about case centric workflows by</p> -<ul> -<li>validating the report configuration documentation,</li> -<li>assigning the relevant place to the report if a registration with the given case_id exists,</li> -<li>clearing messages on the registration, and</li> -<li>generating response messages on the report.</li> -</ul> -<h4 id="example-3">Example</h4> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#4e9a06">&#34;registrations&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">[{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;8&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;events&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;on_create&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;trigger&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;add_case&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}]</span><span style="color:#a40000">,</span> -</span></span><span style="display:flex;"><span><span style="color:#4e9a06">&#34;accept_case_reports&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">[{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;SIGNOFF&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;validations&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;messages&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;event_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;report_accepted&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;bool_expr&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;some expression&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;message&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;some message&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;recipient&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;some recipients&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}]</span> -</span></span></code></pre></div><h3 id="self_report">self_report</h3> -<p>Updates a <code>data_record</code> to set its patient to its sender. The resulting doc will have <code>fields.patient_uuid</code> and <code>fields.patient_id</code> filled with the sender&rsquo;s information. Provides hydrated patient information to subsequent transitions. -The <code>sender</code> is the contact associated with the phone number that sent the original SMS.<br> -If a doc already contains a <code>patient</code> field, does not have a sender or its <code>form</code> is not configured to be enabled for this transition, it will be ignored.</p> -<h4 id="configuration-5">Configuration</h4> -<p>Configuration is stored in the <code>self_report</code> field of <code>app_settings.json</code> as a list of objects connecting forms to messages. -Every object should have this structure:</p> -<table> -<thead> -<tr> -<th>Property</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>form</code></td> -<td>Form code. <strong>Required</strong></td> -</tr> -<tr> -<td><code>messages</code></td> -<td>List of tasks/errors that will be created, determined by <code>event_type</code>. Optional.</td> -</tr> -</tbody> -</table> -<p>Supported <code>events_types</code> are:</p> -<table> -<thead> -<tr> -<th>Event Type</th> -<th>Trigger</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>report_accepted</code></td> -<td>On successful sender updating</td> -</tr> -<tr> -<td><code>sender_not_found</code></td> -<td>Sender not found</td> -</tr> -</tbody> -</table> -<h5 id="example-4">Example</h5> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#4e9a06">&#34;self_report&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;FORM&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;messages&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;event_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;report_accepted&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;recipient&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;reporting_unit&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;messages.form.report_accepted&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;event_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;sender_not_found&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;recipient&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;reporting_unit&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;messages.form.sender_not_found&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;OTHER&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;messages&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;event_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;report_accepted&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;recipient&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;reporting_unit&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;messages.other.report_accepted&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;event_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;sender_not_found&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;recipient&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;reporting_unit&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;messages.other.sender_not_found&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">]</span> -</span></span></code></pre></div><h3 id="update_clinics">update_clinics</h3> -<p>Adds a contact’s info to a data record so as to attribute an incoming SMS message or report to the appropriate contact.</p> -<h4 id="configuration-6">Configuration</h4> -<p>As of version 3.12 you can add configuration to send a message whenever a contact match fails while running this transition. Configuration is stored in the <code>update_clinics</code> field of app_settings.json as a list of objects connecting forms to messages. Every object should have this structure:</p> -<table> -<thead> -<tr> -<th>Property</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>form</code></td> -<td>Form code.</td> -</tr> -<tr> -<td><code>messages</code></td> -<td>List of tasks/errors that will be created, determined by <code>event_type</code>.</td> -</tr> -</tbody> -</table> -<p>Supported <code>events_types</code> are:</p> -<table> -<thead> -<tr> -<th>Event Type</th> -<th>Trigger</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>sys.facility_not_found</code></td> -<td>Facility not found</td> -</tr> -</tbody> -</table> -<p>If this configuration is not set then the message defaults to what is set in the <code>messages.generic.sys.facility_not_found</code> key.</p> -<h5 id="example-5">Example</h5> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#4e9a06">&#34;update_clinics&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;FORM&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;messages&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;event_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;sys.facility_not_found&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;recipient&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;reporting_unit&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;sys.facility_not_found&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;OTHER&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;messages&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;event_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;sys.facility_not_found&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;recipient&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;reporting_unit&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;messages.other.facility_not_found&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">]</span> -</span></span></code></pre></div><h3 id="create_user_for_contacts">create_user_for_contacts</h3> -<p>Users are automatically created for certain contacts. Both creating a new user for a new contact and replacing an existing user with a new user are supported.</p> -<h4 id="configuration-7">Configuration</h4> -<p>Several configurations are required in <code>app_settings</code> to enable the <code>create_user_for_contacts</code> transition.</p> -<p><a href="https://docs.communityhealthtoolkit.org/apps/reference/api/#login-by-sms">Login by SMS</a> must be enabled by setting the <code>token_login</code> configuration.</p> -<p>The <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/#app_settingsjson"><code>app_url</code> property</a> must be set to the URL of the application. This is used to generate the token login link for the new user.</p> -<h5 id="example-6">Example</h5> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#4e9a06">&#34;app_url&#34;</span><span style="color:#a40000">:</span> <span style="color:#4e9a06">&#34;https://demo.app.medicmobile.org&#34;</span><span style="color:#a40000">,</span> -</span></span><span style="display:flex;"><span><span style="color:#4e9a06">&#34;token_login&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;enabled&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;sms.token.login.help&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span><span style="color:#a40000">,</span> -</span></span><span style="display:flex;"><span><span style="color:#4e9a06">&#34;transitions&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;create_user_for_contacts&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h4 id="create-user">Create User</h4> -<p>When adding a new person contact, the <code>create_user_for_contacts</code> transition can be triggered to create a new user associated with that contact. <em>Available since 4.2.x.</em></p> -<h5 id="example-scenario">Example scenario</h5> -<p>A supervisor can onboard a CHW just by creating a new person contact for the CHW with a &ldquo;create contact&rdquo; form.</p> -<p>Once the new contact is synced with the server and has been processed by Sentinel, a user will be automatically created and the new CHW will receive an SMS message (at the phone number specified in the contact) containing a token login link. This link will allow them to login as the newly created user. For security reasons, the token login link is valid for only one use and can only be used within 24 hours.</p> -<h5 id="form-configuration">Form Configuration</h5> -<p>When the <code>create_user_for_contacts</code> transition is enabled, Sentinel will attempt to create a user for any <em>newly created</em> <code>person</code> contacts with the <code>user_for_contact.create</code> field set to <code>'true'</code>. So, <code>contact</code> forms and <code>app</code> forms for adding persons that should trigger new user creation need to include a <code>user_for_contact</code> group that contains a <code>create</code> field. The calculation for the value of the <code>create</code> field should evaluate to <code>'true'</code> when a new user is desired. Any other value for that field will not trigger user creation.</p> -<p>Once Sentinel has generated a user for the contact, the <code>user_for_contact.create</code> field will be automatically removed from the contact document.</p> -<p>Users are only generated for newly created contacts. Editing an existing contact will not trigger user creation regardless of the value set for the <code>user_for_contact.create</code> field.</p> -<h6 id="required-contact-fields">Required contact fields</h6> -<p>The new person contacts <em>must</em> have the following fields set:</p> -<ul> -<li><code>name</code></li> -<li><code>phone</code> - must be set to a valid number</li> -<li><code>roles</code> - must contain the desired roles for the new user <em>(if just a single role is needed, the <code>role</code> field on the contact may be used instead)</em></li> -</ul> -<p>See the <code>person-create</code> contact form provided in the <a href="https://github.com/medic/cht-core/tree/master/config/default">Default config</a> as an example. This form will trigger the creation of a new user for the contact when the role is set to <code>chw</code> or <code>chw_supervisor</code> and a phone number is provided.</p> -<h4 id="replace-user">Replace User</h4> -<p>An existing offline user can be replaced on a device so that a new user can use that device without needing to immediately sync with the server. <em>Available since 4.1.x.</em></p> -<h5 id="example-scenario-1">Example scenario</h5> -<p>Imagine a CHW is leaving the program, and the CHW&rsquo;s device is returned to their supervisor. The supervisor wants to transfer the device to a new CHW immediately without having the device online to sync with the server.</p> -<p>To do this, when the <code>create_user_for_contacts</code> transition is enabled, the supervisor would submit a configured user replacement form for the original user&rsquo;s contact on the device. This form can create a new contact for the new CHW and will trigger a client-side transition to mark the original contact as replaced. After that, the supervisor can give the device to the new CHW, and they can begin using it.</p> -<p>Subsequent reports submitted on the device by the new CHW will be associated with the new contact. When the device is eventually able to synchronize with the server, it will be automatically logged out so the transition to the new user can be completed. A server-side transition will be triggered to initialize the new user for the new CHW. An SMS message containing a token login link will be sent to the new CHW allowing them to login as the initialized user. The password for the original user will be automatically reset by the server-side transition causing any remaining sessions for the original user (e.g. on other devices) to be logged out.</p> -<h5 id="details">Details</h5> -<p>This process does not actually delete the original user, but just resets the password to a random value. To recover the original user, a server administrator should update the user&rsquo;s password to a known value or re-issue a token login link for the user (if enabled).</p> -<p>Because the server-side transition immediately invalidates any remaining sessions for the original user, it is not recommended to use this process for replacing users that are logged in on multiple devices simultaneously. The data on the device used to replace the user will always be completely synchronized before the user is replaced. However, un-synced data from other devices can be left on those devices when the user is replaced using a separate device.</p> -<h5 id="replace_forms-configuration"><code>replace_forms</code> Configuration</h5> -<p>User replacement via the <code>create_user_for_contacts</code> transition is triggered by submitting a configured <code>app</code> form for the original user&rsquo;s contact.</p> -<p>The IDs of the <code>app</code> forms that should trigger the transition must be configured in the <code>create_user_for_contacts.replace_forms</code> array in the <code>app_settings</code>.</p> -<p>Then, the actual forms must set the <code>replacement_contact_id</code> property to the id of the contact that should be associated with the new user.</p> -<p>These forms should only be submitted for the <em>original user&rsquo;s contact</em>. You can control the form visibility by including <code>user._id === contact._id</code> in the form properties expression.</p> -<p>These forms should only be accessible to offline users (replacing online users is not currently supported). This is the default in the example app form properties file (<a href="https://github.com/medic/cht-core/blob/master/config/default/forms/app/replace_user.properties.json">see <code>replace_user.properties.json</code></a>).</p> -<p>You can prevent a user from being replaced multiple times by including <code>!contact.user_for_contact || !contact.user_for_contact.replace</code> in the form properties expression. <em>(This expression is not recommended for situations where multiple users can be associated with the same contact since replacing one of the users would prevent any of the other users for that contact from accessing the form.)</em></p> -<p>See the <code>replace_user</code> app form provided in the <a href="https://github.com/medic/cht-core/tree/master/config/default">Default config</a> as an example.</p> -<h5 id="example-app_settings">Example <code>app_settings</code></h5> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#4e9a06">&#34;create_user_for_contacts&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;replace_forms&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;replace_user&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h4 id="troubleshooting">Troubleshooting</h4> -<p>Configuration is validated when Sentinel starts. Issues with the configuration will be show in the Sentinel logs.</p> -<p>Errors occurring during the client-side transition will be recorded in the browser&rsquo;s console. This is where problems with processing reports from the replace forms will be logged.</p> -<p>Errors occurring during the server-side transition will be recorded in the Sentinel logs and on the contact doc for the original user. So, if the client-side transition marks the original user for replacement, but Sentinel fails to create the new user, the failure will be recorded on the original contact doc in the <code>errors</code> array.</p> \ No newline at end of file +app_settings.json on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/Recent content in app_settings.json on Community Health ToolkitHugo -- gohugo.ioen.accept_case_reportshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/accept_case_reports/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/accept_case_reports/The accept_case_reports key contains the actions to take when reports about cases are received. +app_settings.json .accept_case_reports[] property description required form Form ID of the case form. yes silence_type A comma separated list of schedules to mute. no silence_for Duration from when the report was submitted for which messages should be muted. It is structured as a string with an integer value followed by a space and the time unit. For instance 8 weeks or 2 days..assetlinkshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/assetlinks/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/assetlinks/Requires CHT Core 4.7.0+, CHT Conf 3.22.0+, and CHT Android 1.3.0+ +When using a custom flavor of cht-android to connect to your CHT instance, the ecosystem supports using deep links to open specific content in the app. (E.g. token login links). Security measures in Android require these deep links be verified either automatically or manually. This assetlinks configuration enables auto-verification for your CHT links in your Android app. The provided JSON file will be served at https://&lt;your CHT instance&gt;/..contact_typeshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/hierarchy/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/hierarchy/From 3.7.0 it is possible to configure what types of places and people are available by modifying the contact_types array in the app settings. Each type has the following properties. +Note Prior to version 3.7.0, CHT Core supported 4 contact types - 3 place types (clinic, health_center, district_hospital) and one person type (person). app_settings.json .contact_types[] Property Description Required id String identifier for the type. At times this will be used to sort the contacts in the UI so it is recommended to using a number prefix with gaps between numbers, eg: 10-district, 20-region, etc..dhis_data_setshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/dhis2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/dhis2/From 3.9.0 it is possible to integrate with DHIS2 by modifying the dhis_data_sets property in app_settings.json. +See Also: DHIS2 Integration +app_settings.js .dhis_data_sets[] Property Type Description Required id string The data set id from DHIS2 with which to integrate Yes translation_key string The translation key of the DHIS2 data set name to be displayed Yes Code samples Configure the id and translation_key of the DHIS2 data set. The id corresponds to the id of the data set in the DHIS2 instance you want to integrate with..formshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/forms/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/forms/JSON forms are defined in either the base_settings.json or the app_settings/forms.json file and compiled in to the app_settings.json file with the compile-app-settings action in the cht-conf tool. JSON Forms are used for parsing reports from formatted SMS, SIM applications, and Medic Collect. JSON form definitions are also used for interoperability with third-party systems. Each form is defined as an JSON form object according to the following schema. The key for each object must be unique and all characters must be uppercase..header_tabshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/header_tabs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/header_tabs/app_settings.js .header_tabs As of 3.10.0, app header tabs icons can be configured by modifying the header_tabs section in the app settings. The header_tabs section consists of key:value pairs, where the key is the name of the tab to configure. These values can also be changed from the Admin console, on the Images page under the &ldquo;Header tabs icons&rdquo; tab. +{ &#34;messages&#34;: { &#34;icon&#34;: &#34;fa-user&#34; }, &#34;tasks&#34;: { &#34;resource_icon&#34;: &#34;medic-health-center&#34; }, &#34;analytics&#34;: { &#34;icon&#34;: &#34;fa-flag&#34;, &#34;resource_icon&#34;: &#34;icon-treatment&#34; } } Available tabs tab name default FontAwesome icon messages fa-envelope tasks fa-flag reports fa-list-alt contacts fa-user analytics fa-bar-chart-o app_settings..outboundhttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/outbound/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/outbound/Outbound is only available in CHT Core 3.5.0 and above +Outbound push allows configurers to have the creation or editing of CouchDB documents trigger outbound REST requests using the data in that document. For example, upon receiving a referral report you could send that referral to an external facility system that will manage and process that event. +These triggers can apply to all document types (not just common types such as reports or contacts) and as such care should be taken to only send the documents you intend (see configuration of relevant_to below)..patient_reportshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/patient_reports/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/patient_reports/The patient_reports key contains the actions to take when reports about people are received. +app_settings.json .patient_reports[] property description required form Form ID of the form. yes name Descriptive name of the form. This is not currently used in the app, but can be a helpful annotation. no format Guide of how the form can be used. This is not currently used in the app, but can be a helpful annotation. no silence_type A comma separated list of schedules to mute..permissionshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-permissions/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-permissions/Permissions are defined by the permissions object in the base_settings.json file. The list below illustrates the available system defined permissions. To utilize a permission, you will need to first add the permission as a property of the permissions object, and then associate the permission to user role(s). +Permissions can be assigned to user roles either directly in base_settings.json as an array of user role identifiers, or configured in the App Management app..registrationshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/registrations/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/registrations/The registrations key contains actions that need to be performed for incoming reports of the specified form. +app_settings.json .registrations[] property description required form Form ID that should trigger the schedule. yes events An array of event object definitions of what should happen when this form is received. yes event[].name Name of the event that has happened. The only supported event is on_create which happens when a form is received. yes event[]..remindershttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/reminders/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/reminders/Configure SMS reminders to notify primary contacts to submit reports for their places. +app_settings.js .reminders[] Property Type Description Required form string If a report with this ID is submitted for this place then the SMS will not be sent. Yes translation_key string The translation key to use to look up the SMS message content. Yes message array or string Deprecated. The SMS content. Use translation_key instead. No text_expression string The later text expression to use to set the frequency of this reminder..replication_depthhttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/replication_depth/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/replication_depth/Replication depth is defined under replication_depth as an array of objects. It grants the ability to limit document replication depending on user roles. +app_settings.json .replication_depth property description required role The configured user role the depth applies to. yes depth The replication depth value. Must be a positive integer or 0. yes report_depth As of 3.10. Replication depth applied to reports submitted by other users no Code sample: { &#34;replication_depth&#34;: [ { &#34;role&#34;: &#34;district_manager&#34;, &#34;depth&#34;: 1, &#34;report_depth&#34;: 1 }, { &#34;role&#34;: &#34;national_manager&#34;, &#34;depth&#34;: 2 } ] }.replications [deprecated]https://docs.communityhealthtoolkit.org/apps/reference/app-settings/replications/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/replications/Deprecated The replications field is only available in versions 3.5.0 to 3.9.0. As of 3.10.0 this field is ignored, and replication happens nightly for user meta databases to a central meta data database. Replications are defined under the app_settings.replications key as an array of replication objects. The definition takes the typical form below: +&#34;replications&#34;: [ { &#34;fromSuffix&#34;: &#34;user-[^\\-]+-meta&#34;, &#34;toSuffix&#34;: &#34;users-meta&#34;, &#34;text_expression&#34;: &#34;&#34;, &#34;cron&#34;: &#34;0 2 * * *&#34; } ] property description required fromSuffix The suffix of the source table(s)..roleshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-roles/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-roles/Each user is assigned one of the defined roles. Roles can be defined using the App Management app, which is represented by the roles object of the app-settings.json file. Each role is defined by an identifier as the key, and an object with the following properties: +app_settings.json .roles{} Property Description Required name The translation key for this role Yes offline Determines if user will be an online or offline user. Set to false for users to be &ldquo;online&rdquo; users..scheduleshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/schedules/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/schedules/Schedules are defined in either the base_settings.json or the app_settings/schedules.json file and compiled in to the app_settings.json file with the compile-app-settings action in the cht-conf tool. +The schedules key contains an array of schedule objects, each representing the messages to send based on a registration. +app_settings.json .schedules[] property description required name A unique string label that is used to identify the schedule. Spaces are allowed. yes summary Short description of the of the schedule..smshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/sms/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/sms/SMS settings are defined under the sms key, as an object supporting the following properties: +app_settings.json .sms property default description outgoing_service medic-gateway Defines the service to use to send SMS messages. Currently supports &ldquo;medic-gateway&rdquo;, &ldquo;africas-talking&rdquo; or &ldquo;rapidpro&rdquo;. For more information read the documentation on &ldquo;africas-talking&rdquo; configuration and &ldquo;rapidpro&rdquo; configuration. duplicate_limit 5 The number of identical sms message allowed to be sent to the same recipient. Code sample The definition takes the typical form below:.token_loginhttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/token_login/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/token_login/Login via SMS settings are defined under the token_login key, as an object supporting the following properties: +app_settings.json .token_login property type required description enabled Boolean yes Enables or disables token_login deployment-wide. When this is false, users can&rsquo;t be updated to use token_login and any requests to login with a token link will fail. translation_key String yes Translation key for the information (helper) sms message that the user receives, along with their token-login link Code sample The definition takes the typical form below:.transitionshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/transitions/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/transitions/When sentinel detects a document has changed it runs transitions against the doc. These transitions can be used to generate a short form patient id or assign a report to a facility. +Configuration By default all transitions are disabled. They can be enabled by configuring the transitions property to have a key with the transitions name and a truthy value. As of version 3.12.0 some transitions will partially run on the client for offline users. \ No newline at end of file diff --git a/apps/reference/app-settings/outbound/index.html b/apps/reference/app-settings/outbound/index.html index 15c194f098..e4f28d73c6 100644 --- a/apps/reference/app-settings/outbound/index.html +++ b/apps/reference/app-settings/outbound/index.html @@ -1,9 +1,9 @@ -.outbound | Community Health Toolkit +.outbound | Community Health Toolkit

    .outbound

    Outbound Push: Exchanging data between CHT applications and other tools

    Outbound is only available in CHT Core 3.5.0 and above

    Outbound push allows configurers to have the creation or editing of CouchDB documents trigger outbound REST requests using the data in that document. For example, upon receiving a referral report you could send that referral to an external facility system that will manage and process that event.

    These triggers can apply to all document types (not just common types such as reports or contacts) and as such care should be taken to only send the documents you intend (see configuration of relevant_to below).

    Configuration

    For outbound pushes to occur, you must enable the mark_for_outbound transition in config:

    .outbound

    Outbound Push: Exchanging data between CHT applications and other tools

    Outbound is only available in CHT Core 3.5.0 and above

    Outbound push allows configurers to have the creation or editing of CouchDB documents trigger outbound REST requests using the data in that document. For example, upon receiving a referral report you could send that referral to an external facility system that will manage and process that event.

    These triggers can apply to all document types (not just common types such as reports or contacts) and as such care should be taken to only send the documents you intend (see configuration of relevant_to below).

    Configuration

    For outbound pushes to occur, you must enable the mark_for_outbound transition in config:

    {
       "transitions": {
         "mark_for_outbound": true
       }
    @@ -396,7 +396,8 @@
       "timestamp": "doc.timestamp"
     }
     

    In 3.9

    • Outbound messages are sent immediately
    • If there is an error in sending it will be added to a send queue to be retried every 5 minutes.
    • When it does finally send it will include any new changes to the document that have occurred in that time.
    • Documents are only ever sent once for configuration

    In 3.4-3.8

    • Outbound messages are added to a send queue that is executed once every 5 minutes or so.
    • If there is an error in sending it will be kept in the queue to be retried in another 5 minutes.
    • When it does finally send it will include any new changes to the document that have occurred in that time.
    • Once the document has successfully sent if the document is changed again it will be sent again, using the same rules as above.

    Inbound?

    The outbound feature is used for sending data to an external service. If you are looking to receive data from an external service, take a look at the records api.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/reference/app-settings/patient_reports/index.html b/apps/reference/app-settings/patient_reports/index.html index c65ecb63a3..350d04b71b 100644 --- a/apps/reference/app-settings/patient_reports/index.html +++ b/apps/reference/app-settings/patient_reports/index.html @@ -1,9 +1,9 @@ -.patient_reports | Community Health Toolkit +.patient_reports | Community Health Toolkit

    .patient_reports

    Patient Reports: Defining SMS workflows with schedules, registration, and patient reports.

    The patient_reports key contains the actions to take when reports about people are received.

    app_settings.json .patient_reports[]

    propertydescriptionrequired
    formForm ID of the form.yes
    nameDescriptive name of the form. This is not currently used in the app, but can be a helpful annotation.no
    formatGuide of how the form can be used. This is not currently used in the app, but can be a helpful annotation.no
    silence_typeA comma separated list of schedules to mute.no
    silence_forDuration from when the report was submitted for which messages should be muted. It is structured as a string with an integer value followed by a space and the time unit. For instance 8 weeks or 2 days. The units available are seconds, minutes, hours, days, weeks, months, years, and their singular forms as well. When a message is muted all messages belonging to the same group will be muted, even if it falls outside of this time period. See messages[].group in Schedules for related info.no
    fieldsDescriptive list of form fields. This is not currently used in the app, but can be a helpful annotation.no
    validationsA set of validations to perform on incoming reports. More information about validation rules can be found here.no
    validations.join_responsesA boolean specifying whether validation messages should be combined into one message.no
    validations.list[]An array of validation rules a report should pass to be considered valid.no
    validations.list[].propertyReport field for which this validation rule will be applied.no
    validations.list[].ruleValidation condition to be applied to the property field. More information about rules can be found here.no
    validations.list[].translation_keyTranslation key for the message reply to be sent if a report fails this rule.no
    messagesAn array of automated responses to incoming reports.no
    messages[].translation_keyTranslation key for the message text associated with this eventno
    messages[].event_typeAn event that will trigger sending of this message. Typical values are: report_accepted when the report has been successfully validated, registration_not_found when the shortcode (patient ID or place ID) supplied in the report doesn’t match any shortcode issued by Medic. on_mute and on_unmute are used in the context of muting as described hereno
    messages[].recipientWho the message should be sent to. Use reporting_unit for the sender of the report, clinic for clinic contact, and parent for the parent contact. See SMS Recipientsno

    Code sample

    This sample shows a V report clearing schedules that have messages within 8 days of the report being received. The sample also defines the response messages if the report is accepted or if the patient is not found.

      "patient_reports": [
    + Create project issue

    .patient_reports

    Patient Reports: Defining SMS workflows with schedules, registration, and patient reports.

    The patient_reports key contains the actions to take when reports about people are received.

    app_settings.json .patient_reports[]

    propertydescriptionrequired
    formForm ID of the form.yes
    nameDescriptive name of the form. This is not currently used in the app, but can be a helpful annotation.no
    formatGuide of how the form can be used. This is not currently used in the app, but can be a helpful annotation.no
    silence_typeA comma separated list of schedules to mute.no
    silence_forDuration from when the report was submitted for which messages should be muted. It is structured as a string with an integer value followed by a space and the time unit. For instance 8 weeks or 2 days. The units available are seconds, minutes, hours, days, weeks, months, years, and their singular forms as well. When a message is muted all messages belonging to the same group will be muted, even if it falls outside of this time period. See messages[].group in Schedules for related info.no
    fieldsDescriptive list of form fields. This is not currently used in the app, but can be a helpful annotation.no
    validationsA set of validations to perform on incoming reports. More information about validation rules can be found here.no
    validations.join_responsesA boolean specifying whether validation messages should be combined into one message.no
    validations.list[]An array of validation rules a report should pass to be considered valid.no
    validations.list[].propertyReport field for which this validation rule will be applied.no
    validations.list[].ruleValidation condition to be applied to the property field. More information about rules can be found here.no
    validations.list[].translation_keyTranslation key for the message reply to be sent if a report fails this rule.no
    messagesAn array of automated responses to incoming reports.no
    messages[].translation_keyTranslation key for the message text associated with this eventno
    messages[].event_typeAn event that will trigger sending of this message. Typical values are: report_accepted when the report has been successfully validated, registration_not_found when the shortcode (patient ID or place ID) supplied in the report doesn’t match any shortcode issued by Medic. on_mute and on_unmute are used in the context of muting as described hereno
    messages[].recipientWho the message should be sent to. Use reporting_unit for the sender of the report, clinic for clinic contact, and parent for the parent contact. See SMS Recipientsno

    Code sample

    This sample shows a V report clearing schedules that have messages within 8 days of the report being received. The sample also defines the response messages if the report is accepted or if the patient is not found.

      "patient_reports": [
         {
           "form": "V",
           "name": "Visit (SMS)",
    @@ -343,7 +343,8 @@
     Reference >
     app_settings.json
     : Sms Recipient Resolution

    Settings: The primary location of settings for CHT applications

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/reference/app-settings/registrations/index.html b/apps/reference/app-settings/registrations/index.html index 2b059be0f8..812d32581e 100644 --- a/apps/reference/app-settings/registrations/index.html +++ b/apps/reference/app-settings/registrations/index.html @@ -1,9 +1,9 @@ -.registrations | Community Health Toolkit +.registrations | Community Health Toolkit

    .registrations

    Registrations: Defining SMS workflows with schedules, registration, and patient reports.

    The registrations key contains actions that need to be performed for incoming reports of the specified form.

    app_settings.json .registrations[]

    propertydescriptionrequired
    formForm ID that should trigger the schedule.yes
    eventsAn array of event object definitions of what should happen when this form is received.yes
    event[].nameName of the event that has happened. The only supported event is on_create which happens when a form is received.yes
    event[].triggerWhat should happen after the named event. assign_schedule will assign the schedule named in params to this report. Similarly clear_schedule will permanently clear all messages for a patient or place that are part of schedules listed in the params field. The full set of trigger configuration directives are described here.yes
    event[].paramsAny useful information for the event. In our case, it holds the name of the schedule to be triggered.no
    event[].bool_exprA JavaScript expression that will be cast to boolean to qualify execution of the event. Leaving blank will default to always true. CouchDB document fields can be accessed using doc.key.subkey. Regular expressions can be tested using pattern.test(value) e.g. /^[0-9]+$/.test(doc.fields.last_menstrual_period). In our example above, we’re making sure the form has an LMP date.no
    validationsA set of validations to perform on incoming reports. More information about validation rules can be found here.no
    validations.join_responsesA boolean specifying whether validation messages should be combined into one message.no
    validations.list[]An array of validation rules a report should pass to be considered valid.no
    validations.list[].propertyReport field for which this validation rule will be applied.no
    validations.list[].ruleValidation condition to be applied to the property field. More information about rules can be found here.no
    validations.list[].translation_keyTranslation key for the message reply to be sent if a report fails this rule.no
    messagesAn array of automated responses to incoming reports.no
    messages[].translation_keyTranslation key for the message text associated with this event.no
    messages[].event_typeAn event that will trigger sending of this message. Typical values are: report_accepted when the report has been successfully validated and registration_not_found when the shortcode (patient ID or place ID) supplied in the report doesn’t match any shortcode issued by Medic.no
    messages[].recipientWho the message should be sent to. Use reporting_unit for the sender of the report, clinic for clinic contact, and parent for the parent contact. See SMS Recipientsno

    Code sample

    This sample shows how a schedule would be triggered by a pregnancy report if the last_menstrual_period value is set.

    .registrations

    Registrations: Defining SMS workflows with schedules, registration, and patient reports.

    The registrations key contains actions that need to be performed for incoming reports of the specified form.

    app_settings.json .registrations[]

    propertydescriptionrequired
    formForm ID that should trigger the schedule.yes
    eventsAn array of event object definitions of what should happen when this form is received.yes
    event[].nameName of the event that has happened. The only supported event is on_create which happens when a form is received.yes
    event[].triggerWhat should happen after the named event. assign_schedule will assign the schedule named in params to this report. Similarly clear_schedule will permanently clear all messages for a patient or place that are part of schedules listed in the params field. The full set of trigger configuration directives are described here.yes
    event[].paramsAny useful information for the event. In our case, it holds the name of the schedule to be triggered.no
    event[].bool_exprA JavaScript expression that will be cast to boolean to qualify execution of the event. Leaving blank will default to always true. CouchDB document fields can be accessed using doc.key.subkey. Regular expressions can be tested using pattern.test(value) e.g. /^[0-9]+$/.test(doc.fields.last_menstrual_period). In our example above, we’re making sure the form has an LMP date.no
    validationsA set of validations to perform on incoming reports. More information about validation rules can be found here.no
    validations.join_responsesA boolean specifying whether validation messages should be combined into one message.no
    validations.list[]An array of validation rules a report should pass to be considered valid.no
    validations.list[].propertyReport field for which this validation rule will be applied.no
    validations.list[].ruleValidation condition to be applied to the property field. More information about rules can be found here.no
    validations.list[].translation_keyTranslation key for the message reply to be sent if a report fails this rule.no
    messagesAn array of automated responses to incoming reports.no
    messages[].translation_keyTranslation key for the message text associated with this event.no
    messages[].event_typeAn event that will trigger sending of this message. Typical values are: report_accepted when the report has been successfully validated and registration_not_found when the shortcode (patient ID or place ID) supplied in the report doesn’t match any shortcode issued by Medic.no
    messages[].recipientWho the message should be sent to. Use reporting_unit for the sender of the report, clinic for clinic contact, and parent for the parent contact. See SMS Recipientsno

    Code sample

    This sample shows how a schedule would be triggered by a pregnancy report if the last_menstrual_period value is set.

    
     "registrations": [
       {
         "form": "pregnancy",
    @@ -322,7 +322,8 @@
     Reference >
     app_settings.json
     : Sms Recipient Resolution

    Settings: The primary location of settings for CHT applications

    -

    Last modified 24.11.2021: Fix a typo in registrations (8802ced1)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/reference/app-settings/reminders/index.html b/apps/reference/app-settings/reminders/index.html index 92f3662f8c..79fe55bf0a 100644 --- a/apps/reference/app-settings/reminders/index.html +++ b/apps/reference/app-settings/reminders/index.html @@ -1,9 +1,9 @@ -.reminders | Community Health Toolkit +.reminders | Community Health Toolkit

    .reminders

    Configure SMS reminders to users

    Configure SMS reminders to notify primary contacts to submit reports for their places.

    app_settings.js .reminders[]

    PropertyTypeDescriptionRequired
    formstringIf a report with this ID is submitted for this place then the SMS will not be sent.Yes
    translation_keystringThe translation key to use to look up the SMS message content.Yes
    messagearray or stringDeprecated. The SMS content. Use translation_key instead.No
    text_expressionstringThe later text expression to use to set the frequency of this reminder.Yes (unless cron is defined)
    cronstringThe cron expression to use to set the frequency of this reminderYes (unless text_expression is defined)
    contact_typesarrayAll contact type IDs that should receive the SMS. Defaults to the lowest level places. Added in 3.10.0No

    Code samples

    app_settings.js

    "reminders": [
    + Create project issue

    .reminders

    Configure SMS reminders to users

    Configure SMS reminders to notify primary contacts to submit reports for their places.

    app_settings.js .reminders[]

    PropertyTypeDescriptionRequired
    formstringIf a report with this ID is submitted for this place then the SMS will not be sent.Yes
    translation_keystringThe translation key to use to look up the SMS message content.Yes
    messagearray or stringDeprecated. The SMS content. Use translation_key instead.No
    text_expressionstringThe later text expression to use to set the frequency of this reminder.Yes (unless cron is defined)
    cronstringThe cron expression to use to set the frequency of this reminderYes (unless text_expression is defined)
    contact_typesarrayAll contact type IDs that should receive the SMS. Defaults to the lowest level places. Added in 3.10.0No

    Code samples

    app_settings.js

    "reminders": [
       {
         "form": "stock",
         "translation_key": "sms.reminder.stock",
    @@ -309,7 +309,8 @@
       }
     ],
     
    -

    Last modified 14.07.2020: Add reminders documentation (edcb26e3)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/reference/app-settings/replication_depth/index.html b/apps/reference/app-settings/replication_depth/index.html index 8a20e37eb6..25be8ab3a8 100644 --- a/apps/reference/app-settings/replication_depth/index.html +++ b/apps/reference/app-settings/replication_depth/index.html @@ -1,9 +1,9 @@ -.replication_depth | Community Health Toolkit +.replication_depth | Community Health Toolkit

    .replication_depth

    Replications: Instructions for replication depth

    Replication depth is defined under replication_depth as an array of objects. It grants the ability to limit document replication depending on user roles.

    app_settings.json .replication_depth

    propertydescriptionrequired
    roleThe configured user role the depth applies to.yes
    depthThe replication depth value. Must be a positive integer or 0.yes
    report_depthAs of 3.10. Replication depth applied to reports submitted by other usersno
    Code sample:

    .replication_depth

    Replications: Instructions for replication depth

    Replication depth is defined under replication_depth as an array of objects. It grants the ability to limit document replication depending on user roles.

    app_settings.json .replication_depth

    propertydescriptionrequired
    roleThe configured user role the depth applies to.yes
    depthThe replication depth value. Must be a positive integer or 0.yes
    report_depthAs of 3.10. Replication depth applied to reports submitted by other usersno
    Code sample:
    {
       "replication_depth": [
         { "role": "district_manager", "depth": 1, "report_depth": 1 },
         { "role": "national_manager", "depth": 2 }
       ]
     }
     
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/reference/app-settings/replications/index.html b/apps/reference/app-settings/replications/index.html index 6c908fd0a0..39a1753ed9 100644 --- a/apps/reference/app-settings/replications/index.html +++ b/apps/reference/app-settings/replications/index.html @@ -1,9 +1,9 @@ -.replications [deprecated] | Community Health Toolkit +.replications [deprecated] | Community Health Toolkit

    .replications [deprecated]

    Replications [deprecated]: Configure replication of databases to a main meta database.

    Replications are defined under the app_settings.replications key as an array of replication objects. The definition takes the typical form below:

    "replications": [
    + Create project issue

    .replications [deprecated]

    Replications [deprecated]: Configure replication of databases to a main meta database.

    Replications are defined under the app_settings.replications key as an array of replication objects. The definition takes the typical form below:

    "replications": [
       {
         "fromSuffix": "user-[^\\-]+-meta",
         "toSuffix": "users-meta",
    @@ -309,7 +309,8 @@
       }
     ]
     
    propertydescriptionrequired
    fromSuffixThe suffix of the source table(s). Regular expression may be used.yes
    toSuffixThe suffix of the target table.yes
    text_expressionAny valid text expression. For more information, see LaterJSno if cron provided
    cronAny valid Cron expression. For more information, see LaterJSno if text_expression provided
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/reference/app-settings/schedules/index.html b/apps/reference/app-settings/schedules/index.html index 88e6549377..bbd93bbf45 100644 --- a/apps/reference/app-settings/schedules/index.html +++ b/apps/reference/app-settings/schedules/index.html @@ -1,9 +1,9 @@ -.schedules | Community Health Toolkit +.schedules | Community Health Toolkit

    .schedules

    SMS Schedules: Defining SMS workflows with schedules, registration, and patient reports.

    Schedules are defined in either the base_settings.json or the app_settings/schedules.json file and compiled in to the app_settings.json file with the compile-app-settings action in the cht-conf tool.

    The schedules key contains an array of schedule objects, each representing the messages to send based on a registration.

    app_settings.json .schedules[]

    propertydescriptionrequired
    nameA unique string label that is used to identify the schedule. Spaces are allowed.yes
    summaryShort description of the of the schedule.no
    descriptionA narrative for the schedule.no
    start_fromThe base date from which the messages[].offset is added to determine when to send individual messages. You could specify any property on the report that contains a date value. Starting from 4.5.0, an array of property names is also supported; in this case, the first defined field is used. The default is reported_date, which is when the report was submitted.no
    start_mid_groupWhether or not a schedule can start mid-group. If not present, the schedule will not start mid-group. In other terms, the default value is falseno
    messagesArray of objects, each containing a message to send out and its properties.yes
    messages[].translation_keyThe translation key of the message to send out. Available in 2.15+.yes
    messages[].messagesArray of message objects, each with content and locale properties. From 2.15 on use translation_key instead.no
    messages[].groupInteger identifier to group messages that belong together so that they can be cleared together as a group by future reports. For instance a series of messages announcing a visit, and following up for a missed visit could be grouped together and cleared by a single visit report.yes
    messages[].offsetTime interval from the start_from date for when the message should be sent. It is structured as a string with an integer value followed by a space and the time unit. For instance 8 weeks or 2 days. The units available are seconds, minutes, hours, days, weeks, months, years, and their singular forms as well. Note that although you can specify seconds, the accuracy of the sending time will be determined by delays in the processing the message on the server and on the gateway.yes
    messages[].send_dayString value of the day of the week on which the message should be sent. For instance, to send a message at the beginning of the week setting it to "Monday" will make sure the message goes out on the closest Monday after the start_date + offset.no
    messages[].send_timeTime of day that the message should be sent in 24 hour format.no
    messages[].recipientRecipient of the message. It can be set to reporting_unit (sender of the form), clinic (clinic that the sender of the form is attached to), parent (parent of the sender of the form), or a specific phone number. See SMS Recipientsno

    Code Sample

    This sample shows a schedule with a single message, which will be sent on Monday 9am 4 weeks after the LMP date on the report that triggers this schedule:

      "schedules": [
    + Create project issue

    .schedules

    SMS Schedules: Defining SMS workflows with schedules, registration, and patient reports.

    Schedules are defined in either the base_settings.json or the app_settings/schedules.json file and compiled in to the app_settings.json file with the compile-app-settings action in the cht-conf tool.

    The schedules key contains an array of schedule objects, each representing the messages to send based on a registration.

    app_settings.json .schedules[]

    propertydescriptionrequired
    nameA unique string label that is used to identify the schedule. Spaces are allowed.yes
    summaryShort description of the of the schedule.no
    descriptionA narrative for the schedule.no
    start_fromThe base date from which the messages[].offset is added to determine when to send individual messages. You could specify any property on the report that contains a date value. Starting from 4.5.0, an array of property names is also supported; in this case, the first defined field is used. The default is reported_date, which is when the report was submitted.no
    start_mid_groupWhether or not a schedule can start mid-group. If not present, the schedule will not start mid-group. In other terms, the default value is falseno
    messagesArray of objects, each containing a message to send out and its properties.yes
    messages[].translation_keyThe translation key of the message to send out. Available in 2.15+.yes
    messages[].messagesArray of message objects, each with content and locale properties. From 2.15 on use translation_key instead.no
    messages[].groupInteger identifier to group messages that belong together so that they can be cleared together as a group by future reports. For instance a series of messages announcing a visit, and following up for a missed visit could be grouped together and cleared by a single visit report.yes
    messages[].offsetTime interval from the start_from date for when the message should be sent. It is structured as a string with an integer value followed by a space and the time unit. For instance 8 weeks or 2 days. The units available are seconds, minutes, hours, days, weeks, months, years, and their singular forms as well. Note that although you can specify seconds, the accuracy of the sending time will be determined by delays in the processing the message on the server and on the gateway.yes
    messages[].send_dayString value of the day of the week on which the message should be sent. For instance, to send a message at the beginning of the week setting it to "Monday" will make sure the message goes out on the closest Monday after the start_date + offset.no
    messages[].send_timeTime of day that the message should be sent in 24 hour format.no
    messages[].recipientRecipient of the message. It can be set to reporting_unit (sender of the form), clinic (clinic that the sender of the form is attached to), parent (parent of the sender of the form), or a specific phone number. See SMS Recipientsno

    Code Sample

    This sample shows a schedule with a single message, which will be sent on Monday 9am 4 weeks after the LMP date on the report that triggers this schedule:

      "schedules": [
         {
           "name": "ANC Visit Reminders",
           "summary": "",
    @@ -342,7 +342,8 @@
     Reference >
     app_settings.json
     : Sms Recipient Resolution

    Settings: The primary location of settings for CHT applications

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/reference/app-settings/sms/index.html b/apps/reference/app-settings/sms/index.html index a63f21cfd2..ff89d24f3f 100644 --- a/apps/reference/app-settings/sms/index.html +++ b/apps/reference/app-settings/sms/index.html @@ -1,9 +1,9 @@ -.sms | Community Health Toolkit +.sms | Community Health Toolkit

    .sms

    SMS Settings: Instructions and schema for defining SMS settings

    SMS settings are defined under the sms key, as an object supporting the following properties:

    app_settings.json .sms

    propertydefaultdescription
    outgoing_servicemedic-gatewayDefines the service to use to send SMS messages. Currently supports “medic-gateway”, “africas-talking” or “rapidpro”. For more information read the documentation on “africas-talking” configuration and “rapidpro” configuration.
    duplicate_limit5The number of identical sms message allowed to be sent to the same recipient.

    Code sample

    The definition takes the typical form below:

    .sms

    SMS Settings: Instructions and schema for defining SMS settings

    SMS settings are defined under the sms key, as an object supporting the following properties:

    app_settings.json .sms

    propertydefaultdescription
    outgoing_servicemedic-gatewayDefines the service to use to send SMS messages. Currently supports “medic-gateway”, “africas-talking” or “rapidpro”. For more information read the documentation on “africas-talking” configuration and “rapidpro” configuration.
    duplicate_limit5The number of identical sms message allowed to be sent to the same recipient.

    Code sample

    The definition takes the typical form below:

    "sms": {
       "outgoing_service": "medic-gateway",
       "duplicate_limit": "2"
     }
     

    Duplicate SMS messages handling

    Every time a service (API or Sentinel) creates an SMS, we cache the recipient, and the message content, along with the current timestamp. When more than duplicate_limit messages have been created for the same pair of recipient+content, within the cache time limit, we mark the new message with a “duplicate” status. Such messages are never sent. The cache is cleared 30 minutes after the last SMS message for a specific pair was generated.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/reference/app-settings/token_login/index.html b/apps/reference/app-settings/token_login/index.html index 4190f6f4b9..d2e50407e2 100644 --- a/apps/reference/app-settings/token_login/index.html +++ b/apps/reference/app-settings/token_login/index.html @@ -1,9 +1,9 @@ -.token_login | Community Health Toolkit +.token_login | Community Health Toolkit

    .token_login

    Token login: Instructions and schema for Login by SMS

    Login via SMS settings are defined under the token_login key, as an object supporting the following properties:

    app_settings.json .token_login

    propertytyperequireddescription
    enabledBooleanyesEnables or disables token_login deployment-wide. When this is false, users can’t be updated to use token_login and any requests to login with a token link will fail.
    translation_keyStringyesTranslation key for the information (helper) sms message that the user receives, along with their token-login link

    Code sample

    The definition takes the typical form below:

    "token_login": {
    + Create project issue

    .token_login

    Token login: Instructions and schema for Login by SMS

    Login via SMS settings are defined under the token_login key, as an object supporting the following properties:

    app_settings.json .token_login

    propertytyperequireddescription
    enabledBooleanyesEnables or disables token_login deployment-wide. When this is false, users can’t be updated to use token_login and any requests to login with a token link will fail.
    translation_keyStringyesTranslation key for the information (helper) sms message that the user receives, along with their token-login link

    Code sample

    The definition takes the typical form below:

    "token_login": {
       "enabled": true,
       "translation_key": "sms.token.login.help"
     }
    @@ -308,7 +308,8 @@
     Reference >
     API
     : Login by Sms

    RESTful Application Programming Interfaces for integrating with CHT applications

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/reference/app-settings/transitions/index.html b/apps/reference/app-settings/transitions/index.html index 11b2470ca1..11006abc28 100644 --- a/apps/reference/app-settings/transitions/index.html +++ b/apps/reference/app-settings/transitions/index.html @@ -1,9 +1,9 @@ -.transitions | Community Health Toolkit +.transitions | Community Health Toolkit

    .transitions

    Sentinel Transitions: functions executed when database documents change

    When sentinel detects a document has changed it runs transitions against the doc. These transitions can be used to generate a short form patient id or assign a report to a facility.

    Configuration

    By default all transitions are disabled. They can be enabled by configuring the transitions property to have a key with the transitions name and a truthy value. + Create project issue

    .transitions

    Sentinel Transitions: functions executed when database documents change

    When sentinel detects a document has changed it runs transitions against the doc. These transitions can be used to generate a short form patient id or assign a report to a facility.

    Configuration

    By default all transitions are disabled. They can be enabled by configuring the transitions property to have a key with the transitions name and a truthy value. As of version 3.12.0 some transitions will partially run on the client for offline users. To opt out from client-side transitions, add a "client_side: false" property to the transition configuration.

    {
       "transitions": {
         "a": { },
    @@ -575,7 +575,8 @@
     Reference >
     app_settings.json
     : Sms Recipient Resolution

    Settings: The primary location of settings for CHT applications

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/reference/app-settings/user-permissions/index.html b/apps/reference/app-settings/user-permissions/index.html index 99738229be..8b0130918d 100644 --- a/apps/reference/app-settings/user-permissions/index.html +++ b/apps/reference/app-settings/user-permissions/index.html @@ -1,9 +1,9 @@ -.permissions | Community Health Toolkit +.permissions | Community Health Toolkit

    .permissions

    User Permissions: Assigning fine grained settings for user roles

    Permissions are defined by the permissions object in the base_settings.json file. The list below illustrates the available system defined permissions. To utilize a permission, you will need to first add the permission as a property of the permissions object, and then associate the permission to user role(s).

    Permissions can be assigned to user roles either directly in base_settings.json as an array of user role identifiers, or configured in the App Management app.

    See Also: User roles

    System defined permissions

    PropertyDescription
    can_editThis is probably one of the most important permissions in CHT Framework. It allows creating, editing and deleting documents in CouchDB’s medic database. This permission overrides any other permission in this list.
    can_access_gateway_apiAllows access to gateway API
    can_aggregate_targetsAllows access to Target Aggregates page
    can_bulk_delete_reportsAllows users to select multiple reports and delete
    can_configureAllows update of configuration parameters
    can_upgradeAllows upgrades of the CHT Core Framework version via the API or admin interface
    can_create_peopleAllows creation & editing of person contacts
    can_create_placesAllows creation & editing of place contacts
    can_create_recordsAllows creation of reports
    can_create_usersAllows creation of user logins
    can_delete_contactsAllows deletion of people and places
    can_delete_messagesAllows deletion of messages
    can_delete_reportsAllows deletion of reports
    can_delete_usersAllows deletion of users
    can_edit_profileAllows editing of their own user profile
    can_edit_verificationAllows setting and editing of report verification status. To block the user from updating the existing status, use can_verify_reports instead.
    can_export_allAllows export of data including data they do not have access to
    can_export_contactsAllows export of contacts
    can_export_dhisAllows export of DHIS2 metrics
    can_export_feedbackAllows export of user feedback
    can_export_messagesAllows export of reports and messages
    can_log_out_on_androidDisplays logout menu item in hamburger menu for Android users and can be used to log out from the application
    can_update_placesAllows editing of place documents
    can_update_reportsAllows editing of report documents
    can_update_usersAllows editing of user documents
    can_verify_reportsAllows setting report verification status if no status is currently set. To allow the user to update the existing status, use can_edit_verification instead.
    can_view_analyticsAllows access to in-app analytics
    can_view_analytics_tabDisplays analytics tab on the application
    can_view_call_actionDisplays a button to call the selected person
    can_view_contactsAllows viewing contacts
    can_view_contacts_tabDisplays the contacts tab in the application
    can_view_last_visited_dateEnable display of the date a family was last visited
    can_view_message_actionDisplays a button to send a message to the selected contact
    can_view_messagesAllows viewing messages
    can_view_messages_tabDisplays the messages tab in the application
    can_view_outgoing_messagesAllows viewing outgoing messages when logged in as an administrator
    can_view_reportsAllows viewing reports
    can_view_reports_tabDisplays the reports tab in the application
    can_view_tasksAllows viewing tasks
    can_view_tasks_tabDisplays tasks tab in the application
    can_view_tasks_groupDisplays all available tasks within same place after submitting
    can_view_uhc_statsAllows users to view UHC metrics
    can_view_unallocated_data_recordsAllows viewing reports that have no associated contact
    can_view_usersAllows viewing all user accounts
    can_write_wealth_quintilesAllows updating contacts with wealth quintile information
    can_view_old_filter_and_searchAllows users to see the old filter and search in Reports Tab and Contact Tab which is considered deprecated and will be completely removed in a future release. Admin user will always see the new redesigned filter. See Feature Flags for more info.
    can_view_old_action_barAllows users to see the old action bar in Message Tab, Reports Tab and Contact Tab which is considered deprecated and will be completely removed in a future release. The More Options menu will be hidden when this permission is enabled. The Admin user will always see the new More Options menu. See Feature Flags for more info.
    can_default_facility_filterDefaults the Place Filter in Reports tab to the user’s associated facility. The user should have a contact associated that belongs to a facility. This feature is not available for Admin and Offline type of users. Use with caution, online users that can access thousands of reports can experience slow performance especially where the network is slow. Added in 4.3.
    can_have_multiple_placesAllows users to be assigned more than one facility_id. Helps support health systems where offline Supervisors manage CHWs from different geographical areas. Each facility_id must be at the same level in the hierarchy. Added in 4.9.0

    Code sample

    This sample shows how to define the permissions object in the base_settings.json file. Observe how can_edit permission has been associated to supervisor_role and chw_role user roles.

    "permissions": {
    + Create project issue

    .permissions

    User Permissions: Assigning fine grained settings for user roles

    Permissions are defined by the permissions object in the base_settings.json file. The list below illustrates the available system defined permissions. To utilize a permission, you will need to first add the permission as a property of the permissions object, and then associate the permission to user role(s).

    Permissions can be assigned to user roles either directly in base_settings.json as an array of user role identifiers, or configured in the App Management app.

    See Also: User roles

    System defined permissions

    PropertyDescription
    can_editThis is probably one of the most important permissions in CHT Framework. It allows creating, editing and deleting documents in CouchDB’s medic database. This permission overrides any other permission in this list.
    can_access_gateway_apiAllows access to gateway API
    can_aggregate_targetsAllows access to Target Aggregates page
    can_bulk_delete_reportsAllows users to select multiple reports and delete
    can_configureAllows update of configuration parameters
    can_upgradeAllows upgrades of the CHT Core Framework version via the API or admin interface
    can_create_peopleAllows creation & editing of person contacts
    can_create_placesAllows creation & editing of place contacts
    can_create_recordsAllows creation of reports
    can_create_usersAllows creation of user logins
    can_delete_contactsAllows deletion of people and places
    can_delete_messagesAllows deletion of messages
    can_delete_reportsAllows deletion of reports
    can_delete_usersAllows deletion of users
    can_edit_profileAllows editing of their own user profile
    can_edit_verificationAllows setting and editing of report verification status. To block the user from updating the existing status, use can_verify_reports instead.
    can_export_allAllows export of data including data they do not have access to
    can_export_contactsAllows export of contacts
    can_export_dhisAllows export of DHIS2 metrics
    can_export_feedbackAllows export of user feedback
    can_export_messagesAllows export of reports and messages
    can_log_out_on_androidDisplays logout menu item in hamburger menu for Android users and can be used to log out from the application
    can_update_placesAllows editing of place documents
    can_update_reportsAllows editing of report documents
    can_update_usersAllows editing of user documents
    can_verify_reportsAllows setting report verification status if no status is currently set. To allow the user to update the existing status, use can_edit_verification instead.
    can_view_analyticsAllows access to in-app analytics
    can_view_analytics_tabDisplays analytics tab on the application
    can_view_call_actionDisplays a button to call the selected person
    can_view_contactsAllows viewing contacts
    can_view_contacts_tabDisplays the contacts tab in the application
    can_view_last_visited_dateEnable display of the date a family was last visited
    can_view_message_actionDisplays a button to send a message to the selected contact
    can_view_messagesAllows viewing messages
    can_view_messages_tabDisplays the messages tab in the application
    can_view_outgoing_messagesAllows viewing outgoing messages when logged in as an administrator
    can_view_reportsAllows viewing reports
    can_view_reports_tabDisplays the reports tab in the application
    can_view_tasksAllows viewing tasks
    can_view_tasks_tabDisplays tasks tab in the application
    can_view_tasks_groupDisplays all available tasks within same place after submitting
    can_view_uhc_statsAllows users to view UHC metrics
    can_view_unallocated_data_recordsAllows viewing reports that have no associated contact
    can_view_usersAllows viewing all user accounts
    can_write_wealth_quintilesAllows updating contacts with wealth quintile information
    can_view_old_filter_and_searchAllows users to see the old filter and search in Reports Tab and Contact Tab which is considered deprecated and will be completely removed in a future release. Admin user will always see the new redesigned filter. See Feature Flags for more info.
    can_view_old_action_barAllows users to see the old action bar in Message Tab, Reports Tab and Contact Tab which is considered deprecated and will be completely removed in a future release. The More Options menu will be hidden when this permission is enabled. The Admin user will always see the new More Options menu. See Feature Flags for more info.
    can_default_facility_filterDefaults the Place Filter in Reports tab to the user’s associated facility. The user should have a contact associated that belongs to a facility. This feature is not available for Admin and Offline type of users. Use with caution, online users that can access thousands of reports can experience slow performance especially where the network is slow. Added in 4.3.
    can_have_multiple_placesAllows users to be assigned more than one facility_id. Helps support health systems where offline Supervisors manage CHWs from different geographical areas. Each facility_id must be at the same level in the hierarchy. Added in 4.9.0

    Code sample

    This sample shows how to define the permissions object in the base_settings.json file. Observe how can_edit permission has been associated to supervisor_role and chw_role user roles.

    "permissions": {
       "can_edit": [ "supervisor_role", "chw_role" ],
       "can_access_gateway_api": [ "supervisor_role" ],
       "can_aggregate_targets": [ "supervisor_role", "chw_role" ],
    @@ -308,7 +308,8 @@
       ...
     }
     
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/reference/app-settings/user-roles/index.html b/apps/reference/app-settings/user-roles/index.html index 4e30c890b4..1972d6cc74 100644 --- a/apps/reference/app-settings/user-roles/index.html +++ b/apps/reference/app-settings/user-roles/index.html @@ -1,9 +1,9 @@ -.roles | Community Health Toolkit +.roles | Community Health Toolkit

    .roles

    User Roles: Defining the roles that can be assigned to users.

    Each user is assigned one of the defined roles. Roles can be defined using the App Management app, which is represented by the roles object of the app-settings.json file. Each role is defined by an identifier as the key, and an object with the following properties:

    app_settings.json .roles{}

    PropertyDescriptionRequired
    nameThe translation key for this roleYes
    offlineDetermines if user will be an online or offline user. Set to false for users to be “online” users.No, default true
    -

    Last modified 04.06.2020: Refactored reference section (6526502d)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/reference/contact-page/index.html b/apps/reference/contact-page/index.html index 49ad462084..84f14ab206 100644 --- a/apps/reference/contact-page/index.html +++ b/apps/reference/contact-page/index.html @@ -1,9 +1,9 @@ -contact-summary.templated.js | Community Health Toolkit +contact-summary.templated.js | Community Health Toolkit

    contact-summary.templated.js

    Contact Pages: Customizing the fields, cards, and actions on profile pages

    Contact profile pages display basic information about the contact along with their history and upcoming tasks. + Create project issue

    contact-summary.templated.js

    Contact Pages: Customizing the fields, cards, and actions on profile pages

    Contact profile pages display basic information about the contact along with their history and upcoming tasks. A contact’s profile page is defined by the Fields, Cards, and Care Guides available.

    Helper variables and functions for the contact summary can be defined in contact-summary-extras.js. There are several variables available to inspect to generate the summary information:

    VariableDescription
    contactThe currently selected contact. This has minimal stubs for the contact.parent, so if you want to refer to a property on the parent use lineage below.
    reportsAn array of reports for the contact or for any of the contact’s person children. Note that if the contact has more than 500 reports, only the 500 with the latest reported_date values will be provided. Prior to version 4.7.0, only 50 reports were provided.
    lineageAn array of the contact’s parents (2.13+), eg lineage[0] is the parent, lineage[1] is the grandparent, etc. Each lineage entry has full information for the contact, so you can use lineage[1].contact.phone. lineage will include only those contacts which are visible to the logged in profile
    targetDocDoc with target document of the contact, hydrated with the config information of every target it contains a value for. If there is no target document available (for example when viewing a contact that does not upload targets), this value will be undefined. This value might also be undefined if the contact has not yet synced the current target document. Added in 3.9.0.
    uhcStatsObject containing UHC stats information. Added in v3.12.0
    uhcStats.uhcIntervalObject containing the start and end date of UHC reporting period, it is calculated from the uhc.visit_count.month_start_date defined in the app settings.
    uhcStats.uhcInterval.startTimestamp, start date of the UHC reporting period.
    uhcStats.uhcInterval.endTimestamp, end date of the UHC reporting period.
    uhcStats.homeVisitsObject containing the contact’s home visits stats. The contact’s type should have count_visits enabled and the UHC visit count settings should be defined, additionally this information is only available for users that have can_view_uhc_stats permission and that are not System Administrators.
    uhcStats.homeVisits.lastVisitedDateTimestamp, date of contact’s last home visit.
    uhcStats.homeVisits.countNumber of contact’s home visits in the current reporting interval.
    uhcStats.homeVisits.countGoalNumber, home visits goal, defined in the UHC visit count settings.
    chtObject containing the CHT API for contact summary, targets and tasks. Added in v3.12.0

    CHT API

    Introduced in v3.12.0

    Provides CHT-Core Framework’s functions to contact summary, targets and tasks. The API is available in the cht reserved variable under the v1 version.

    FunctionArgumentsDescription
    hasPermissions(permissions, userRoles, chtPermissionsSettings)permissions: String or array of permission name(s).
    userRoles: (Optional) Array of user roles. Default to the current logged in user.
    chtPermissionsSettings: (Optional) Object of configured permissions in CHT-Core’s settings. Default to the current instance’s configured permissions.
    Returns true if the user has the permission(s), otherwise returns false.
    hasAnyPermission(permissionsGroups, userRoles, chtPermissionsSettings)permissionsGroups: Array of groups of permission name(s).
    userRoles: (Optional) Array of user roles. Default to the current logged in user.
    chtPermissionsSettings: (Optional) Object of configured permissions in CHT-Core’s settings. Default to the current instance’s configured permissions.
    Returns true if the user has all the permissions of any of the provided groups, otherwise returns false.
    getExtensionLib(name)name: String of script nameReturns an executable function identified by the given name configured as extension-libs.

    CHT API’s code samples

    const canEdit = cht.v1.hasPermissions('can_edit');
     const canManagePlaces = cht.v1.hasPermissions(['can_create_places', 'can_update_places']);
     const hasAnyGroup = cht.v1.hasAnyPermission([
    @@ -377,7 +377,8 @@
     Quick Guides >
     Forms >
     Form Inputs

    Data accessible from within CHT forms

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/reference/extension-libs/index.html b/apps/reference/extension-libs/index.html index f2c8589873..268882c9bf 100644 --- a/apps/reference/extension-libs/index.html +++ b/apps/reference/extension-libs/index.html @@ -1,9 +1,9 @@ -extension-libs/ | Community Health Toolkit +extension-libs/ | Community Health Toolkit

    extension-libs/

    Used for providing custom scripts for execution in CHT apps

    Introduced in v4.2.0

    Introduction

    Extension libraries are blocks of code that are cached with the CHT web application giving app developers a powerful tool to extend the CHT. This is an advanced feature and requires an app developer with some software development experience.

    An example of a use for this feature is to provide a function to calculate a risk score based on a machine learning model. The function can then be called passing in values from app forms and return the result to be stored with the report.

    Library

    The first step is to create the js file to return the function that will be called by the web application. Create a new file using this template:

    module.exports = function(/* parameters */) {
    + Create project issue

    extension-libs/

    Used for providing custom scripts for execution in CHT apps

    Introduced in v4.2.0

    Introduction

    Extension libraries are blocks of code that are cached with the CHT web application giving app developers a powerful tool to extend the CHT. This is an advanced feature and requires an app developer with some software development experience.

    An example of a use for this feature is to provide a function to calculate a risk score based on a machine learning model. The function can then be called passing in values from app forms and return the result to be stored with the report.

    Library

    The first step is to create the js file to return the function that will be called by the web application. Create a new file using this template:

    module.exports = function(/* parameters */) {
       return result;
     }
     

    Now populate the function as needed. For complex functions, it is recommended to use third party libraries (such as momentjs, lodash, etc) and use a bundler (eg: webpack) to make it easy to build a single file. It’s recommended to use development best practices such as linting, unit tests, and minification to ensure quality and small download size.

    xpath functions

    To call the function from within a form the parameters and return value will need to have a very specific structure to work with Enketo xforms.

    {
    @@ -332,7 +332,8 @@
         average.js
         calculate-risk-score.js
     

    Now run the command: cht upload-extension-libs

    This will create or update a document to CouchDB with an ID of extension-libs with each of the configured scripts attached. Once this is created the webapp service worker will be updated so the libraries are cached on the phone ready for use offline.

    Invoking the function

    CHT API

    The function will now be available via the CHT API for tasks, targets, and contact summary configurations.

    CHT xPath functions

    To execute the function from within an xform use the cht:extension-lib xpath function.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/reference/forms/app/index.html b/apps/reference/forms/app/index.html index 22237671f4..3336603582 100644 --- a/apps/reference/forms/app/index.html +++ b/apps/reference/forms/app/index.html @@ -1,9 +1,9 @@ -app | Community Health Toolkit +app | Community Health Toolkit

    app

    App Forms: Used to complete reports, tasks, and actions in the app

    App forms are used for care guides within the web app, whether accessed in browser or via the Android app. When a user completes an app form, the contents are saved in the database with the type data_record. These docs are known in the app as Reports.

    App forms are defined by the following files:

    • A XML form definition using a CHT-enhanced ODK XForm format
    • A XLSForm form definition, easier to write and converts to the XForm (optional)
    • Meta information in the {form_name}.properties.json file (optional)
    • Media files in the {form_name}-media directory (optional). How to include multimedia files.

    XForm

    A CHT-enhanced version of the ODK XForm standard is supported.

    Data needed during the completion of the form (eg patient’s name, prior information) is passed into the inputs group. Reports that have at least one of place_id, patient_id, and patient_uuid at the top level will be associated with that contact.

    See Also: Passing contact data to care guides

    A typical form ends with a summary group (eg group_summary, or group_review) where important information is shown to the user before they submit the form.

    In between the inputs and the closing group is the form flow - a collection of questions that can be grouped into pages. All data fields submitted with a form are stored, but often important information that will need to be accessed from the form is brought to the top level. To make sure forms are properly associated to a contact, make sure at least one of place_id, patient_id, and patient_uuid is stored at the top level of the form.

    See Also: Content and Layout

    XLSForm

    Since writing raw XML can be tedious, we suggest creating the forms using the XLSForm standard, and using the cht-conf command line configurer tool to convert them to XForm format.

    typenamelabelrelevantappearancecalculate
    begin groupinputsInputs./source = ‘user’field-list
    hiddensource
    hiddensource_id
    hiddentask_idTask_ID
    begin groupcontact
    string_idPatient IDselect-contact type-person
    stringpatient_idMedic IDhidden
    stringnamePatient Namehidden
    end group
    end group
    calculate_id../inputs/contact/_id
    calculatepatient_id../inputs/contact/patient_id
    calculatename../inputs/contact/name
    begin groupgroup_summarySummaryfield-list summary
    noter_patient_info**${patient_name}** ID: ${r_patient_id}
    noter_followupFollow Up <i class=“fa fa-flag”></i>
    noter_followup_note${r_followup_instructions}
    end group

    Note: If the form uses a file picker to upload any type of file, and it is accessed by using CHT Android, then include the READ_EXTERNAL_STORAGE permission in order to access the files in the device. To enable this permission add the following line in the branded app’s AndroidManifest.xml.

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    + Create project issue

    app

    App Forms: Used to complete reports, tasks, and actions in the app

    App forms are used for care guides within the web app, whether accessed in browser or via the Android app. When a user completes an app form, the contents are saved in the database with the type data_record. These docs are known in the app as Reports.

    App forms are defined by the following files:

    • A XML form definition using a CHT-enhanced ODK XForm format
    • A XLSForm form definition, easier to write and converts to the XForm (optional)
    • Meta information in the {form_name}.properties.json file (optional)
    • Media files in the {form_name}-media directory (optional). How to include multimedia files.

    XForm

    A CHT-enhanced version of the ODK XForm standard is supported.

    Data needed during the completion of the form (eg patient’s name, prior information) is passed into the inputs group. Reports that have at least one of place_id, patient_id, and patient_uuid at the top level will be associated with that contact.

    See Also: Passing contact data to care guides

    A typical form ends with a summary group (eg group_summary, or group_review) where important information is shown to the user before they submit the form.

    In between the inputs and the closing group is the form flow - a collection of questions that can be grouped into pages. All data fields submitted with a form are stored, but often important information that will need to be accessed from the form is brought to the top level. To make sure forms are properly associated to a contact, make sure at least one of place_id, patient_id, and patient_uuid is stored at the top level of the form.

    See Also: Content and Layout

    XLSForm

    Since writing raw XML can be tedious, we suggest creating the forms using the XLSForm standard, and using the cht-conf command line configurer tool to convert them to XForm format.

    typenamelabelrelevantappearancecalculate
    begin groupinputsInputs./source = ‘user’field-list
    hiddensource
    hiddensource_id
    hiddentask_idTask_ID
    begin groupcontact
    string_idPatient IDselect-contact type-person
    stringpatient_idMedic IDhidden
    stringnamePatient Namehidden
    end group
    end group
    calculate_id../inputs/contact/_id
    calculatepatient_id../inputs/contact/patient_id
    calculatename../inputs/contact/name
    begin groupgroup_summarySummaryfield-list summary
    noter_patient_info**${patient_name}** ID: ${r_patient_id}
    noter_followupFollow Up <i class=“fa fa-flag”></i>
    noter_followup_note${r_followup_instructions}
    end group

    Note: If the form uses a file picker to upload any type of file, and it is accessed by using CHT Android, then include the READ_EXTERNAL_STORAGE permission in order to access the files in the device. To enable this permission add the following line in the branded app’s AndroidManifest.xml.

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
     

    Supported XLSForm Meta Fields

    XLSForm has a number of data type options available for meta data collection, of which the following are supported in CHT app forms:

    elementdescription
    startA timestamp of when the form entry was started, which occurs when the form is fully loaded.
    endA timestamp of when the form entry ended, which is when the user hits the Submit button.
    todayDay on which the form entry was started.

    XPath

    Calculations are achieved within app forms using XPath statements in the “calculate” field of XForms and XLSForms. CHT apps support XPath from the ODK XForm spec, which is based on a subset of XPath 1.0, and is evaluated by openrosa-xpath-evaluator. The ODK XForm documentation provides useful notes about the available operators and functions. Additionally, CHT specific functions are available for forms in CHT apps.

    CHT XForm Widgets

    Some XForm widgets have been added or modified for use in CHT applications. The code for these widgets can be found in the CHT Core Framework repository.

    Bikram Sambat Datepicker

    Calendar widget using Bikram Sambat calendar, which is used by default for appropriate languages. The CHT documentation includes a conversion tool to check the conversion between Gregorian and Bikram Sambat dates.

    See Also: `to-bikram-sambat` XPath function

    Countdown Timer

    A visual timer widget that starts when tapped/clicked, and has an audible alert when done. To use it, first make sure you have the namespaces column in the “settings” tab of your XLSForm populated with a value that includes cht=https://communityhealthtoolkit.org. Then, you can add the timer as a trigger field with the appearance set to countdown-timer. The duration of the timer (in seconds) can be set in a column named instance::cht:duration (the default value is 60).

    typeappearanceinstance::cht:duration
    triggercountdown-timer30

    If you want to make the timer mandatory so users must wait for the timer to complete before continuing to the next page or submitting the form, you can populate the required column with an XPath expression as you would do for any other required question. A value of "OK" will be set for the trigger field when the timer completes.

    Contact Selector

    A dropdown field to search and select a person or place, and save their UUID in the report. The contact’s data will also be mapped to fields with matching names within the containing group. If the contact selector’s appearance includes bind-id-only, the associated data fields are not mapped. See the form input guide for an example.

    Rapid Diagnostic Test Capture

    Take a picture of a Rapid Diagnotistic Test and save it with the report. Works with rdt-capture Android application. To use create a string field with appearance mrdt-verify.

    Display Base64 Image

    Available in +3.13.0.

    To display an image based on a field containing the Base64 encode value, add the appearance display-base64-image to a field type text.

    Android App Launcher

    Available in +3.13.0 and in Android device only.

    A widget to launch an Android app that receives and sends data back to an app form in CHT-Core.

    This widget requires the cht-android app in order to work, and will be disabled for users running the CHT in a browser. This widget requires the READ_EXTERNAL_STORAGE permission in CHT Android to work properly, to enable this permission add the following line in the branded app’s AndroidManifest.xml.

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
     

    Use the Android App Launcher widget in a form to configure an intent to launch an Android app installed in the mobile device. The widget will send values from input fields type text to the app and will assign the app’s response into output fields type text. The only supported field type is text. The widget will automatically display a button to launch the app.

    To define the widget, create a group with the appearance android-app-launcher, then define the Android intent fields with type text. The fields action, category, type, uri, packageName and flags are optional. Every Android app has specific ways of launching with intents, so check the app’s documentation and assign the corresponding values in the default column. See example below:

    typenamelabelappearancerepeat_countdefault
    begin groupcamera-appNO_LABELandroid-app-launcher
    textactionNO_LABELandroid.media.action.IMAGE_CAPTURE
    textcategoryNO_LABEL
    texttypeNO_LABEL
    texturiNO_LABEL
    textpackageNameNO_LABEL
    textflagsNO_LABEL
    end groupcamera-app

    To define the widget’s input fields and send data as Android Intent’s extras, create a group inside the widget with the appearance android-app-inputs. In order to assign the app’s response to the widget’s output fields, create a group with the appearance android-app-outputs.

    Important to remember: The fields inside the input and the output groups should to match in name and location to what the Android app receives and returns, otherwise the communication between the widget and the Android app won’t work properly.

    typenamelabelappearancerepeat_countdefault
    begin groupcamera-appNO_LABELandroid-app-launcher
    textactionNO_LABELandroid.media.action.IMAGE_CAPTURE
    begin groupcamera-app-inputsNO_LABELandroid-app-inputs
    textlocationLocation
    textdestinationDestination
    end groupcamera-app-inputs
    begin groupcamera-app-outputsNO_LABELandroid-app-outputs
    textpicturePicture
    textdateDate
    end groupcamera-app-outputs
    end groupcamera-app

    To instruct the widget to process nested data objects, create a new group inside the input or the output group with the appearance android-app-object. Objects cannot be assigned to a field, it should be a group with fields to map the properties to fields that share the same name.

    Important to remember: The nested group’s name should match in name and location to what the Android app receives and returns, otherwise it won’t be able to find the nested object.

    typenamelabelappearancerepeat_countdefault
    begin groupcamera-appNO_LABELandroid-app-launcher
    textactionNO_LABELandroid.media.action.IMAGE_CAPTURE
    begin groupcamera-app-inputsNO_LABELandroid-app-inputs
    textlocationLocation
    begin groupphoto_configurationNO_LABELandroid-app-object
    textapertureAperture
    textshutter_speedShutter Speed
    end groupphoto_configuration
    end groupcamera-app-inputs
    begin groupcamera-app-outputsNO_LABELandroid-app-outputs
    textpicturePicture
    textdateDate
    end groupcamera-app-outputs
    end groupcamera-app

    To instruct the widget to process an array of strings or numbers, create a new repeat with fix size in the repeat_count column and place it inside the input or the output group with the appearance android-app-value-list, then create 1 field type text to store every array’s value, only 1 field is allowed. To process an array of objects, use the appearance android-app-object-list instead.

    Important to remember: The repeat’s name should match in name and location to what the Android app receives and returns, otherwise it won’t be able to find the array.

    typenamelabelappearancerepeat_countdefault
    begin groupcamera-appNO_LABELandroid-app-launcher
    textactionNO_LABELandroid.media.action.IMAGE_CAPTURE
    textflagsNO_LABEL268435456
    begin groupcamera-app-inputsNO_LABELandroid-app-inputs
    textlocationLocation
    begin repeatphoto_filtersNO_LABELandroid-app-value-list2
    textfilterFilter
    end repeat
    end groupcamera-app-inputs
    begin groupcamera-app-outputsNO_LABELandroid-app-outputs
    textdateDate
    begin repeatcaptureNO_LABELandroid-app-object-list3
    textlight_percentageLight
    textcontrast_percentageContrast
    begin grouppatient_detailsNO_LABELandroid-app-object
    textpicturePatient picture
    textpatient_idPatient ID
    textpatient_namePatient name
    end grouppatient_details
    end repeat
    end groupcamera-app-outputs
    end groupcamera-app

    CHT XPath Functions

    add-date

    Added in 4.0.0.

    Adds the provided number of years/months/days/hours/minutes to a date value.

    add-date(date, years, months, days, hours, minutes)
     

    This function is useful for things like calculating a date that is a specific number of days in the future. For example, the following returns a date that is two weeks from now: add-date(today(), 0, 0, 14).

    You can also add negative numbers to get dates in the past. This can be used to calculate a person’s birthdate date based on how many years/months old they are: add-date(today(), 0-${age_years}, 0-${age_months}).

    cht:difference-in-days

    Added in 4.7.0.

    Calculates the number of whole days between two dates.

    cht:difference-in-weeks

    Added in 4.7.0.

    Calculates the number of whole calendar weeks between two dates.

    cht:difference-in-months

    Calculates the number of whole calendar months between two dates. This is often used when determining a child’s age for immunizations or assessments.

    cht:difference-in-years

    Added in 4.7.0.

    Calculates the number of whole calendar years between two dates.

    cht:extension-lib

    Added in 4.2.0.

    This function invokes a configured extension library. The first parameter is a string with the name of the library to execute, and any remaining parameters are passed through as is. For example, to calculate an average of two numbers, the xpath could be: cht:extension-lib('average.js', /data/first, /data/second ).

    cht:strip-whitespace

    Added in 4.10.0.

    Removes all whitespace characters from a string.

    cht:validate-luhn

    Added in 4.10.0.

    Validate a given number using the Luhn algorithm to help detect typos. Provide the field as the first parameter and optionally include the expected string length in the second parameter. Returns true if the number is valid.

    parse-timestamp-to-date

    Added in 3.13.0.

    Use this function to parse from a timestamp number to a date. This is useful when using other XForm utility functions that receive date type as parameter, see example below:

    typenamelabelcalculationdefault
    stringstart_date_timeNO_LABEL1628945040308
    stringstart_date_time_formattedStarted on:format-date-time(parse-timestamp-to-date(${start_date_time}), “%e/%b/%Y %H:%M:%S”)

    to-bikram-sambat

    Added in 3.14.0.

    This function converts a date to a string containing the value of the date formatted according to the Bikram Sambat calendar.

    See also: Bikram Sambat Datepicker

    z-score

    In Enketo forms you have access to an XPath function to calculate the z-score value for a patient. The function accesses table data stored in CouchDB.

    The z-score function takes four parameters:

    • The name of z-score table to use, which corresponds to value of the database document’s _id attribute.
    • Patient’s sex, which corresponds to the data object’s name. In the example below male for this parameter corresponds to charts[].data.male in the database document.
    • First parameter for the table lookup, such as age. Value maps to the key value in the database document.
    • Second parameter for the table lookup, such as height. Value is compared against the points in the database document.

    Example Use

    This example XForm form shows the use of the z-score function. To calculate the z-score for a patient given their sex, age, and weight the XPath calculation is as follows:

    z-score('weight-for-age', ../my_sex, ../my_age, ../my_weight)

    The data used by this function needs to be added to CouchDB. The example below shows the structure of the database document. It creates a weight-for-age table, where you can see that a male aged 0 at 2.08kg has a z-score of -3. Your database doc will be substantially larger, so you may find the conversion script helpful to convert z-score tables to the required doc format.

    {
    @@ -340,7 +340,8 @@
     Quick Guides >
     Forms >
     Form Inputs

    Data accessible from within CHT forms

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/reference/forms/collect/index.html b/apps/reference/forms/collect/index.html index f0b610dde9..5d5d1b5714 100644 --- a/apps/reference/forms/collect/index.html +++ b/apps/reference/forms/collect/index.html @@ -1,9 +1,9 @@ -collect | Community Health Toolkit +collect | Community Health Toolkit

    collect

    Collect Forms: Served to the Medic Collect application

    ODK XForms are used to render forms in the Medic Collect Android app. These forms cannot use any CHT-specific XForm notations. All Medic Collect forms are processed as SMS (even when submitted over a wifi) therefore a corresponding JSON form with matching fields is used to interpret the incoming report.

    Collect forms must be in the forms/collect folder to be processed by cht-conf’s convert-collect-forms and upload-collect-forms actions. Once uploaded to the server, they can be downloaded by the Medic Collect app. These forms can also be included in Medic Collect builds for users without a data connection to get forms.

    XForms require a couple minor changes to be compatible with Medic Collect so that they can properly be received by a Medic instance. The changes can be done either manually in the XForm’s XML, or automatically with XLSForm forms using cht-conf.

    Automatic changes with XLSForms

    If using a XLSForm and using cht-conf to convert to XForm, the necessary fields will be automatically added to the resulting XForm. You can override the default prefix and separator by declaring sms_keyword and sms_separator respectively in the Settings tab.

    Manual changes in XForm

    Collect forms need prefix and delimiter values added to the XForm’s XML. This is done where the form ID is declared in the instance’s data model. For example, the following:

    <instance>
    + Create project issue

    collect

    Collect Forms: Served to the Medic Collect application

    ODK XForms are used to render forms in the Medic Collect Android app. These forms cannot use any CHT-specific XForm notations. All Medic Collect forms are processed as SMS (even when submitted over a wifi) therefore a corresponding JSON form with matching fields is used to interpret the incoming report.

    Collect forms must be in the forms/collect folder to be processed by cht-conf’s convert-collect-forms and upload-collect-forms actions. Once uploaded to the server, they can be downloaded by the Medic Collect app. These forms can also be included in Medic Collect builds for users without a data connection to get forms.

    XForms require a couple minor changes to be compatible with Medic Collect so that they can properly be received by a Medic instance. The changes can be done either manually in the XForm’s XML, or automatically with XLSForm forms using cht-conf.

    Automatic changes with XLSForms

    If using a XLSForm and using cht-conf to convert to XForm, the necessary fields will be automatically added to the resulting XForm. You can override the default prefix and separator by declaring sms_keyword and sms_separator respectively in the Settings tab.

    Manual changes in XForm

    Collect forms need prefix and delimiter values added to the XForm’s XML. This is done where the form ID is declared in the instance’s data model. For example, the following:

    <instance>
        <data id="myform" >
        ...
     

    becomes:

    <instance>
        <data id="myform" prefix="J1!FORM_CODE!" delimiter="#">
        ...
     

    Note that FORM_CODE should be replaced with the form code as defined in the JSON forms version of the form. If the form code is ABCD the prefix value would be J1!ABCD!, resulting in prefix="J1!ABCD!". In case you are curious, the J1 lets the CHT server know that version 1 of the JavaRosa parser should be used on the incoming SMS.

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/reference/forms/contact/index.html b/apps/reference/forms/contact/index.html index 88aa84e3d5..7fac5edf8a 100644 --- a/apps/reference/forms/contact/index.html +++ b/apps/reference/forms/contact/index.html @@ -1,9 +1,9 @@ -contact | Community Health Toolkit +contact | Community Health Toolkit

    contact

    Contact Forms: Used for creating and editing people and places

    Contact forms are used to create and edit contacts (persons and places). Each contact-type should ideally have two forms; one for creation, and another for editing.

    These forms are stored in the forms/contact sub-folder of the project config directory. The naming convention used should be <contact_type_id-{create|edit}>.xlsx.

    Form details

    Survey sheet

    To collect information about the contact, use a top-level group with the id of the contact_type as the name of the group (e.g. person when adding or editing a person contact). Information in this group will be saved to the contact’s document in the database.

    typenamelabel::en
    begin grouppersonNO_LABEL
    hiddenparentParent Id
    hiddentypeContact Type
    stringnameFull Name
    end group

    The parent, type, and name fields are mandatory on forms that are adding contacts. parent will be automatically populated with the id of the parent contact. type will be automatically set to the contact_type id when saving the new contact.

    Input data

    contact forms have access to a variety of input data.

    Settings sheet

    The form_id should follow the pattern contact:CONTACT_TYPE_ID:ACTION where CONTACT_TYPE_ID is the contact_type id for the contact and ACTION is create or edit. (e.g. contact:clinic:create)

    Properties

    Starting in cht-core release 3.10, we can now configure property files in contact create forms to show or hide them based on an expression or permission as specified in the app form schema. Note: this applies only to the create form, not the contacts themselves.

    Generic contact forms

    If your place contact forms are similar across all levels of your specified project hierarchy, you can templatise the form creation process. You will need to create the following files in forms/contact: place-types.json, PLACE_TYPE-create.xlsx and PLACE_TYPE-edit.xlsx.

    place-types.json maps the place contact_type id to a human-readable description that will be shown on the app’s user interface.

    Both PLACE_TYPE-create.xlsx and PLACE_TYPE-edit.xlsx will contain two placeholder values PLACE_TYPE and PLACE_NAME which will be replaced by the keys and values specified in place-types.json respectively during form conversion. Also, copies of the different place-type forms will be created (if they don’t exist) during the form conversion process with PLACE_TYPE being replaced with the keys specified in place-types.json.

    Convert and build the contact forms into your application using the convert-contact-forms and upload-contact-forms actions in cht-conf.

    cht --local convert-contact-forms upload-contact-forms

    For examples on how to structure the above files you can have a look at the default configuration in CHT-core.

    Creating person and place contacts in the same form

    Contact forms for creating a place can also optionally create one or more person-type documents. One of these person contacts can be linked to the created place as the primary contact.

    Below is a simple structure of a place form showing all the necessary components.

    Place forms survey sheet

    Section 1 is similar to what has been described earlier for person forms.

    Section 2 specifies the contact that will be linked to the place being created. parent, type and contact_type and name are mandatory. This also applies to the place-type definition in section 4. contact on the other hand is not mandatory for the successful creation of a place. It is usually more convenient to create a place and its primary contact at the same time.

    You can also create additional contacts linked to the place being created when you have a structure similar to that shown in section 3.


    CHT Applications > + Create project issue

    contact

    Contact Forms: Used for creating and editing people and places

    Contact forms are used to create and edit contacts (persons and places). Each contact-type should ideally have two forms; one for creation, and another for editing.

    These forms are stored in the forms/contact sub-folder of the project config directory. The naming convention used should be <contact_type_id-{create|edit}>.xlsx.

    Form details

    Survey sheet

    To collect information about the contact, use a top-level group with the id of the contact_type as the name of the group (e.g. person when adding or editing a person contact). Information in this group will be saved to the contact’s document in the database.

    typenamelabel::en
    begin grouppersonNO_LABEL
    hiddenparentParent Id
    hiddentypeContact Type
    stringnameFull Name
    end group

    The parent, type, and name fields are mandatory on forms that are adding contacts. parent will be automatically populated with the id of the parent contact. type will be automatically set to the contact_type id when saving the new contact.

    Input data

    contact forms have access to a variety of input data.

    Settings sheet

    The form_id should follow the pattern contact:CONTACT_TYPE_ID:ACTION where CONTACT_TYPE_ID is the contact_type id for the contact and ACTION is create or edit. (e.g. contact:clinic:create)

    Properties

    Starting in cht-core release 3.10, we can now configure property files in contact create forms to show or hide them based on an expression or permission as specified in the app form schema. Note: this applies only to the create form, not the contacts themselves.

    Generic contact forms

    If your place contact forms are similar across all levels of your specified project hierarchy, you can templatise the form creation process. You will need to create the following files in forms/contact: place-types.json, PLACE_TYPE-create.xlsx and PLACE_TYPE-edit.xlsx.

    place-types.json maps the place contact_type id to a human-readable description that will be shown on the app’s user interface.

    Both PLACE_TYPE-create.xlsx and PLACE_TYPE-edit.xlsx will contain two placeholder values PLACE_TYPE and PLACE_NAME which will be replaced by the keys and values specified in place-types.json respectively during form conversion. Also, copies of the different place-type forms will be created (if they don’t exist) during the form conversion process with PLACE_TYPE being replaced with the keys specified in place-types.json.

    Convert and build the contact forms into your application using the convert-contact-forms and upload-contact-forms actions in cht-conf.

    cht --local convert-contact-forms upload-contact-forms

    For examples on how to structure the above files you can have a look at the default configuration in CHT-core.

    Creating person and place contacts in the same form

    Contact forms for creating a place can also optionally create one or more person-type documents. One of these person contacts can be linked to the created place as the primary contact.

    Below is a simple structure of a place form showing all the necessary components.

    Place forms survey sheet

    Section 1 is similar to what has been described earlier for person forms.

    Section 2 specifies the contact that will be linked to the place being created. parent, type and contact_type and name are mandatory. This also applies to the place-type definition in section 4. contact on the other hand is not mandatory for the successful creation of a place. It is usually more convenient to create a place and its primary contact at the same time.

    You can also create additional contacts linked to the place being created when you have a structure similar to that shown in section 3.


    CHT Applications > Quick Guides > Forms > Form Inputs

    Data accessible from within CHT forms

    CHT Applications > @@ -313,7 +313,8 @@ Quick Guides > Forms > App Form SMS

    Trigger calls and SMS from within the form, or send an SMS once submitted.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/reference/forms/index.html b/apps/reference/forms/index.html index 4e5fc29831..d7fd5f82f8 100644 --- a/apps/reference/forms/index.html +++ b/apps/reference/forms/index.html @@ -1,9 +1,9 @@ -forms/ | Community Health Toolkit +forms/ | Community Health Toolkit

    forms/

    Forms: All the App forms, Contact forms, and Collect forms

    app

    App Forms: Used to complete reports, tasks, and actions in the app

    collect

    Collect Forms: Served to the Medic Collect application

    contact

    Contact Forms: Used for creating and editing people and places

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/reference/forms/index.xml b/apps/reference/forms/index.xml index b94ba998a3..62c540679e 100644 --- a/apps/reference/forms/index.xml +++ b/apps/reference/forms/index.xml @@ -1,1162 +1,6 @@ -Community Health Toolkit – forms/https://docs.communityhealthtoolkit.org/apps/reference/forms/Recent content in forms/ on Community Health ToolkitHugo -- gohugo.ioenApps: apphttps://docs.communityhealthtoolkit.org/apps/reference/forms/app/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/forms/app/ -<p>App forms are used for care guides within the web app, whether accessed in browser or via the Android app. When a user completes an app form, the contents are saved in the database with the type <code>data_record</code>. These docs are known in the app as <a href="https://docs.communityhealthtoolkit.org/apps/features/reports/">Reports</a>.</p> -<p>App forms are defined by the following files:</p> -<ul> -<li>A XML form definition using a CHT-enhanced ODK XForm format</li> -<li>A XLSForm form definition, easier to write and converts to the XForm (optional)</li> -<li>Meta information in the <code>{form_name}.properties.json</code> file (optional)</li> -<li>Media files in the <code>{form_name}-media</code> directory (optional). How to <a href="https://docs.communityhealthtoolkit.org/apps/guides/forms/multimedia/">include multimedia files</a>.</li> -</ul> -<h2 id="xform">XForm</h2> -<p>A CHT-enhanced version of the ODK XForm standard is supported.</p> -<p>Data needed during the completion of the form (eg patient&rsquo;s name, prior information) is passed into the <code>inputs</code> group. Reports that have at least one of <code>place_id</code>, <code>patient_id</code>, and <code>patient_uuid</code> at the top level will be associated with that contact.</p> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/reference/contact-page/#care-guides">Passing contact data to care guides</a></p> -<p>A typical form ends with a summary group (eg <code>group_summary</code>, or <code>group_review</code>) where important information is shown to the user before they submit the form.</p> -<p>In between the <code>inputs</code> and the closing group is the form flow - a collection of questions that can be grouped into pages. All data fields submitted with a form are stored, but often important information that will need to be accessed from the form is brought to the top level. To make sure forms are properly associated to a contact, make sure at least one of <code>place_id</code>, <code>patient_id</code>, and <code>patient_uuid</code> is stored at the top level of the form.</p> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/design/best-practices/#content-and-layout">Content and Layout</a></p> -<h2 id="xlsform">XLSForm</h2> -<p>Since writing raw XML can be tedious, we suggest creating the forms using the <a href="http://xlsform.org/">XLSForm standard</a>, and using the <a href="https://github.com/medic/cht-conf">cht-conf</a> command line configurer tool to <a href="#build">convert them to XForm format</a>.</p> -<table> -<thead> -<tr> -<th>type</th> -<th>name</th> -<th>label</th> -<th>relevant</th> -<th>appearance</th> -<th>calculate</th> -<th>&hellip;</th> -</tr> -</thead> -<tbody> -<tr> -<td>begin group</td> -<td>inputs</td> -<td>Inputs</td> -<td>./source = &lsquo;user&rsquo;</td> -<td>field-list</td> -<td></td> -<td></td> -</tr> -<tr> -<td>hidden</td> -<td>source</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>hidden</td> -<td>source_id</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>hidden</td> -<td>task_id</td> -<td>Task_ID</td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>begin group</td> -<td>contact</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>string</td> -<td>_id</td> -<td>Patient ID</td> -<td></td> -<td>select-contact type-person</td> -<td></td> -<td></td> -</tr> -<tr> -<td>string</td> -<td>patient_id</td> -<td>Medic ID</td> -<td></td> -<td>hidden</td> -<td></td> -<td></td> -</tr> -<tr> -<td>string</td> -<td>name</td> -<td>Patient Name</td> -<td></td> -<td>hidden</td> -<td></td> -<td></td> -</tr> -<tr> -<td>end group</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>end group</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>calculate</td> -<td>_id</td> -<td></td> -<td></td> -<td></td> -<td>../inputs/contact/_id</td> -<td></td> -</tr> -<tr> -<td>calculate</td> -<td>patient_id</td> -<td></td> -<td></td> -<td></td> -<td>../inputs/contact/patient_id</td> -<td></td> -</tr> -<tr> -<td>calculate</td> -<td>name</td> -<td></td> -<td></td> -<td></td> -<td>../inputs/contact/name</td> -<td></td> -</tr> -<tr> -<td>&hellip;</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>begin group</td> -<td>group_summary</td> -<td>Summary</td> -<td></td> -<td>field-list summary</td> -<td></td> -<td></td> -</tr> -<tr> -<td>note</td> -<td>r_patient_info</td> -<td>**${patient_name}** ID: ${r_patient_id}</td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>note</td> -<td>r_followup</td> -<td>Follow Up &lt;i class=&ldquo;fa fa-flag&rdquo;&gt;&lt;/i&gt;</td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>note</td> -<td>r_followup_note</td> -<td>${r_followup_instructions}</td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>end group</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -</tbody> -</table> -<p><strong>Note:</strong> If the form uses a file picker to upload any type of file, and it is accessed by using CHT Android, then include the <code>READ_EXTERNAL_STORAGE</code> permission in order to access the files in the device. To enable this permission add the following line in the branded app&rsquo;s <code>AndroidManifest.xml</code>.</p> -<pre tabindex="0"><code>&lt;uses-permission android:name=&#34;android.permission.READ_EXTERNAL_STORAGE&#34;/&gt; -</code></pre><h3 id="supported-xlsform-meta-fields">Supported XLSForm Meta Fields</h3> -<p><a href="http://xlsform.org/">XLSForm</a> has a number of <a href="https://xlsform.org/en/#metadata">data type options</a> available for meta data collection, of which the following are supported in CHT app forms:</p> -<table> -<thead> -<tr> -<th>element</th> -<th>description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>start</code></td> -<td>A timestamp of when the form entry was started, which occurs when the form is fully loaded.</td> -</tr> -<tr> -<td><code>end</code></td> -<td>A timestamp of when the form entry ended, which is when the user hits the Submit button.</td> -</tr> -<tr> -<td><code>today</code></td> -<td>Day on which the form entry was started.</td> -</tr> -</tbody> -</table> -<h2 id="xpath">XPath</h2> -<p>Calculations are achieved within app forms using XPath statements in the &ldquo;calculate&rdquo; field of XForms and XLSForms. CHT apps support XPath from the <a href="https://getodk.github.io/xforms-spec">ODK XForm spec</a>, which is based on a subset of <a href="https://www.w3.org/TR/1999/REC-xpath-19991116/">XPath 1.0</a>, and is evaluated by <a href="https://github.com/enketo/enketo/tree/main/packages/openrosa-xpath-evaluator"><code>openrosa-xpath-evaluator</code></a>. The ODK XForm documentation provides useful notes about the available <a href="https://getodk.github.io/xforms-spec/#xpath-operators">operators</a> and <a href="https://getodk.github.io/xforms-spec/#xpath-functions">functions</a>. Additionally, <a href="#cht-xpath-functions">CHT specific functions</a> are available for forms in CHT apps.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -The <code>+</code> operator for string concatenation is deprecated and will be removed in a future version. You are strongly encouraged to use the <a href="https://getodk.github.io/xforms-spec/#fn:concat"><code>concat()</code></a> function instead. -</div> -<h2 id="cht-xform-widgets">CHT XForm Widgets</h2> -<p>Some XForm widgets have been added or modified for use in CHT applications. The code for these widgets can be found in the <a href="https://github.com/medic/cht-core/tree/master/webapp/src/js/enketo/widgets">CHT Core Framework repository</a>.</p> -<h3 id="bikram-sambat-datepicker">Bikram Sambat Datepicker</h3> -<p>Calendar widget using Bikram Sambat calendar, which is used by default for appropriate languages. The CHT documentation includes a <a href="https://docs.communityhealthtoolkit.org/bikram-sambat/">conversion tool</a> to check the conversion between Gregorian and Bikram Sambat dates. -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/reference/forms/app/#to-bikram-sambat">`to-bikram-sambat` XPath function</a></p> -</p> -<h3 id="countdown-timer">Countdown Timer</h3> -<p>A visual timer widget that starts when tapped/clicked, and has an audible alert when done. To use it, first make sure you have the <a href="https://getodk.github.io/xforms-spec/#namespaces"><code>namespaces</code> column</a> in the &ldquo;settings&rdquo; tab of your XLSForm populated with a value that includes <code>cht=https://communityhealthtoolkit.org</code>. Then, you can add the timer as a <code>trigger</code> field with the <em>appearance</em> set to <code>countdown-timer</code>. The duration of the timer (in seconds) can be set in a column named <em>instance::cht:duration</em> (the default value is <code>60</code>).</p> -<table> -<thead> -<tr> -<th>type</th> -<th>appearance</th> -<th>instance::cht:duration</th> -</tr> -</thead> -<tbody> -<tr> -<td>trigger</td> -<td>countdown-timer</td> -<td>30</td> -</tr> -</tbody> -</table> -<p>If you want to make the timer mandatory so users must wait for the timer to complete before continuing to the next page or submitting the form, you can populate the <em>required</em> column with an XPath expression as you would do for any other required question. A value of <code>&quot;OK&quot;</code> will be set for the <code>trigger</code> field when the timer completes.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -The <code>trigger</code> implementation of the countdown timer is only supported for CHT versions <code>4.7.0+</code>. For older CHT versions, the deprecated <code>note</code> implementation of the countdown timer can be used. However, it does not support setting a value in the <em>required</em> column. To use the deprecated countdown timer, create a <code>note</code> field with the <em>appearance</em> set to <code>countdown-timer</code>. The duration of the timer (in seconds) can be set in the <code>default</code> column. If this value is not set, the timer will be set to 60 seconds. -</div> -<h3 id="contact-selector">Contact Selector</h3> -<p>A dropdown field to search and select a person or place, and save their UUID in the report. The contact&rsquo;s data will also be mapped to fields with matching names within the containing group. If the contact selector&rsquo;s appearance includes <code>bind-id-only</code>, the associated data fields are not mapped. See <a href="https://docs.communityhealthtoolkit.org/apps/guides/forms/form-inputs/#contact-selector">the form input guide</a> for an example.</p> -<h3 id="rapid-diagnostic-test-capture">Rapid Diagnostic Test Capture</h3> -<p>Take a picture of a Rapid Diagnotistic Test and save it with the report. Works with <a href="https://github.com/medic/rdt-capture/">rdt-capture Android application</a>. To use create a string field with appearance <code>mrdt-verify</code>.</p> -<h3 id="display-base64-image">Display Base64 Image</h3> -<p><em>Available in +3.13.0.</em></p> -<p>To display an image based on a field containing the Base64 encode value, add the appearance <code>display-base64-image</code> to a field type <code>text</code>.</p> -<h3 id="android-app-launcher">Android App Launcher</h3> -<p><em>Available in +3.13.0 and in Android device only.</em></p> -<p>A widget to launch an Android app that receives and sends data back to an app form in CHT-Core.</p> -<p>This widget requires the <code>cht-android</code> app in order to work, and will be disabled for users running the CHT in a browser. This widget requires the <code>READ_EXTERNAL_STORAGE</code> permission in CHT Android to work properly, to enable this permission add the following line in the branded app&rsquo;s <code>AndroidManifest.xml</code>.</p> -<pre tabindex="0"><code>&lt;uses-permission android:name=&#34;android.permission.READ_EXTERNAL_STORAGE&#34;/&gt; -</code></pre><p>Use the Android App Launcher widget in a form to configure an intent to launch an Android app installed in the mobile device. The widget will send values from input fields type <code>text</code> to the app and will assign the app&rsquo;s response into output fields type <code>text</code>. The only supported field type is <code>text</code>. The widget will automatically display a button to launch the app.</p> -<p>To define the widget, create a <code>group</code> with the appearance <code>android-app-launcher</code>, then define the <a href="https://developer.android.com/reference/android/content/Intent">Android intent</a> fields with type <code>text</code>. The fields <code>action</code>, <code>category</code>, <code>type</code>, <code>uri</code>, <code>packageName</code> and <code>flags</code> are optional. Every Android app has specific ways of launching with intents, so check the app&rsquo;s documentation and assign the corresponding values in the <code>default</code> column. See example below:</p> -<table> -<thead> -<tr> -<th>type</th> -<th>name</th> -<th>label</th> -<th>appearance</th> -<th>repeat_count</th> -<th>default</th> -<th>&hellip;</th> -</tr> -</thead> -<tbody> -<tr> -<td>begin group</td> -<td>camera-app</td> -<td>NO_LABEL</td> -<td>android-app-launcher</td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>text</td> -<td>action</td> -<td>NO_LABEL</td> -<td></td> -<td></td> -<td>android.media.action.IMAGE_CAPTURE</td> -<td>&hellip;</td> -</tr> -<tr> -<td>text</td> -<td>category</td> -<td>NO_LABEL</td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>text</td> -<td>type</td> -<td>NO_LABEL</td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>text</td> -<td>uri</td> -<td>NO_LABEL</td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>text</td> -<td>packageName</td> -<td>NO_LABEL</td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>text</td> -<td>flags</td> -<td>NO_LABEL</td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>&hellip;</td> -<td>&hellip;</td> -<td>&hellip;</td> -<td>&hellip;</td> -<td>&hellip;</td> -<td>&hellip;</td> -<td>&hellip;</td> -</tr> -<tr> -<td>end group</td> -<td>camera-app</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -</tbody> -</table> -<p>To define the widget&rsquo;s input fields and send data as Android Intent&rsquo;s <code>extras</code>, create a group inside the widget with the appearance <code>android-app-inputs</code>. In order to assign the app&rsquo;s response to the widget&rsquo;s output fields, create a group with the appearance <code>android-app-outputs</code>.</p> -<p><strong>Important to remember:</strong> The fields inside the input and the output groups should to match in name and location to what the Android app receives and returns, otherwise the communication between the widget and the Android app won&rsquo;t work properly.</p> -<table> -<thead> -<tr> -<th>type</th> -<th>name</th> -<th>label</th> -<th>appearance</th> -<th>repeat_count</th> -<th>default</th> -<th>&hellip;</th> -</tr> -</thead> -<tbody> -<tr> -<td>begin group</td> -<td>camera-app</td> -<td>NO_LABEL</td> -<td>android-app-launcher</td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>text</td> -<td>action</td> -<td>NO_LABEL</td> -<td></td> -<td></td> -<td>android.media.action.IMAGE_CAPTURE</td> -<td>&hellip;</td> -</tr> -<tr> -<td>begin group</td> -<td>camera-app-inputs</td> -<td>NO_LABEL</td> -<td>android-app-inputs</td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>text</td> -<td>location</td> -<td>Location</td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>text</td> -<td>destination</td> -<td>Destination</td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>end group</td> -<td>camera-app-inputs</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>begin group</td> -<td>camera-app-outputs</td> -<td>NO_LABEL</td> -<td>android-app-outputs</td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>text</td> -<td>picture</td> -<td>Picture</td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>text</td> -<td>date</td> -<td>Date</td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>end group</td> -<td>camera-app-outputs</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>&hellip;</td> -<td>&hellip;</td> -<td>&hellip;</td> -<td>&hellip;</td> -<td>&hellip;</td> -<td>&hellip;</td> -<td>&hellip;</td> -</tr> -<tr> -<td>end group</td> -<td>camera-app</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -</tbody> -</table> -<p>To instruct the widget to process nested data objects, create a new group inside the input or the output group with the appearance <code>android-app-object</code>. Objects cannot be assigned to a field, it should be a group with fields to map the properties to fields that share the same name.</p> -<p><strong>Important to remember:</strong> The nested group&rsquo;s name should match in name and location to what the Android app receives and returns, otherwise it won&rsquo;t be able to find the nested object.</p> -<table> -<thead> -<tr> -<th>type</th> -<th>name</th> -<th>label</th> -<th>appearance</th> -<th>repeat_count</th> -<th>default</th> -<th>&hellip;</th> -</tr> -</thead> -<tbody> -<tr> -<td>begin group</td> -<td>camera-app</td> -<td>NO_LABEL</td> -<td>android-app-launcher</td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>text</td> -<td>action</td> -<td>NO_LABEL</td> -<td></td> -<td></td> -<td>android.media.action.IMAGE_CAPTURE</td> -<td>&hellip;</td> -</tr> -<tr> -<td>begin group</td> -<td>camera-app-inputs</td> -<td>NO_LABEL</td> -<td>android-app-inputs</td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>text</td> -<td>location</td> -<td>Location</td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>begin group</td> -<td>photo_configuration</td> -<td>NO_LABEL</td> -<td>android-app-object</td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>text</td> -<td>aperture</td> -<td>Aperture</td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>text</td> -<td>shutter_speed</td> -<td>Shutter Speed</td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>end group</td> -<td>photo_configuration</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>end group</td> -<td>camera-app-inputs</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>begin group</td> -<td>camera-app-outputs</td> -<td>NO_LABEL</td> -<td>android-app-outputs</td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>text</td> -<td>picture</td> -<td>Picture</td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>text</td> -<td>date</td> -<td>Date</td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>end group</td> -<td>camera-app-outputs</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>&hellip;</td> -<td>&hellip;</td> -<td>&hellip;</td> -<td>&hellip;</td> -<td>&hellip;</td> -<td>&hellip;</td> -<td>&hellip;</td> -</tr> -<tr> -<td>end group</td> -<td>camera-app</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -</tbody> -</table> -<p>To instruct the widget to process an array of strings or numbers, create a new <code>repeat</code> with fix size in the <code>repeat_count</code> column and place it inside the input or the output group with the appearance <code>android-app-value-list</code>, then create 1 field type <code>text</code> to store every array&rsquo;s value, <em>only 1 field is allowed</em>. To process an array of objects, use the appearance <code>android-app-object-list</code> instead.</p> -<p><strong>Important to remember:</strong> The <code>repeat</code>&rsquo;s name should match in name and location to what the Android app receives and returns, otherwise it won&rsquo;t be able to find the array.</p> -<table> -<thead> -<tr> -<th>type</th> -<th>name</th> -<th>label</th> -<th>appearance</th> -<th>repeat_count</th> -<th>default</th> -<th>&hellip;</th> -</tr> -</thead> -<tbody> -<tr> -<td>begin group</td> -<td>camera-app</td> -<td>NO_LABEL</td> -<td>android-app-launcher</td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>text</td> -<td>action</td> -<td>NO_LABEL</td> -<td></td> -<td></td> -<td>android.media.action.IMAGE_CAPTURE</td> -<td>&hellip;</td> -</tr> -<tr> -<td>text</td> -<td>flags</td> -<td>NO_LABEL</td> -<td></td> -<td></td> -<td>268435456</td> -<td>&hellip;</td> -</tr> -<tr> -<td>begin group</td> -<td>camera-app-inputs</td> -<td>NO_LABEL</td> -<td>android-app-inputs</td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>text</td> -<td>location</td> -<td>Location</td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>begin repeat</td> -<td>photo_filters</td> -<td>NO_LABEL</td> -<td>android-app-value-list</td> -<td>2</td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>text</td> -<td>filter</td> -<td>Filter</td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>end repeat</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>end group</td> -<td>camera-app-inputs</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>begin group</td> -<td>camera-app-outputs</td> -<td>NO_LABEL</td> -<td>android-app-outputs</td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>text</td> -<td>date</td> -<td>Date</td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>begin repeat</td> -<td>capture</td> -<td>NO_LABEL</td> -<td>android-app-object-list</td> -<td>3</td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>text</td> -<td>light_percentage</td> -<td>Light</td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>text</td> -<td>contrast_percentage</td> -<td>Contrast</td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>begin group</td> -<td>patient_details</td> -<td>NO_LABEL</td> -<td>android-app-object</td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>text</td> -<td>picture</td> -<td>Patient picture</td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>text</td> -<td>patient_id</td> -<td>Patient ID</td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>text</td> -<td>patient_name</td> -<td>Patient name</td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>end group</td> -<td>patient_details</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>end repeat</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>end group</td> -<td>camera-app-outputs</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -<tr> -<td>&hellip;</td> -<td>&hellip;</td> -<td>&hellip;</td> -<td>&hellip;</td> -<td>&hellip;</td> -<td>&hellip;</td> -<td>&hellip;</td> -</tr> -<tr> -<td>end group</td> -<td>camera-app</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td>&hellip;</td> -</tr> -</tbody> -</table> -<h2 id="cht-xpath-functions">CHT XPath Functions</h2> -<h3 id="add-date"><code>add-date</code></h3> -<p><em>Added in 4.0.0.</em></p> -<p>Adds the provided number of years/months/days/hours/minutes to a date value.</p> -<pre tabindex="0"><code>add-date(date, years, months, days, hours, minutes) -</code></pre><p>This function is useful for things like calculating a date that is a specific number of days in the future. For example, the following returns a date that is two weeks from now: <code>add-date(today(), 0, 0, 14)</code>.</p> -<p>You can also add negative numbers to get dates in the past. This can be used to calculate a person&rsquo;s birthdate date based on how many years/months old they are: <code>add-date(today(), 0-${age_years}, 0-${age_months})</code>.</p> -<h3 id="chtdifference-in-days"><code>cht:difference-in-days</code></h3> -<p><em>Added in 4.7.0.</em></p> -<p>Calculates the number of whole days between two dates.</p> -<h3 id="chtdifference-in-weeks"><code>cht:difference-in-weeks</code></h3> -<p><em>Added in 4.7.0.</em></p> -<p>Calculates the number of whole calendar weeks between two dates.</p> -<h3 id="chtdifference-in-months"><code>cht:difference-in-months</code></h3> -<p>Calculates the number of whole calendar months between two dates. This is often used when determining a child&rsquo;s age for immunizations or assessments.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -For CHT versions lower than <code>4.7.0</code>, the deprecated <code>difference-in-months</code> function (without the <code>cht</code> namespace) should be used. -</div> -<h3 id="chtdifference-in-years"><code>cht:difference-in-years</code></h3> -<p><em>Added in 4.7.0.</em></p> -<p>Calculates the number of whole calendar years between two dates.</p> -<h3 id="chtextension-lib"><code>cht:extension-lib</code></h3> -<p><em>Added in 4.2.0.</em></p> -<p>This function invokes a configured <a href="https://docs.communityhealthtoolkit.org/apps/reference/extension-libs/">extension library</a>. The first parameter is a string with the name of the library to execute, and any remaining parameters are passed through as is. For example, to calculate an average of two numbers, the xpath could be: <code>cht:extension-lib('average.js', /data/first, /data/second )</code>.</p> -<h3 id="chtstrip-whitespace"><code>cht:strip-whitespace</code></h3> -<p><em>Added in 4.10.0.</em></p> -<p>Removes all whitespace characters from a string.</p> -<h3 id="chtvalidate-luhn"><code>cht:validate-luhn</code></h3> -<p><em>Added in 4.10.0.</em></p> -<p>Validate a given number using the <a href="https://en.wikipedia.org/wiki/Luhn_algorithm">Luhn algorithm</a> to help detect typos. Provide the field as the first parameter and optionally include the expected string length in the second parameter. Returns <code>true</code> if the number is valid.</p> -<h3 id="parse-timestamp-to-date"><code>parse-timestamp-to-date</code></h3> -<p><em>Added in 3.13.0.</em></p> -<p>Use this function to parse from a timestamp number to a date. This is useful when using other XForm utility functions that receive date type as parameter, see example below:</p> -<table> -<thead> -<tr> -<th>type</th> -<th>name</th> -<th>label</th> -<th>calculation</th> -<th>default</th> -<th>&hellip;</th> -</tr> -</thead> -<tbody> -<tr> -<td>string</td> -<td>start_date_time</td> -<td>NO_LABEL</td> -<td></td> -<td>1628945040308</td> -<td></td> -</tr> -<tr> -<td>string</td> -<td>start_date_time_formatted</td> -<td>Started on:</td> -<td>format-date-time(<strong>parse-timestamp-to-date(${start_date_time})</strong>, &ldquo;%e/%b/%Y %H:%M:%S&rdquo;)</td> -<td></td> -<td></td> -</tr> -</tbody> -</table> -<h3 id="to-bikram-sambat"><code>to-bikram-sambat</code></h3> -<p><em>Added in 3.14.0.</em></p> -<p>This function converts a <code>date</code> to a <code>string</code> containing the value of the date formatted according to the <a href="https://en.wikipedia.org/wiki/Vikram_Samvat">Bikram Sambat</a> calendar.</p> -<p>See also: <a href="https://docs.communityhealthtoolkit.org/apps/reference/forms/app/#cht-xform-widgets">Bikram Sambat Datepicker</a></p> -<h3 id="z-score"><code>z-score</code></h3> -<p>In Enketo forms you have access to an XPath function to calculate the z-score value for a patient. The function accesses table data stored in CouchDB.</p> -<p>The <code>z-score</code> function takes four parameters:</p> -<ul> -<li>The name of z-score table to use, which corresponds to value of the database document&rsquo;s <code>_id</code> attribute.</li> -<li>Patient&rsquo;s sex, which corresponds to the data object&rsquo;s name. In the example below <code>male</code> for this parameter corresponds to <code>charts[].data.male</code> in the database document.</li> -<li>First parameter for the table lookup, such as age. Value maps to the <code>key</code> value in the database document.</li> -<li>Second parameter for the table lookup, such as height. Value is compared against the <code>points</code> in the database document.</li> -</ul> -<h4 id="example-use">Example Use</h4> -<p><a href="https://github.com/medic/cht-core/blob/3.13.x/demo-forms/z-score.xml">This example XForm form</a> shows the use of the z-score function. To calculate the z-score for a patient given their sex, age, and weight the XPath calculation is as follows:</p> -<p><code>z-score('weight-for-age', ../my_sex, ../my_age, ../my_weight)</code></p> -<p>The data used by this function needs to be added to CouchDB. The example below shows the structure of the database document. It creates a <code>weight-for-age</code> table, where you can see that a male aged 0 at 2.08kg has a z-score of -3. Your database doc will be substantially larger, so you may find the <a href="https://github.com/medic/cht-core/blob/master/scripts/zscore-table-to-json.js">conversion script</a> helpful to convert z-score tables to the required doc format.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;zscore-charts&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;charts&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;weight-for-age&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;data&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;male&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;points&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> <span style="color:#0000cf;font-weight:bold">1.701</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">2.08</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">2.459</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">2.881</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">3.346</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">3.859</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">4.419</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">5.031</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">5.642</span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h2 id="input-data">Input data</h2> -<p><code>app</code> forms have access to a variety of <a href="https://docs.communityhealthtoolkit.org/apps/guides/forms/form-inputs/#app-forms">input data</a> depending on the source of the form.</p> -<h2 id="cht-special-fields">CHT Special Fields</h2> -<p>Some fields in app forms control specific aspects of CHT Apps, either to bring data into forms or for a feature outside of the form.</p> -<h3 id="quintiles">Quintiles</h3> -<p>The <code>NationalQuintile</code> and <code>UrbanQuintile</code> fields on a form will be assigned to all people belonging to the place. This is helpful when household surveys have quintile information which could be used to target health services for individuals. -<p><em>Read More</em>: <a href="https://docs.communityhealthtoolkit.org/apps/guides/forms/wealth-quintiles/">Tracking Wealth Quintiles</a></p> -</p> -<h3 id="uhc-mode">UHC Mode</h3> -<p>When the <code>visited_contact_uuid</code> field is set at the top level of a form, this form is counted as a household visit in <a href="https://docs.communityhealthtoolkit.org/apps/features/uhc-mode/">UHC Mode</a>. This field must be a <code>calculate</code> field with the place UUID of the visited contact. You may run into performance issues if you configure this to look at forms submitted very frequently. -<p><em>Read More</em>: <a href="https://docs.communityhealthtoolkit.org/apps/guides/forms/uhc-mode/">Configuring UHC Mode</a></p> -</p> -<h2 id="uploading-binary-attachments">Uploading Binary Attachments</h2> -<p>Forms can include arbitrary binary data which is submitted and included in the doc as an attachment. If this is an image type it&rsquo;ll then be displayed inline in the report UI.</p> -<p>To mark an element as having binary data add an extra column in the XLSForm called <code>instance::type</code> and specify <code>binary</code> in the element&rsquo;s row.</p> -<h2 id="properties">Properties</h2> -<p>The meta information in the <code>{form_name}.properties.json</code> file defines the form&rsquo;s title and icon, as well as when and where the form should be available.</p> -<h3 id="formsappform_namepropertiesjson"><code>forms/app/{form_name}.properties.json</code></h3> -<table> -<thead> -<tr> -<th>property</th> -<th>description</th> -<th>required</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>title</code></td> -<td>The form&rsquo;s title seen in the app. Uses a localization array using the 2-letter code, not the translation keys discussed in the <a href="https://docs.communityhealthtoolkit.org/apps/reference/translations/">Localization section</a>.</td> -<td>yes</td> -</tr> -<tr> -<td><code>icon</code></td> -<td>Icon associated with the form. The value is the image&rsquo;s key in the <code>resources.json</code> file, as described in the <a href="https://docs.communityhealthtoolkit.org/apps/reference/resources/#icons">Icons section</a></td> -<td>yes</td> -</tr> -<tr> -<td><code>subject_key</code></td> -<td>Override the default report list title with a custom translation key. The translation uses a summary of the report as the evaluation context so you can include report fields in your value, for example: <code>Case registration {{case_id}}</code>. Useful properties available in the summary include: <code>from</code> (the phone number of the sender), <code>phone</code> (the phone number of the report contact), <code>form</code> (the form code), <code>subject.name</code> (the name of the subject), and <code>case_id</code> (the generated case id). Defaults to the name of the report subject.</td> -<td>no</td> -</tr> -<tr> -<td><code>hidden_fields</code></td> -<td>Array of Strings of form fields to hide in the view report UI in the app. This is only applied to future reports and will not change how existing reports are displayed.</td> -<td>no</td> -</tr> -<tr> -<td><code>context</code></td> -<td>The context defines when and where the form should be available in the app</td> -<td>no</td> -</tr> -<tr> -<td><code>context.person</code></td> -<td>Boolean determining if the form can be seen in the Action list for a person&rsquo;s profile. This is still subject to the <code>expression</code>.</td> -<td>no</td> -</tr> -<tr> -<td><code>context.place</code></td> -<td>Boolean determining if the form can be seen in the Action list for a person&rsquo;s profile. This is still subject to the <code>expression</code>.</td> -<td>no</td> -</tr> -<tr> -<td><code>context.expression</code></td> -<td>A JavaScript expression which is evaluated when a contact profile or the reports tab is viewed. If the expression evaluates to true, the form will be listed as an available action. The inputs <code>contact</code>, <code>user</code>, and <code>summary</code> are available. By default, forms are not shown on the reports tab, use <code>&quot;expression&quot;: &quot;!contact&quot;</code> to show the form on the Reports tab since there is no contact for this scenario.</td> -<td>no</td> -</tr> -<tr> -<td><code>context.permission</code></td> -<td>String permission key required to allow the user to view and submit this form. If blank, this defaults to allowing all access.</td> -<td>no</td> -</tr> -</tbody> -</table> -<h3 id="code-sample">Code sample</h3> -<p>In this sample properties file, the associated form would only show on a person&rsquo;s page, and only if their sex is unspecified or female and they are between 10 and 65 years old:</p> -<h4 id="formsapppregnancypropertiesjson"><code>forms/app/pregnancy.properties.json</code></h4> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;title&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;locale&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;en&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;content&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;New Pregnancy&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;locale&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;hi&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;content&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;नई गर्भावस्था&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;icon&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;pregnancy-1&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;hidden_fields&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> <span style="color:#4e9a06">&#34;private&#34;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#34;internal&#34;</span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;context&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;person&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;place&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;expression&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type === &#39;person&#39; &amp;&amp; (!contact.sex || contact.sex === &#39;female&#39;) &amp;&amp; (!contact.date_of_birth || (ageInYears(contact) &gt;= 10 &amp;&amp; ageInYears(contact) &lt; 65))&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;permission&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;can_register_pregnancies&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h2 id="build">Build</h2> -<p>Convert and build the app forms into your application using the <code>convert-app-forms</code> and <code>upload-app-forms</code> actions in <code>cht-conf</code>.</p> -<pre><code>cht --local convert-app-forms upload-app-forms -</code></pre>Apps: collecthttps://docs.communityhealthtoolkit.org/apps/reference/forms/collect/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/forms/collect/ -<p>ODK XForms are used to render forms in the Medic Collect Android app. These forms cannot use any CHT-specific XForm notations. All Medic Collect forms are processed as SMS (even when submitted over a wifi) therefore a corresponding JSON form with matching fields is used to interpret the incoming report.</p> -<p>Collect forms must be in the <code>forms/collect</code> folder to be processed by <a href="https://github.com/medic/cht-conf"><code>cht-conf</code></a>&rsquo;s <code>convert-collect-forms</code> and <code>upload-collect-forms</code> actions. Once uploaded to the server, they can be downloaded by the Medic Collect app. These forms can also be included in Medic Collect builds for users without a data connection to get forms.</p> -<p>XForms require a couple minor changes to be compatible with Medic Collect so that they can properly be received by a Medic instance. The changes can be done either manually in the XForm&rsquo;s XML, or automatically with XLSForm forms using <code>cht-conf</code>.</p> -<h3 id="automatic-changes-with-xlsforms">Automatic changes with XLSForms</h3> -<p>If using a <a href="http://xlsform.org/">XLSForm</a> and using <a href="https://github.com/medic/cht-conf"><code>cht-conf</code></a> to convert to XForm, the necessary fields will be automatically added to the resulting XForm. You can override the default prefix and separator by declaring <code>sms_keyword</code> and <code>sms_separator</code> respectively in the Settings tab.</p> -<h3 id="manual-changes-in-xform">Manual changes in XForm</h3> -<p>Collect forms need <code>prefix</code> and <code>delimiter</code> values added to the XForm&rsquo;s XML. This is done where the form ID is declared in the instance&rsquo;s data model. For example, the following:</p> -<pre tabindex="0"><code>&lt;instance&gt; -&lt;data id=&#34;myform&#34; &gt; -... -</code></pre><p>becomes:</p> -<pre tabindex="0"><code>&lt;instance&gt; -&lt;data id=&#34;myform&#34; prefix=&#34;J1!FORM_CODE!&#34; delimiter=&#34;#&#34;&gt; -... -</code></pre><p>Note that <code>FORM_CODE</code> should be replaced with the form code as defined in the JSON forms version of the form. If the form code is <code>ABCD</code> the prefix value would be <code>J1!ABCD!</code>, resulting in <code>prefix=&quot;J1!ABCD!&quot;</code>. In case you are curious, the <code>J1</code> lets the CHT server know that version 1 of the JavaRosa parser should be used on the incoming SMS.</p>Apps: contacthttps://docs.communityhealthtoolkit.org/apps/reference/forms/contact/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/forms/contact/ -<p>Contact forms are used to create and edit contacts (persons and places). Each contact-type should ideally have two forms; one for creation, and another for editing.</p> -<p>These forms are stored in the <code>forms/contact</code> sub-folder of the project config directory. The naming convention used should be <code>&lt;contact_type_id-{create|edit}&gt;.xlsx</code>.</p> -<h2 id="form-details">Form details</h2> -<h3 id="survey-sheet">Survey sheet</h3> -<p>To collect information about the contact, use a top-level group with the <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/hierarchy/">id of the contact_type</a> as the <code>name</code> of the group (e.g. <code>person</code> when adding or editing a person contact). Information in this group will be saved to the contact&rsquo;s document in the database.</p> -<table> -<thead> -<tr> -<th>type</th> -<th>name</th> -<th>label::en</th> -</tr> -</thead> -<tbody> -<tr> -<td>begin group</td> -<td>person</td> -<td>NO_LABEL</td> -</tr> -<tr> -<td>hidden</td> -<td>parent</td> -<td>Parent Id</td> -</tr> -<tr> -<td>hidden</td> -<td>type</td> -<td>Contact Type</td> -</tr> -<tr> -<td>string</td> -<td>name</td> -<td>Full Name</td> -</tr> -<tr> -<td>&hellip;</td> -<td></td> -<td></td> -</tr> -<tr> -<td>end group</td> -<td></td> -<td></td> -</tr> -</tbody> -</table> -<p>The <code>parent</code>, <code>type</code>, and <code>name</code> fields are mandatory on forms that are adding contacts. <code>parent</code> will be automatically populated with the id of the parent contact. <code>type</code> will be automatically set to the contact_type id when saving the new contact.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Edit forms</h4> -For edit forms, the name of the top-level group should still match the contact_type id of the contact, but only the relevant fields for editing need to be specified in the form. -</div> -<h4 id="input-data">Input data</h4> -<p><code>contact</code> forms have access to a variety of <a href="https://docs.communityhealthtoolkit.org/apps/guides/forms/form-inputs/#app-forms">input data</a>.</p> -<h3 id="settings-sheet">Settings sheet</h3> -<p>The <code>form_id</code> should follow the pattern <code>contact:CONTACT_TYPE_ID:ACTION</code> where CONTACT_TYPE_ID is the contact_type id for the contact and ACTION is <code>create</code> or <code>edit</code>. (e.g. <code>contact:clinic:create</code>)</p> -<h3 id="properties">Properties</h3> -<p>Starting in cht-core release 3.10, we can now configure property files in contact create forms to show or hide them based on an expression or permission as specified in the <a href="https://docs.communityhealthtoolkit.org/apps/reference/forms/app/#formsappform_namepropertiesjson">app form schema</a>. Note: this applies only to the create form, not the contacts themselves.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -The form expression for contact forms will only have access to <code>user</code> data. The <code>contact</code> and <code>summary</code> data are <a href="https://github.com/medic/cht-core/issues/6612">not currently available</a>. -</div> -<h2 id="generic-contact-forms">Generic contact forms</h2> -<p>If your place contact forms are similar across all levels of your specified project hierarchy, you can templatise the form creation process. You will need to create the following files in <code>forms/contact</code>: <code>place-types.json</code>, <code>PLACE_TYPE-create.xlsx</code> and <code>PLACE_TYPE-edit.xlsx</code>.</p> -<p><code>place-types.json</code> maps the place contact_type id to a human-readable description that will be shown on the app&rsquo;s user interface.</p> -<p>Both <code>PLACE_TYPE-create.xlsx</code> and <code>PLACE_TYPE-edit.xlsx</code> will contain two placeholder values <code>PLACE_TYPE</code> and <code>PLACE_NAME</code> which will be replaced by the keys and values specified in <code>place-types.json</code> respectively during form conversion. Also, copies of the different place-type forms will be created (if they don&rsquo;t exist) during the form conversion process with <code>PLACE_TYPE</code> being replaced with the keys specified in <code>place-types.json</code>.</p> -<p>Convert and build the contact forms into your application using the <code>convert-contact-forms</code> and <code>upload-contact-forms</code> actions in <code>cht-conf</code>.</p> -<blockquote> -<p><code>cht --local convert-contact-forms upload-contact-forms</code></p> -</blockquote> -<p>For examples on how to structure the above files you can have a look at the <a href="https://github.com/medic/cht-core/tree/master/config/default/forms/contact">default</a> configuration in CHT-core.</p> -<h2 id="creating-person-and-place-contacts-in-the-same-form">Creating person and place contacts in the same form</h2> -<p>Contact forms for creating a place can also optionally create one or more person-type documents. One of these person contacts can be linked to the created place as the primary contact.</p> -<p>Below is a simple structure of a place form showing all the necessary components.</p> -<p><img src="place-contact-form-survey.png" alt="Place forms survey sheet"></p> -<p>Section 1 is similar to what has been described earlier for person forms.</p> -<p>Section 2 specifies the contact that will be linked to the place being created. <code>parent</code>, <code>type</code> and <code>contact_type</code> and <code>name</code> are mandatory. This also applies to the place-type definition in section 4. <code>contact</code> on the other hand is not mandatory for the successful creation of a place. It is usually more convenient to create a place and its primary contact at the same time.</p> -<p>You can also create additional contacts linked to the place being created when you have a structure similar to that shown in section 3.</p> \ No newline at end of file +forms/ on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/reference/forms/Recent content in forms/ on Community Health ToolkitHugo -- gohugo.ioenapphttps://docs.communityhealthtoolkit.org/apps/reference/forms/app/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/forms/app/App forms are used for care guides within the web app, whether accessed in browser or via the Android app. When a user completes an app form, the contents are saved in the database with the type data_record. These docs are known in the app as Reports. +App forms are defined by the following files: +A XML form definition using a CHT-enhanced ODK XForm format A XLSForm form definition, easier to write and converts to the XForm (optional) Meta information in the {form_name}.collecthttps://docs.communityhealthtoolkit.org/apps/reference/forms/collect/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/forms/collect/ODK XForms are used to render forms in the Medic Collect Android app. These forms cannot use any CHT-specific XForm notations. All Medic Collect forms are processed as SMS (even when submitted over a wifi) therefore a corresponding JSON form with matching fields is used to interpret the incoming report. +Collect forms must be in the forms/collect folder to be processed by cht-conf&rsquo;s convert-collect-forms and upload-collect-forms actions. Once uploaded to the server, they can be downloaded by the Medic Collect app.contacthttps://docs.communityhealthtoolkit.org/apps/reference/forms/contact/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/forms/contact/Contact forms are used to create and edit contacts (persons and places). Each contact-type should ideally have two forms; one for creation, and another for editing. +These forms are stored in the forms/contact sub-folder of the project config directory. The naming convention used should be &lt;contact_type_id-{create|edit}&gt;.xlsx. +Form details Survey sheet To collect information about the contact, use a top-level group with the id of the contact_type as the name of the group (e. \ No newline at end of file diff --git a/apps/reference/index.html b/apps/reference/index.html index 69c522a939..949ef1dbe9 100644 --- a/apps/reference/index.html +++ b/apps/reference/index.html @@ -1,9 +1,9 @@ -Reference Documentation | Community Health Toolkit +Reference Documentation | Community Health Toolkit

    Reference Documentation

    Technical details of CHT app components for app developers

    API to interact with CHT Applications

    RESTful Application Programming Interfaces for integrating with CHT applications

    forms/

    Forms: All the App forms, Contact forms, and Collect forms

    resources/

    Graphics: Used for custom branding, logos, and icons

    translations/

    Localization: Localized labels for CHT applications

    app_settings.json

    Settings: The primary location of settings for CHT applications

    contact-summary.templated.js

    Contact Pages: Customizing the fields, cards, and actions on profile pages

    extension-libs/

    Used for providing custom scripts for execution in CHT apps

    targets.js

    Targets: Definition of target widgets calculated and seen in the app

    tasks.js

    Tasks: Definition of tasks shown to app users

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/reference/index.xml b/apps/reference/index.xml index f2b067f3ab..feaf974a78 100644 --- a/apps/reference/index.xml +++ b/apps/reference/index.xml @@ -1,4609 +1,17 @@ -Community Health Toolkit – Reference Documentationhttps://docs.communityhealthtoolkit.org/apps/reference/Recent content in Reference Documentation on Community Health ToolkitHugo -- gohugo.ioenApps: API to interact with CHT Applicationshttps://docs.communityhealthtoolkit.org/apps/reference/api/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/api/ -<style> -.td-content #TableOfContents ul ul ul { -display: none; -} -</style> -<div class="pageinfo pageinfo-primary"> -<p>This page covers the endpoints to use when integrating with the CHT server. If there isn&rsquo;t an endpoint that provides the function or data you need, direct access to the database is possible via the <a href="https://docs.couchdb.org/en/stable/api/index.html">CouchDB API</a>. Access to the <a href="https://docs.communityhealthtoolkit.org/core/overview/data-flows-for-analytics/">PostgreSQL database</a> may also prove useful for data analysis. If additional endpoints would be helpful, please make suggestions via a <a href="https://github.com/medic/cht-core/issues/new/choose">GitHub issue</a>.</p> -</div> -<nav id="TableOfContents"> -<ul> -<li><a href="#settings">Settings</a> -<ul> -<li><a href="#get-apiv1settings">GET /api/v1/settings</a></li> -<li><a href="#put-apiv1settings">PUT /api/v1/settings</a> -<ul> -<li><a href="#query-parameters">Query Parameters</a></li> -</ul> -</li> -</ul> -</li> -<li><a href="#export">Export</a> -<ul> -<li><a href="#get-apiv2exportdhis">GET /api/v2/export/dhis</a></li> -<li><a href="#get-apiv2exportreports">GET /api/v2/export/reports</a> -<ul> -<li><a href="#query-parameters-1">Query parameters</a></li> -</ul> -</li> -<li><a href="#get-apiv2exportmessages">GET /api/v2/export/messages</a> -<ul> -<li><a href="#output">Output</a></li> -<li><a href="#examples">Examples</a></li> -</ul> -</li> -<li><a href="#get-apiv2exportfeedback">GET /api/v2/export/feedback</a> -<ul> -<li><a href="#query-parameters-2">Query Parameters</a></li> -</ul> -</li> -<li><a href="#get-apiv2exportcontacts">GET /api/v2/export/contacts</a> -<ul> -<li><a href="#output-1">Output</a></li> -<li><a href="#query-parameters-3">Query parameters</a></li> -</ul> -</li> -<li><a href="#get-apiv2exportuser-devices">GET /api/v2/export/user-devices</a> -<ul> -<li><a href="#output-2">Output</a></li> -</ul> -</li> -</ul> -</li> -<li><a href="#forms">Forms</a> -<ul> -<li><a href="#get-apiv1forms">GET /api/v1/forms</a> -<ul> -<li><a href="#headers">Headers</a></li> -<li><a href="#examples-1">Examples</a></li> -</ul> -</li> -<li><a href="#get-apiv1formsidformat">GET /api/v1/forms/{{id}}.{{format}}</a> -<ul> -<li><a href="#parameters">Parameters</a></li> -<li><a href="#examples-2">Examples</a></li> -</ul> -</li> -<li><a href="#post-apiv1formsvalidate">POST /api/v1/forms/validate</a> -<ul> -<li><a href="#headers-1">Headers</a></li> -<li><a href="#examples-3">Examples</a></li> -</ul> -</li> -</ul> -</li> -<li><a href="#records">Records</a> -<ul> -<li><a href="#post-apiv2records">POST /api/v2/records</a> -<ul> -<li><a href="#headers-2">Headers</a></li> -<li><a href="#examples-4">Examples</a></li> -<li><a href="#errors">Errors</a></li> -</ul> -</li> -</ul> -</li> -<li><a href="#sms">SMS</a> -<ul> -<li><a href="#post-apisms">POST /api/sms</a></li> -<li><a href="#post-apiv1smsaggregatorendpoint">POST /api/v1/sms/{aggregator}/{endpoint}</a></li> -</ul> -</li> -<li><a href="#person">Person</a> -<ul> -<li><a href="#get-apiv1personuuid">GET /api/v1/person/{{uuid}}</a> -<ul> -<li><a href="#permissions">Permissions</a></li> -<li><a href="#query-parameters-4">Query parameters</a></li> -<li><a href="#examples-5">Examples</a></li> -</ul> -</li> -</ul> -</li> -<li><a href="#people">People</a> -<ul> -<li><a href="#supported-properties">Supported Properties</a> -<ul> -<li><a href="#required">Required</a></li> -<li><a href="#optional">Optional</a></li> -</ul> -</li> -<li><a href="#post-apiv1people">POST /api/v1/people</a> -<ul> -<li><a href="#permissions-1">Permissions</a></li> -<li><a href="#examples-6">Examples</a></li> -</ul> -</li> -</ul> -</li> -<li><a href="#place">Place</a> -<ul> -<li><a href="#get-apiv1placeuuid">GET /api/v1/place/{{uuid}}</a> -<ul> -<li><a href="#permissions-2">Permissions</a></li> -<li><a href="#query-parameters-5">Query parameters</a></li> -<li><a href="#examples-7">Examples</a></li> -</ul> -</li> -</ul> -</li> -<li><a href="#places">Places</a> -<ul> -<li><a href="#supported-properties-1">Supported Properties</a> -<ul> -<li><a href="#required-properties">Required Properties</a></li> -<li><a href="#optional-properties">Optional Properties</a></li> -<li><a href="#place-types">Place Types</a></li> -</ul> -</li> -<li><a href="#post-apiv1places">POST /api/v1/places</a> -<ul> -<li><a href="#permissions-3">Permissions</a></li> -<li><a href="#examples-8">Examples</a></li> -</ul> -</li> -<li><a href="#post-apiv1placesid">POST /api/v1/places/{{id}}</a> -<ul> -<li><a href="#permissions-4">Permissions</a></li> -<li><a href="#examples-9">Examples</a></li> -</ul> -</li> -</ul> -</li> -<li><a href="#users">Users</a> -<ul> -<li><a href="#supported-properties-2">Supported Properties</a> -<ul> -<li><a href="#login-by-sms">Login by SMS</a></li> -</ul> -</li> -<li><a href="#get-apiv1users">GET /api/v1/users</a> -<ul> -<li><a href="#permissions-5">Permissions</a></li> -<li><a href="#examples-10">Examples</a></li> -</ul> -</li> -<li><a href="#get-apiv2users">GET /api/v2/users</a> -<ul> -<li><a href="#permissions-6">Permissions</a></li> -<li><a href="#query-parameters-6">Query Parameters</a></li> -<li><a href="#examples-11">Examples</a></li> -</ul> -</li> -<li><a href="#get-apiv2usersusername">GET /api/v2/users/{{username}}</a> -<ul> -<li><a href="#permissions-7">Permissions</a></li> -<li><a href="#examples-12">Examples</a></li> -</ul> -</li> -<li><a href="#post-apiv1users">POST /api/v1/users</a> -<ul> -<li><a href="#permissions-8">Permissions</a></li> -<li><a href="#examples-13">Examples</a></li> -<li><a href="#errors-1">Errors</a></li> -</ul> -</li> -<li><a href="#post-apiv2users">POST /api/v2/users</a> -<ul> -<li><a href="#logging">Logging</a></li> -<li><a href="#headers-3">Headers</a></li> -<li><a href="#permissions-9">Permissions</a></li> -<li><a href="#example">Example</a></li> -<li><a href="#errors-2">Errors</a></li> -</ul> -</li> -<li><a href="#post-apiv1usersusername">POST /api/v1/users/{{username}}</a> -<ul> -<li><a href="#permissions-10">Permissions</a></li> -<li><a href="#updating-yourself">Updating yourself</a></li> -<li><a href="#url-parameters">URL Parameters</a></li> -<li><a href="#examples-14">Examples</a></li> -</ul> -</li> -<li><a href="#delete-apiv1usersusername">DELETE /api/v1/users/{{username}}</a> -<ul> -<li><a href="#permissions-11">Permissions</a></li> -<li><a href="#url-parameters-1">URL Parameters</a></li> -<li><a href="#examples-15">Examples</a></li> -</ul> -</li> -<li><a href="#get-apiv1users-info">GET /api/v1/users-info</a> -<ul> -<li><a href="#example-1">Example</a></li> -</ul> -</li> -</ul> -</li> -<li><a href="#bulk-operations">Bulk Operations</a> -<ul> -<li><a href="#post-apiv1bulk-delete">POST /api/v1/bulk-delete</a> -<ul> -<li><a href="#permissions-12">Permissions</a></li> -<li><a href="#parameters-1">Parameters</a></li> -<li><a href="#errors-3">Errors</a></li> -<li><a href="#examples-16">Examples</a></li> -</ul> -</li> -</ul> -</li> -<li><a href="#monitoring">Monitoring</a> -<ul> -<li><a href="#get-apiv1monitoring">GET /api/v1/monitoring</a> -<ul> -<li><a href="#permissions-13">Permissions</a></li> -<li><a href="#examples-17">Examples</a></li> -<li><a href="#response-content">Response content</a></li> -<li><a href="#errors-4">Errors</a></li> -</ul> -</li> -<li><a href="#get-apiv2monitoring">GET /api/v2/monitoring</a> -<ul> -<li><a href="#permissions-14">Permissions</a></li> -<li><a href="#examples-18">Examples</a></li> -<li><a href="#response-content-1">Response content</a></li> -<li><a href="#errors-5">Errors</a></li> -</ul> -</li> -<li><a href="#get-apiv1express-metrics">GET /api/v1/express-metrics</a> -<ul> -<li><a href="#permissions-15">Permissions</a></li> -</ul> -</li> -</ul> -</li> -<li><a href="#upgrades">Upgrades</a> -<ul> -<li><a href="#post-apiv1upgrade">POST /api/v1/upgrade</a> -<ul> -<li><a href="#example-3">Example</a></li> -</ul> -</li> -<li><a href="#post-apiv1upgradestage">POST /api/v1/upgrade/stage</a></li> -<li><a href="#post-apiv1upgradecomplete">POST /api/v1/upgrade/complete</a></li> -</ul> -</li> -<li><a href="#hydrate">Hydrate</a> -<ul> -<li><a href="#get-apiv1hydrate">GET /api/v1/hydrate</a> -<ul> -<li><a href="#query-parameters-8">Query parameters</a></li> -<li><a href="#example-4">Example</a></li> -</ul> -</li> -<li><a href="#post-apiv1hydrate">POST /api/v1/hydrate</a> -<ul> -<li><a href="#parameters-2">Parameters</a></li> -<li><a href="#example-5">Example</a></li> -</ul> -</li> -</ul> -</li> -<li><a href="#contacts-by-phone">Contacts by phone</a> -<ul> -<li><a href="#get-apiv1contacts-by-phone">GET /api/v1/contacts-by-phone</a> -<ul> -<li><a href="#query-parameters-9">Query parameters</a></li> -<li><a href="#example-6">Example</a></li> -</ul> -</li> -<li><a href="#post-apiv1contacts-by-phone">POST /api/v1/contacts-by-phone</a> -<ul> -<li><a href="#parameters-3">Parameters</a></li> -<li><a href="#example-7">Example</a></li> -</ul> -</li> -</ul> -</li> -<li><a href="#replication-limit">Replication Limit</a> -<ul> -<li><a href="#get-apiv1users-doc-count">GET /api/v1/users-doc-count</a> -<ul> -<li><a href="#query-parameters-10">Query parameters</a></li> -<li><a href="#example-8">Example</a></li> -</ul> -</li> -</ul> -</li> -<li><a href="#credentials">Credentials</a> -<ul> -<li><a href="#put-apiv1credentials">PUT /api/v1/credentials</a></li> -<li><a href="#3x-api">3.x API</a></li> -</ul> -</li> -</ul> -</nav> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -<p>Various properties throughout this API use a timestamp value, the -following formats are supported:</p> -<ul> -<li> -<p>ISO 8601 combined date and time with timezone of the format below where &ldquo;Z&rdquo; -is offset from UTC like &ldquo;-03&rdquo;, &ldquo;+1245&rdquo;, or just &ldquo;Z&rdquo; which is UTC (0 offset);</p> -<pre><code> YYYY-MM-DDTHH:mm:ssZ -YYYY-MM-DDTHH:mm:ss.SSSZ -</code></pre> -</li> -<li> -<p>Milliseconds since Unix Epoch</p> -</li> -</ul> -<p>A compatible value can be generated using the <code>toISOString</code> or <code>toValue</code> method -on a Javascript Date object.</p> -<h5 id="examples">Examples</h5> -<ul> -<li>2011-10-10T14:48:00-0300</li> -<li>2016-07-01T13:48:24+00:00</li> -<li>2016-07-01T13:48:24Z</li> -<li>1467383343484 (MS since Epoch)</li> -</ul> -</div> -<h2 id="settings">Settings</h2> -<p>Get and update the app settings.</p> -<h3 id="get-apiv1settings">GET /api/v1/settings</h3> -<p>Returns the settings in JSON format.</p> -<h3 id="put-apiv1settings">PUT /api/v1/settings</h3> -<h4 id="query-parameters">Query Parameters</h4> -<table> -<thead> -<tr> -<th>Variable</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>overwrite</td> -<td>Whether to replace settings document with input document. If both replace and overwrite are set, then it overwrites only. Defaults to replace.</td> -</tr> -<tr> -<td>replace</td> -<td>Whether to replace existing settings for the given properties or to merge. Defaults to false (merging).</td> -</tr> -</tbody> -</table> -<p>Returns a JSON object with two fields:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;success&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;upgraded&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><table> -<thead> -<tr> -<th>Variable</th> -<th>Type</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>success</td> -<td>Boolean</td> -<td>Always <code>true</code></td> -</tr> -<tr> -<td>upgraded</td> -<td>Boolean</td> -<td>Whether the settings doc was updated or not</td> -</tr> -</tbody> -</table> -<h2 id="export">Export</h2> -<p>Request different types of data in various formats.</p> -<p>Each of the export endpoints except contacts, feedback, and user-devices supports a parameter which returns date formatted in human readable form (ISO 8601). Setting this parameter to false or leaving it out will return dates formatted as an epoch timestamp.</p> -<p>To set this parameter for a GET request use:</p> -<pre tabindex="0"><code>http://admin:pass@localhost:5988/api/v2/export/messages?options[humanReadable]=true -</code></pre><p>To set this parameter for a POST request submit this as the request body:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;options&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;humanReadable&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="get-apiv2exportdhis">GET /api/v2/export/dhis</h3> -<p>Exports target data formatted as a DHIS2 dataValueSet. The data can be filtered to a specific section of the contact hierarchy or for a given time interval.</p> -<table> -<thead> -<tr> -<th>Parameter</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>dataSet</td> -<td>A DHIS2 dataSet ID. Targets associated with this dataSet will have their data aggregated. (required)</td> -</tr> -<tr> -<td>date.from</td> -<td>Filter the target data to be aggregated to be within the month of this timestamp. (required)</td> -</tr> -<tr> -<td>orgUnit</td> -<td>Filter the target data to only that associated with contacts with attribute <code>{ dhis: { orgUnit } }</code>. (optional)</td> -</tr> -</tbody> -</table> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;filters&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;dataSet&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;VMuFODsyWaO&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;date&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;from&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">949392000000</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;orgUnit&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;KbY9DJ8mBkx&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="get-apiv2exportreports">GET /api/v2/export/reports</h3> -<p>It uses the <a href="https://github.com/medic/cht-core/tree/master/shared-libs/search">search shared library</a> to ensure identical results in the export and the front-end. It also only supports exporting CSV so we can efficiently stream infinitely large exports.</p> -<h4 id="query-parameters-1">Query parameters</h4> -<p>These are identical to the JS objects passed to the shared library, as if you were using it directly in Javascript.</p> -<p>You may either pass JSON in the request body using <code>POST</code>:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#a40000">POST</span> <span style="color:#a40000">/api/v</span><span style="color:#0000cf;font-weight:bold">2</span><span style="color:#a40000">/export/reports</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;filters&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;forms&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;selected&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;code&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;immunization_visit&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>Or using form-style parameters as <code>GET</code>:</p> -<pre tabindex="0"><code>GET /api/v2/export/reports?filters[search]=&amp;filters[forms][selected][0][code]=immunization_visit -</code></pre><p><strong>NB:</strong> this API is bound directly to this library. For more information on what queries you can perform with the search library, see <a href="https://github.com/medic/cht-core/tree/master/shared-libs/search">its documentation</a>.</p> -<h3 id="get-apiv2exportmessages">GET /api/v2/export/messages</h3> -<p>Download messages.</p> -<h4 id="output">Output</h4> -<table> -<thead> -<tr> -<th>Column</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>Record UUID</td> -<td>The unique ID for the message in the database.</td> -</tr> -<tr> -<td>Patient ID</td> -<td>The generated short patient ID for use in SMS.</td> -</tr> -<tr> -<td>Reported Date</td> -<td>The date the message was received or generated.</td> -</tr> -<tr> -<td>From</td> -<td>This phone number the message is or will be sent from.</td> -</tr> -<tr> -<td>Contact Name</td> -<td>The name of the user this message is assigned to.</td> -</tr> -<tr> -<td>Message Type</td> -<td>The type of the message</td> -</tr> -<tr> -<td>Message State</td> -<td>The state of the message at the time this export was generated</td> -</tr> -<tr> -<td>Received Timestamp</td> -<td>The datetime the message was received. Only applies to incoming messages.</td> -</tr> -<tr> -<td>Other Timestamps</td> -<td>The datetime the message transitioned to each state.</td> -</tr> -<tr> -<td>Sent By</td> -<td>The phone number the message was sent from. Only applies to incoming messages.</td> -</tr> -<tr> -<td>To Phone</td> -<td>The phone number the message is or will be sent to. Only applies to outgoing messages.</td> -</tr> -<tr> -<td>Message Body</td> -<td>The content of the message.</td> -</tr> -</tbody> -</table> -<h4 id="examples">Examples</h4> -<pre tabindex="0"><code>/api/v2/export/messages -</code></pre><h3 id="get-apiv2exportfeedback">GET /api/v2/export/feedback</h3> -<p>Export a file containing the user feedback.</p> -<h4 id="query-parameters-2">Query Parameters</h4> -<table> -<thead> -<tr> -<th>Variable</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>format</td> -<td>The format of the returned file, either &lsquo;csv&rsquo; or &lsquo;xml&rsquo;. Defaults to &lsquo;csv&rsquo;.</td> -</tr> -<tr> -<td>locale</td> -<td>Locale for translatable data. Defaults to &rsquo;en&rsquo;.</td> -</tr> -<tr> -<td>tz</td> -<td>The timezone to show date values in, as an offset in minutes from GMT, for example &lsquo;-120&rsquo;.</td> -</tr> -<tr> -<td>skip_header_row</td> -<td>&rsquo;true&rsquo; to omit the column headings. Defaults to &lsquo;false&rsquo;.</td> -</tr> -</tbody> -</table> -<h3 id="get-apiv2exportcontacts">GET /api/v2/export/contacts</h3> -<p>Returns a JSON array of contacts.</p> -<h4 id="output-1">Output</h4> -<table> -<thead> -<tr> -<th>Column</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>id</td> -<td>The unique ID for the contact in the database.</td> -</tr> -<tr> -<td>rev</td> -<td>The current CouchDb revision of contact in the database.</td> -</tr> -<tr> -<td>name</td> -<td>The name of the user this message is assigned to.</td> -</tr> -<tr> -<td>patient_id</td> -<td>The generated short patient ID for use in SMS.</td> -</tr> -<tr> -<td>type</td> -<td>The contact type. For configurable hierarchies, this will always be <code>contact</code>.</td> -</tr> -<tr> -<td>contact_type</td> -<td>The configurable contact type. Will be empty if using the default hierarchy.</td> -</tr> -<tr> -<td>place_id</td> -<td>The generated short place ID for use in SMS.</td> -</tr> -</tbody> -</table> -<h4 id="query-parameters-3">Query parameters</h4> -<p>These are identical to the JS objects passed to the shared library, as if you were using it directly in Javascript.</p> -<p>You may either pass JSON in the request body using <code>POST</code>:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#a40000">POST</span> <span style="color:#a40000">/api/v</span><span style="color:#0000cf;font-weight:bold">2</span><span style="color:#a40000">/export/contacts</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;filters&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;search&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;jim&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>Or using form-style parameters as <code>GET</code>:</p> -<pre tabindex="0"><code>GET /api/v2/export/contacts?filters[search]=jim -</code></pre><h3 id="get-apiv2exportuser-devices">GET /api/v2/export/user-devices</h3> -<p><em>Added in 4.7.0</em></p> -<p>Returns a JSON array of CHT-related software versions for each user device. This information is derived from the latest telemetry entry for each user device. If a particular user has used multiple devices, an entry will be included for <em>each</em> device. You can reference the <code>date</code> value to determine which devices have been <em>recently</em> used. If multiple users used the same physical device (e.g. they were logged into the same phone at different times), an entry will be included for <em>each</em> user.</p> -<h4 id="output-2">Output</h4> -<table> -<thead> -<tr> -<th>Column</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>user</td> -<td>The user&rsquo;s name.</td> -</tr> -<tr> -<td>deviceId</td> -<td>The unique key for the user&rsquo;s device.</td> -</tr> -<tr> -<td>date</td> -<td>The date the telemetry entry was taken in YYYY-MM-DD, see <a href="https://docs.communityhealthtoolkit.org/apps/guides/performance/telemetry/">relevant docs</a>.</td> -</tr> -<tr> -<td>browser.name</td> -<td>The name of the browser used.</td> -</tr> -<tr> -<td>browser.version</td> -<td>The version of the browser used.</td> -</tr> -<tr> -<td>apk</td> -<td>The Internal <a href="https://developer.android.com/reference/android/R.styleable#AndroidManifest_versionCode">version code</a> of the Android app.</td> -</tr> -<tr> -<td>android</td> -<td>The version of Android OS.</td> -</tr> -<tr> -<td>cht</td> -<td>The version of CHT the user was on at time the telemetry entry was generated.</td> -</tr> -<tr> -<td>settings</td> -<td>The revision of the App Settings document stored in CouchDB.</td> -</tr> -</tbody> -</table> -<h2 id="forms">Forms</h2> -<h3 id="get-apiv1forms">GET /api/v1/forms</h3> -<p>Returns a list of currently installed forms (in all available formats) in JSON format.</p> -<h4 id="headers">Headers</h4> -<table> -<thead> -<tr> -<th>Key</th> -<th>Value</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>X-OpenRosa-Version</td> -<td>1.0</td> -<td>If this header is specified returns XML formatted forms list. See <a href="https://bitbucket.org/javarosa/javarosa/wiki/FormListAPI">OpenRosa FormListAPI</a>.</td> -</tr> -</tbody> -</table> -<h4 id="examples-1">Examples</h4> -<p>Get list of forms currently installed.</p> -<pre tabindex="0"><code>GET /api/v1/forms -</code></pre><pre tabindex="0"><code>HTTP/1.1 200 -Content-Type: application/json; charset=utf-8 -[&#34;anc_visit.xml&#34;,&#34;anc_registration.xml&#34;,&#34;off.xml&#34;, &#34;off.json&#34;] -</code></pre><p>Get OpenRosa XForms compatible forms installed in XML format.</p> -<pre tabindex="0"><code>GET /api/v1/forms -Host: medic.local -X-OpenRosa-Version: 1.0 -</code></pre><pre tabindex="0"><code>HTTP/1.1 200 OK -Content-Type: text/xml; charset=utf-8 -X-OpenRosa-Version: 1.0 -&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?&gt; -&lt;xforms xmlns=&#34;http://openrosa.org/xforms/xformsList&#34;&gt; -&lt;xform&gt; -&lt;name&gt;Visit&lt;/name&gt; -&lt;formID&gt;ANCVisit&lt;/formID&gt; -&lt;hash&gt;md5:1f0f096602ed794a264ab67224608cf4&lt;/hash&gt; -&lt;downloadUrl&gt;http://medic.local/api/v1/forms/anc_visit.xml&lt;/downloadUrl&gt; -&lt;/xform&gt; -&lt;xform&gt; -&lt;name&gt;Registration with LMP&lt;/name&gt; -&lt;formID&gt;PregnancyRegistration&lt;/formID&gt; -&lt;hash&gt;md5:1f0f096602ed794a264ab67224608cf4&lt;/hash&gt; -&lt;downloadUrl&gt;http://medic.local/api/v1/forms/anc_registration.xml&lt;/downloadUrl&gt; -&lt;/xform&gt; -&lt;xform&gt; -&lt;name&gt;Stop&lt;/name&gt; -&lt;formID&gt;Stop&lt;/formID&gt; -&lt;hash&gt;md5:1f0f096602ed794a264ab67224608cf4&lt;/hash&gt; -&lt;downloadUrl&gt;http://medic.local/api/v1/forms/off.xml&lt;/downloadUrl&gt; -&lt;/xform&gt; -&lt;/xforms&gt; -</code></pre><h3 id="get-apiv1formsidformat">GET /api/v1/forms/{{id}}.{{format}}</h3> -<p>Return form definition for a given form ID and format.</p> -<h4 id="parameters">Parameters</h4> -<table> -<thead> -<tr> -<th>Variable</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>id</td> -<td>Form identifier</td> -</tr> -<tr> -<td>format</td> -<td>Format string or file extension. e.g. xml, json</td> -</tr> -</tbody> -</table> -<h4 id="examples-2">Examples</h4> -<p>Get latest version of the PregnancyRegistration form in xml (XForms) format.</p> -<pre tabindex="0"><code>GET /api/v1/forms/pregnancyregistration.xml -</code></pre><p>Get the latest version of the NPYY form in JSON format.</p> -<pre tabindex="0"><code>GET /api/v1/forms/NPYY.json -</code></pre><h3 id="post-apiv1formsvalidate">POST /api/v1/forms/validate</h3> -<p><em>Added in 3.12.0</em></p> -<p>Validate the XForm passed. Require the <code>can_configure</code> permission.</p> -<h4 id="headers-1">Headers</h4> -<table> -<thead> -<tr> -<th>Key</th> -<th>Value</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>Content-Type</td> -<td>application/xml</td> -<td>The form is sent in XML format</td> -</tr> -<tr> -<td>Authorization</td> -<td>Basic KEY</td> -<td>KEY is the &ldquo;basic&rdquo; token</td> -</tr> -</tbody> -</table> -<h4 id="examples-3">Examples</h4> -<pre tabindex="0"><code>POST /api/v1/forms/validate HTTP/1.1 -Content-Type: application/xml -Authorization: Basic XXXXXXXXXX -&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?&gt; -&lt;xforms xmlns=&#34;http://openrosa.org/xforms/xformsList&#34;&gt; -&lt;xform&gt; -&lt;name&gt;Visit&lt;/name&gt; -&lt;formID&gt;ANCVisit&lt;/formID&gt; -&lt;hash&gt;md5:1f0f096602ed794a264ab67224608cf4&lt;/hash&gt; -&lt;downloadUrl&gt;http://medic.local/api/v1/forms/anc_visit.xml&lt;/downloadUrl&gt; -&lt;/xform&gt; -&lt;xform&gt; -&lt;name&gt;Registration with LMP&lt;/name&gt; -&lt;formID&gt;PregnancyRegistration&lt;/formID&gt; -&lt;hash&gt;md5:1f0f096602ed794a264ab67224608cf4&lt;/hash&gt; -&lt;downloadUrl&gt;http://medic.local/api/v1/forms/anc_registration.xml&lt;/downloadUrl&gt; -&lt;/xform&gt; -&lt;xform&gt; -&lt;name&gt;Stop&lt;/name&gt; -&lt;formID&gt;Stop&lt;/formID&gt; -&lt;hash&gt;md5:1f0f096602ed794a264ab67224608cf4&lt;/hash&gt; -&lt;downloadUrl&gt;http://medic.local/api/v1/forms/off.xml&lt;/downloadUrl&gt; -&lt;/xform&gt; -&lt;/xforms&gt; -</code></pre><p>Example response when the form passed the validations:</p> -<pre tabindex="0"><code>HTTP/1.1 200 OK -Content-Type: application/json -{ok: true} -</code></pre><p>Example response when the form failed the validations:</p> -<pre tabindex="0"><code>HTTP/1.1 400 Bad Request -Content-Type: application/json -{error: &#34;Error transforming xml. xsltproc return ...&#34;} -</code></pre><h2 id="records">Records</h2> -<h3 id="post-apiv2records">POST /api/v2/records</h3> -<p>Create a new record based on a <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/forms/">JSON form</a> that has been configured.</p> -<p>Records can be created one of two ways, parsing the form data yourself and submitting a JSON object or by submitting the raw message string.</p> -<h4 id="headers-2">Headers</h4> -<table> -<thead> -<tr> -<th>Key</th> -<th>Value</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>Content-Type</td> -<td>application/x-www-form-urlencoded</td> -<td>Processes form parameters.</td> -</tr> -<tr> -<td>Content-Type</td> -<td>application/json</td> -<td>Processes form data in request body as JSON.</td> -</tr> -</tbody> -</table> -<p>Only one variant of the <code>Content-Type</code> header may be provided; RFC 2616 does not -allow multiple content types to appear in a single <code>Content-Type</code> header.</p> -<h5 id="form-parameters">Form Parameters</h5> -<table> -<thead> -<tr> -<th>Variable</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>message</td> -<td>Message string in a supported format like Muvuku or Textforms. Depending if your CHT instance is configured in forms-only mode or not you might receive an error if the form is not found.</td> -</tr> -<tr> -<td>from</td> -<td>Reporting phone number.</td> -</tr> -<tr> -<td>sent_timestamp</td> -<td>Timestamp in MS since Unix Epoch of when the message was received on the gateway. Defaults to now.</td> -</tr> -</tbody> -</table> -<h5 id="json-properties">JSON Properties</h5> -<p>Special values reside in the property <code>_meta</code>, so you can&rsquo;t have a form field named <code>_meta</code>. Only strings and numbers are currently support as field values.</p> -<p>All property names will be lowercased.</p> -<table> -<thead> -<tr> -<th>Key</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>_meta.form</td> -<td>The form code.</td> -</tr> -<tr> -<td>_meta.from</td> -<td>Reporting phone number. Optional.</td> -</tr> -<tr> -<td>_meta.reported_date</td> -<td>Timestamp in MS since Unix Epoch of when the message was received on the gateway. Defaults to now.</td> -</tr> -<tr> -<td>_meta.locale</td> -<td>Optional locale string. Example: &lsquo;fr&rsquo;</td> -</tr> -</tbody> -</table> -<h4 id="examples-4">Examples</h4> -<p>Creating new record using message field.</p> -<pre tabindex="0"><code>POST /api/v1/records -Content-Type: application/x-www-form-urlencoded -message=1!YYYZ!Sam#23#2015#ANC&amp;from=+5511943348031&amp;sent_timestamp=1352399720000 -</code></pre><pre tabindex="0"><code>HTTP/1.1 200 OK -Content-Type: application/json; charset=utf-8 -{ -&#34;success&#34;: true, -&#34;id&#34;: &#34;364c796a843fbe0a73476f9153012733&#34; -} -</code></pre><p>Creating new record with JSON.</p> -<pre tabindex="0"><code>POST /api/v1/records -Content-Type: application/json -{ -&#34;nurse&#34;: &#34;Sam&#34;, -&#34;week&#34;: 23, -&#34;year&#34;: 2015, -&#34;visit&#34;: &#34;ANC&#34;, -&#34;_meta&#34;: { -&#34;form&#34;: &#34;YYYZ&#34;, -&#34;reported_date&#34;: 1352399720000 -} -} -</code></pre><pre tabindex="0"><code>HTTP/1.1 200 OK -Content-Type: application/json; charset=utf-8 -{ -&#34;success&#34;: true, -&#34;id&#34;: &#34;364c796a843fbe0a73476f9153012733&#34; -} -</code></pre><h4 id="errors">Errors</h4> -<p>If required fields are not found return 500.</p> -<p>If invalid JSON return error response 500.</p> -<p>If submitting JSON and corresponding form is not found on the server you will receive an error.</p> -<h2 id="sms">SMS</h2> -<h3 id="post-apisms">POST /api/sms</h3> -<p>Endpoint used by cht-gateway to send sms messages. More documentation in the <a href="https://github.com/medic/cht-gateway/blob/master/README.md">cht-gateway repo</a>.</p> -<h3 id="post-apiv1smsaggregatorendpoint">POST /api/v1/sms/{aggregator}/{endpoint}</h3> -<p>Endpoint for integration with SMS aggregators. More details on the <a href="https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/rapidpro/">RapidPro</a> and <a href="https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/africas-talking/">Africa&rsquo;s Talking</a> pages.</p> -<h2 id="person">Person</h2> -<h3 id="get-apiv1personuuid">GET /api/v1/person/{{uuid}}</h3> -<p><em>Added in 4.9.0</em></p> -<p>Returns a person&rsquo;s data in JSON format.</p> -<h4 id="permissions">Permissions</h4> -<p><code>can_view_contacts</code></p> -<h4 id="query-parameters-4">Query parameters</h4> -<table> -<thead> -<tr> -<th>Name</th> -<th>Required</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>with_lineage</td> -<td>false</td> -<td>If &ldquo;true&rdquo;, the person&rsquo;s parent lineage will be included in the returned data. Default is &ldquo;false&rdquo;.</td> -</tr> -</tbody> -</table> -<h4 id="examples-5">Examples</h4> -<p>Get a person by uuid:</p> -<pre tabindex="0"><code>GET /api/v1/person/f512e1d8-841b-4bc1-8154-b6794755f45b -</code></pre><pre tabindex="0"><code>HTTP/1.1 200 OK -Content-Type: application/json; charset=utf-8 -{ -&#34;_id&#34;: &#34;f512e1d8-841b-4bc1-8154-b6794755f45b&#34;, -&#34;_rev&#34;: &#34;3-9dbc362b262f88d63f270fe06a94dfe8&#34;, -&#34;type&#34;: &#34;person&#34;, -&#34;name&#34;: &#34;Example CHW&#34;, -&#34;date_of_birth&#34;: &#34;2002-02-20&#34;, -&#34;phone&#34;: &#34;+254712345679&#34;, -&#34;sex&#34;: &#34;female&#34;, -&#34;role&#34;: &#34;chw&#34;, -&#34;reported_date&#34;: 1708453778059, -&#34;parent&#34;: { -&#34;_id&#34;: &#34;d9153705-4574-43c3-b945-71aa2164d1d6&#34;, -&#34;parent&#34;: { -&#34;_id&#34;: &#34;b935ef10-0339-4263-99fc-34d4f8d72891&#34; -} -}, -&#34;patient_id&#34;: &#34;74615&#34; -} -</code></pre><p>Get a person by uuid with lineage:</p> -<pre tabindex="0"><code>GET /api/v1/person/f512e1d8-841b-4bc1-8154-b6794755f45b?with_lineage=true -</code></pre><pre tabindex="0"><code>HTTP/1.1 200 OK -Content-Type: application/json; charset=utf-8 -{ -&#34;_id&#34;: &#34;f512e1d8-841b-4bc1-8154-b6794755f45b&#34;, -&#34;_rev&#34;: &#34;3-9dbc362b262f88d63f270fe06a94dfe8&#34;, -&#34;type&#34;: &#34;person&#34;, -&#34;name&#34;: &#34;Example CHW&#34;, -&#34;date_of_birth&#34;: &#34;2002-02-20&#34;, -&#34;phone&#34;: &#34;+254712345679&#34;, -&#34;sex&#34;: &#34;female&#34;, -&#34;role&#34;: &#34;chw&#34;, -&#34;reported_date&#34;: 1708453778059, -&#34;parent&#34;: { -&#34;_id&#34;: &#34;d9153705-4574-43c3-b945-71aa2164d1d6&#34;, -&#34;_rev&#34;: &#34;2-5f5fde4a8def0f40f89bd164d93bed4f&#34;, -&#34;parent&#34;: { -&#34;_id&#34;: &#34;b935ef10-0339-4263-99fc-34d4f8d72891&#34;, -&#34;_rev&#34;: &#34;2-bdea703bfec184085c31a6bab022764f&#34;, -&#34;type&#34;: &#34;district_hospital&#34;, -&#34;name&#34;: &#34;Example Health Facility&#34;, -&#34;contact&#34;: { -&#34;_id&#34;: &#34;e5237f20-2d28-4272-8006-c4903e032ab4&#34;, -&#34;_rev&#34;: &#34;3-5a0a8e95cef8bafc186a9494c75afb3c&#34;, -&#34;type&#34;: &#34;person&#34;, -&#34;name&#34;: &#34;Example Supervisor&#34;, -&#34;date_of_birth&#34;: &#34;2002-02-20&#34;, -&#34;phone&#34;: &#34;+254712345678&#34;, -&#34;sex&#34;: &#34;female&#34;, -&#34;role&#34;: &#34;chw_supervisor&#34;, -&#34;reported_date&#34;: 1708453756441, -&#34;parent&#34;: { -&#34;_id&#34;: &#34;b935ef10-0339-4263-99fc-34d4f8d72891&#34; -}, -&#34;patient_id&#34;: &#34;20424&#34; -}, -&#34;reported_date&#34;: 1708453756440, -&#34;place_id&#34;: &#34;54380&#34; -}, -&#34;type&#34;: &#34;health_center&#34;, -&#34;name&#34;: &#34;Example Health Center&#34;, -&#34;contact&#34;: { -&#34;_id&#34;: &#34;f512e1d8-841b-4bc1-8154-b6794755f45b&#34;, -&#34;_rev&#34;: &#34;3-9dbc362b262f88d63f270fe06a94dfe8&#34;, -&#34;type&#34;: &#34;person&#34;, -&#34;name&#34;: &#34;Example CHW&#34;, -&#34;date_of_birth&#34;: &#34;2002-02-20&#34;, -&#34;phone&#34;: &#34;+254712345679&#34;, -&#34;sex&#34;: &#34;female&#34;, -&#34;role&#34;: &#34;chw&#34;, -&#34;reported_date&#34;: 1708453778059, -&#34;parent&#34;: { -&#34;_id&#34;: &#34;d9153705-4574-43c3-b945-71aa2164d1d6&#34;, -&#34;parent&#34;: { -&#34;_id&#34;: &#34;b935ef10-0339-4263-99fc-34d4f8d72891&#34; -} -}, -&#34;patient_id&#34;: &#34;74615&#34; -}, -&#34;reported_date&#34;: 1708453778059, -&#34;place_id&#34;: &#34;17437&#34; -}, -&#34;patient_id&#34;: &#34;74615&#34; -} -</code></pre><h2 id="people">People</h2> -<h3 id="supported-properties">Supported Properties</h3> -<p>Use JSON in the request body to specify a person&rsquo;s details.</p> -<p>Note: this does not accommodate having a <code>place</code> field on your form and will likely be revised soon.</p> -<h4 id="required">Required</h4> -<table> -<thead> -<tr> -<th>Key</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>name</td> -<td>String used to describe the person.</td> -</tr> -<tr> -<td>type</td> -<td>ID of the <code>contact_type</code> for the new person. Defaults to &lsquo;person&rsquo; for backwards compatibility.</td> -</tr> -</tbody> -</table> -<h4 id="optional">Optional</h4> -<table> -<thead> -<tr> -<th>Key</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>place</td> -<td>String that references a place or object that defines a new place.</td> -</tr> -<tr> -<td>reported_date</td> -<td>Timestamp of when the record was reported or created. Defaults to now.</td> -</tr> -</tbody> -</table> -<h3 id="post-apiv1people">POST /api/v1/people</h3> -<p>Create new people.</p> -<h4 id="permissions-1">Permissions</h4> -<p>By default any user can create or modify a place. Use these permissions to restrict access:</p> -<p><code>can_create_people</code>, <code>can_create_places</code></p> -<h4 id="examples-6">Examples</h4> -<p>Create new person and place hierarchy.</p> -<pre tabindex="0"><code>POST /api/v1/people -Content-Type: application/json -{ -&#34;name&#34;: &#34;Hannah&#34;, -&#34;phone&#34;: &#34;+2548277210095&#34;, -&#34;type&#34;: &#34;contact&#34;, -&#34;contact_type&#34;: &#34;patient&#34;, -&#34;place&#34;: { -&#34;name&#34;: &#34;CHP Area One&#34;, -&#34;type&#34;: &#34;health_center&#34;, -&#34;parent&#34;: { -&#34;name&#34;: &#34;CHP Branch One&#34;, -&#34;type&#34;: &#34;district_hospital&#34; -} -} -} -</code></pre><p>Create new person and assign existing place.</p> -<pre tabindex="0"><code>POST /api/v1/people -Content-Type: application/json -{ -&#34;name&#34;: &#34;Samuel&#34;, -&#34;place&#34;: &#34;1d83f2b4a27eceb40df9e9f9ad06d137&#34;, -&#34;type&#34;: &#34;contact&#34;, -&#34;contact_type&#34;: &#34;chp&#34; -} -</code></pre><p>Example response:</p> -<pre tabindex="0"><code>HTTP/1.1 200 OK -Content-Type: application/json -{ -&#34;id&#34;: &#34;71df9d25ed6732ea3b4435862510d115&#34;, -&#34;rev&#34;: &#34;1-a4060843d78f46a60a6f41051e40e3b5&#34; -} -</code></pre><h2 id="place">Place</h2> -<h3 id="get-apiv1placeuuid">GET /api/v1/place/{{uuid}}</h3> -<p><em>Added in 4.10.0</em></p> -<p>Returns a place&rsquo;s data in JSON format.</p> -<h4 id="permissions-2">Permissions</h4> -<p><code>can_view_contacts</code></p> -<h4 id="query-parameters-5">Query parameters</h4> -<table> -<thead> -<tr> -<th>Name</th> -<th>Required</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>with_lineage</td> -<td>false</td> -<td>If &ldquo;true&rdquo;, the place&rsquo;s parent lineage will be included in the returned data. Default is &ldquo;false&rdquo;.</td> -</tr> -</tbody> -</table> -<h4 id="examples-7">Examples</h4> -<p>Get a place by uuid:</p> -<pre tabindex="0"><code>GET /api/v1/place/d9153705-4574-43c3-b945-71aa2164d1d6 -</code></pre><pre tabindex="0"><code>HTTP/1.1 200 OK -Content-Type: application/json; charset=utf-8 -{ -&#34;_id&#34;: &#34;d9153705-4574-43c3-b945-71aa2164d1d6&#34;, -&#34;_rev&#34;: &#34;2-5f5fde4a8def0f40f89bd164d93bed4f&#34;, -&#34;parent&#34;: { -&#34;_id&#34;: &#34;b935ef10-0339-4263-99fc-34d4f8d72891&#34; -}, -&#34;type&#34;: &#34;health_center&#34;, -&#34;name&#34;: &#34;Example Health Center&#34;, -&#34;contact&#34;: { -&#34;_id&#34;: &#34;f512e1d8-841b-4bc1-8154-b6794755f45b&#34;, -&#34;parent&#34;: { -&#34;_id&#34;: &#34;d9153705-4574-43c3-b945-71aa2164d1d6&#34;, -&#34;parent&#34;: { -&#34;_id&#34;: &#34;b935ef10-0339-4263-99fc-34d4f8d72891&#34; -} -} -}, -&#34;reported_date&#34;: 1708453778059, -&#34;place_id&#34;: &#34;17437&#34; -} -</code></pre><p>Get a place by uuid with lineage:</p> -<pre tabindex="0"><code>GET /api/v1/place/d9153705-4574-43c3-b945-71aa2164d1d6?with_lineage=true -</code></pre><pre tabindex="0"><code>HTTP/1.1 200 OK -Content-Type: application/json; charset=utf-8 -{ -&#34;_id&#34;: &#34;d9153705-4574-43c3-b945-71aa2164d1d6&#34;, -&#34;_rev&#34;: &#34;2-5f5fde4a8def0f40f89bd164d93bed4f&#34;, -&#34;parent&#34;: { -&#34;_id&#34;: &#34;b935ef10-0339-4263-99fc-34d4f8d72891&#34;, -&#34;_rev&#34;: &#34;2-bdea703bfec184085c31a6bab022764f&#34;, -&#34;parent&#34;: &#34;&#34;, -&#34;type&#34;: &#34;district_hospital&#34;, -&#34;name&#34;: &#34;Example Health Facility&#34;, -&#34;contact&#34;: { -&#34;_id&#34;: &#34;e5237f20-2d28-4272-8006-c4903e032ab4&#34;, -&#34;_rev&#34;: &#34;3-5a0a8e95cef8bafc186a9494c75afb3c&#34;, -&#34;type&#34;: &#34;person&#34;, -&#34;name&#34;: &#34;Example Supervisor&#34;, -&#34;date_of_birth&#34;: &#34;2002-02-20&#34;, -&#34;phone&#34;: &#34;+254712345678&#34;, -&#34;sex&#34;: &#34;female&#34;, -&#34;role&#34;: &#34;chw_supervisor&#34;, -&#34;reported_date&#34;: 1708453756441, -&#34;parent&#34;: { -&#34;_id&#34;: &#34;b935ef10-0339-4263-99fc-34d4f8d72891&#34; -}, -&#34;patient_id&#34;: &#34;20424&#34; -}, -&#34;reported_date&#34;: 1708453756440, -&#34;place_id&#34;: &#34;54380&#34; -}, -&#34;type&#34;: &#34;health_center&#34;, -&#34;name&#34;: &#34;Example Health Center&#34;, -&#34;contact&#34;: { -&#34;_id&#34;: &#34;f512e1d8-841b-4bc1-8154-b6794755f45b&#34;, -&#34;_rev&#34;: &#34;3-9dbc362b262f88d63f270fe06a94dfe8&#34;, -&#34;type&#34;: &#34;person&#34;, -&#34;name&#34;: &#34;Example CHW&#34;, -&#34;date_of_birth&#34;: &#34;2002-02-20&#34;, -&#34;phone&#34;: &#34;+254712345679&#34;, -&#34;sex&#34;: &#34;female&#34;, -&#34;role&#34;: &#34;chw&#34;, -&#34;reported_date&#34;: 1708453778059, -&#34;parent&#34;: { -&#34;_id&#34;: &#34;d9153705-4574-43c3-b945-71aa2164d1d6&#34;, -&#34;parent&#34;: { -&#34;_id&#34;: &#34;b935ef10-0339-4263-99fc-34d4f8d72891&#34; -} -}, -&#34;patient_id&#34;: &#34;74615&#34; -}, -&#34;reported_date&#34;: 1708453778059, -&#34;place_id&#34;: &#34;17437&#34; -} -</code></pre><h2 id="places">Places</h2> -<p>By default any user can create or modify a place.</p> -<h3 id="supported-properties-1">Supported Properties</h3> -<p>Use JSON in the request body to specify a place&rsquo;s details.</p> -<h4 id="required-properties">Required Properties</h4> -<table> -<thead> -<tr> -<th>Key</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>name</td> -<td>String used to describe the place.</td> -</tr> -<tr> -<td>type</td> -<td>Place type</td> -</tr> -<tr> -<td>parent</td> -<td>String that references a place or object that defines a new place. Optional for District Hospital and National Office types.</td> -</tr> -</tbody> -</table> -<h4 id="optional-properties">Optional Properties</h4> -<table> -<thead> -<tr> -<th>Key</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>contact</td> -<td>String identifier for a person or object that defines a new person.</td> -</tr> -<tr> -<td>reported_date</td> -<td>Timestamp of when the record was reported or created. Defaults to now.</td> -</tr> -</tbody> -</table> -<h4 id="place-types">Place Types</h4> -<table> -<thead> -<tr> -<th>Key</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>clinic</td> -<td>Clinic</td> -</tr> -<tr> -<td>health_center</td> -<td>Health Center</td> -</tr> -<tr> -<td>district_hospital</td> -<td>District Hospital</td> -</tr> -<tr> -<td>national_office</td> -<td>National Office</td> -</tr> -</tbody> -</table> -<h3 id="post-apiv1places">POST /api/v1/places</h3> -<p>Create a new place and optionally a contact.</p> -<h4 id="permissions-3">Permissions</h4> -<p>By default any user can create new places. Use these permissions to restrict access:</p> -<p><code>can_create_places</code>, <code>can_create_people</code></p> -<h4 id="examples-8">Examples</h4> -<p>Create new place referencing existing parent.</p> -<pre tabindex="0"><code>POST /api/v1/places -Content-Type: application/json -{ -&#34;name&#34;: &#34;Busia Clinic&#34;, -&#34;type&#34;: &#34;clinic&#34;, -&#34;parent&#34;: &#34;1d83f2b4a27eceb40df9e9f9ad06d137&#34; -} -</code></pre><p>Create child and parent places.</p> -<pre tabindex="0"><code>POST /api/v1/places -Content-Type: application/json -{ -&#34;name&#34;: &#34;CHP Area One&#34;, -&#34;type&#34;: &#34;health_center&#34;, -&#34;parent&#34;: { -&#34;name&#34;: &#34;CHP Branch One&#34;, -&#34;type&#34;: &#34;district_hospital&#34; -} -} -</code></pre><p>Also creates contact (person).</p> -<pre tabindex="0"><code>POST /api/v1/places -Content-Type: application/json -{ -&#34;name&#34;: &#34;CHP Area One&#34;, -&#34;type&#34;: &#34;health_center&#34;, -&#34;parent&#34;: { -&#34;name&#34;: &#34;CHP Branch One&#34;, -&#34;type&#34;: &#34;district_hospital&#34; -}, -&#34;contact&#34;: { -&#34;name&#34;: &#34;Paul&#34;, -&#34;phone&#34;: &#34;+254883720611&#34; -} -} -</code></pre><p>Or assigns them.</p> -<pre tabindex="0"><code>POST /api/v1/places -Content-Type: application/json -{ -&#34;name&#34;: &#34;CHP Area One&#34;, -&#34;type&#34;: &#34;health_center&#34;, -&#34;parent&#34;: { -&#34;name&#34;: &#34;CHP Branch One&#34;, -&#34;type&#34;: &#34;district_hospital&#34; -}, -&#34;contact&#34;: &#34;71df9d25ed6732ea3b4435862510ef8e&#34; -} -</code></pre><p>Example success response:</p> -<pre tabindex="0"><code>HTTP/1.1 200 OK -Content-Type: application/json -{ -&#34;id&#34;: &#34;71df9d25ed6732ea3b4435862510d115&#34;, -&#34;rev&#34;: &#34;1-a4060843d78f46a60a6f41051e40e3b5&#34; -} -</code></pre><p>Error response if facility structure is not correct:</p> -<pre tabindex="0"><code>HTTP/1.1 400 Bad Request -Content-Type: text/plain -Health Centers should have &#34;district_hospital&#34; parent type. -</code></pre><h3 id="post-apiv1placesid">POST /api/v1/places/{{id}}</h3> -<p>Update a place and optionally its contact.</p> -<h4 id="permissions-4">Permissions</h4> -<p>By default any user can update a place. Use these permissions to restrict access:</p> -<p><code>can_update_places</code></p> -<h4 id="examples-9">Examples</h4> -<p>Update a place&rsquo;s contact.</p> -<pre tabindex="0"><code>POST /api/v1/places/1d83f2b4a27eceb40df9e9f9ad06d137 -Content-Type: application/json -{ -&#34;contact&#34;: &#34;71df9d25ed6732ea3b4435862505f7a9&#34; -} -</code></pre><pre tabindex="0"><code>HTTP/1.1 200 OK -Content-Type: application/json -{ -&#34;id&#34;: &#34;1d83f2b4a27eceb40df9e9f9ad06d137&#34;, -&#34;rev&#34;: &#34;12-a4060843d78f46a60a6f41051e40e3b5&#34; -} -</code></pre><h2 id="users">Users</h2> -<p>All user related requests are limited to users with admin privileges by default.</p> -<h3 id="supported-properties-2">Supported Properties</h3> -<p>Use JSON in the request body to specify user details. Any properties submitted -that are not on the list below will be ignored. Any properties not included -will be undefined.</p> -<table> -<thead> -<tr> -<th>Name</th> -<th>Required</th> -<th>Type</th> -<th>Description</th> -<th>Version</th> -</tr> -</thead> -<tbody> -<tr> -<td>username</td> -<td>yes</td> -<td>String</td> -<td>identifier used for authentication</td> -<td></td> -</tr> -<tr> -<td>roles</td> -<td>yes</td> -<td>Array</td> -<td></td> -<td></td> -</tr> -<tr> -<td>place</td> -<td>yes, if the roles contain an offline role</td> -<td>string or object</td> -<td>Place identifier string (UUID) or object this user resides in.</td> -<td></td> -</tr> -<tr> -<td>contact</td> -<td>yes, if the roles contain an offline role</td> -<td>string or object</td> -<td>A person identifier string (UUID) or object based on the form configured in the app.</td> -<td></td> -</tr> -<tr> -<td>password</td> -<td>yes, if <code>token_login</code> is not enabled for the user</td> -<td>String</td> -<td>Password string used for authentication. Only allowed to be set, not retrieved.</td> -<td></td> -</tr> -<tr> -<td>phone</td> -<td>yes, if <code>token_login</code> is enabled for the user</td> -<td>String</td> -<td>Valid phone number</td> -<td></td> -</tr> -<tr> -<td>token_login</td> -<td>no</td> -<td>Boolean</td> -<td>A boolean representing whether or not the Login by SMS should be enabled for this user.</td> -<td>3.10.0</td> -</tr> -<tr> -<td>fullname</td> -<td>no</td> -<td>String</td> -<td>Full name</td> -<td></td> -</tr> -<tr> -<td>email</td> -<td>no</td> -<td>String</td> -<td>Email address</td> -<td></td> -</tr> -<tr> -<td>known</td> -<td>no</td> -<td>Boolean</td> -<td>Boolean to define if the user has logged in before.</td> -<td></td> -</tr> -</tbody> -</table> -<h4 id="login-by-sms">Login by SMS</h4> -<p>When creating or updating a user, sending a truthy value for the field <code>token_login</code> will enable Login by SMS for this user. -This action resets the user&rsquo;s password to an unknown string and generates a complex 64 character token, that is used to generate a token-login URL. -The URL is sent to the user&rsquo;s phone number by SMS, along with another (configurable) SMS that can contain additional information. -Accessing this link, before its expiration time, will log the user in directly - without the need of any other credentials. -The link can only be accessed once, the token becomes invalid after being used for one login. -The token expires in 24 hours, after which logging in is only possible by either generating a new token, or disabling <code>token_login</code> and manually setting a password.</p> -<p>The SMS messages are stored in a doc of a new type <code>login_token</code>. These docs cannot be viewed as reports from the webapp, and can only be edited by admins, but their messages are visible in the Admin Message Queue page.</p> -<p>To disable login by SMS for a user, update the user sending <code>token_login</code> with a <code>false</code> value. -To regenerate the token, update the user sending <code>token_login</code> with a <code>true</code> value.</p> -<table> -<thead> -<tr> -<th><code>token_login</code></th> -<th>user state</th> -<th>action</th> -</tr> -</thead> -<tbody> -<tr> -<td>undefined</td> -<td>new</td> -<td>None</td> -</tr> -<tr> -<td>undefined</td> -<td>existent, no token</td> -<td>None</td> -</tr> -<tr> -<td>undefined</td> -<td>existent, with token</td> -<td>None. Login by SMS remains enabled. Token is unchanged.</td> -</tr> -<tr> -<td>true</td> -<td>new</td> -<td>Login by SMS enabled. Token is generated and SMS is sent.</td> -</tr> -<tr> -<td>true</td> -<td>existent, no token</td> -<td>Password is reset. Login by SMS enabled. Token is generated and SMS is sent. Existent sessions are invalidated.</td> -</tr> -<tr> -<td>true</td> -<td>existent, with token</td> -<td>Password is reset. Login by SMS enabled. New token is generated and SMS is sent. Old token is invalid. Existent sessions are invalidated.</td> -</tr> -<tr> -<td>false</td> -<td>new</td> -<td>None.</td> -</tr> -<tr> -<td>false</td> -<td>existent, no token</td> -<td>None.</td> -</tr> -<tr> -<td>false</td> -<td>existent, with token</td> -<td>Request requires a password. Login by SMS is disabled. Old token is invalidated. Existent sessions are invalidated.</td> -</tr> -</tbody> -</table> -<p>This feature uses <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/#app_settingsjson"><code>app_settings.app_url</code></a> and <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/token_login/"><code>app_settings.token_login</code></a> to be defined and enabled. -If <code>app_settings.app_url</code> is not defined, the generated token-login URL will use the <code>Host</code> request header, which may not always be correct.</p> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/concepts/access/#remote-login">Accessing CHT Apps</a></p> -<h3 id="get-apiv1users">GET /api/v1/users</h3> -<p><em>DEPRECATED: use <a href="#get-apiv2users">/api/v2/users</a></em></p> -<p>Returns a list of users and their profile data in JSON format.</p> -<h4 id="permissions-5">Permissions</h4> -<p><code>can_view_users</code></p> -<h4 id="examples-10">Examples</h4> -<p>Get list of users:</p> -<pre tabindex="0"><code>GET /api/v1/users -</code></pre><pre tabindex="0"><code>HTTP/1.1 200 OK -Content-Type: application/json; charset=utf-8 -[ -{ -&#34;id&#34;: &#34;org.couchdb.user:admin&#34;, -&#34;rev&#34;: &#34;10-6486428924d11781c107ea74de6b63b6&#34;, -&#34;type&#34;: &#34;admin&#34;, -&#34;username&#34;: &#34;admin&#34; -}, -{ -&#34;id&#34;: &#34;org.couchdb.user:demo&#34;, -&#34;rev&#34;: &#34;14-8758c8493edcc6dac50366173fc3e24a&#34;, -&#34;type&#34;: &#34;district-manager&#34;, -&#34;fullname&#34;: &#34;Example User&#34;, -&#34;username&#34;: &#34;demo&#34;, -&#34;place&#34;: { -&#34;_id&#34;: &#34;eeb17d6d-5dde-c2c0-62c4a1a0ca17d38b&#34;, -&#34;type&#34;: &#34;district_hospital&#34;, -&#34;name&#34;: &#34;Sample District&#34;, -&#34;contact&#34;: { -&#34;_id&#34;: &#34;eeb17d6d-5dde-c2c0-62c4a1a0ca17fd17&#34;, -&#34;type&#34;: &#34;person&#34;, -&#34;name&#34;: &#34;Paul&#34;, -&#34;phone&#34;: &#34;+2868917046&#34; -} -}, -&#34;contact&#34;: { -&#34;_id&#34;: &#34;eeb17d6d-5dde-c2c0-62c4a1a0ca17fd17&#34;, -&#34;type&#34;: &#34;person&#34;, -&#34;name&#34;: &#34;Paul&#34;, -&#34;phone&#34;: &#34;+2868917046&#34; -} -} -] -</code></pre><h3 id="get-apiv2users">GET /api/v2/users</h3> -<p><em>Added in 4.1.0</em></p> -<p>Returns a list of users and their profile data in JSON format.</p> -<h4 id="permissions-6">Permissions</h4> -<p><code>can_view_users</code></p> -<h4 id="query-parameters-6">Query Parameters</h4> -<table> -<thead> -<tr> -<th>Variable</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>facility_id</td> -<td>Added in 4.7.0. String identifier representing the uuid of the user’s facility.</td> -</tr> -<tr> -<td>contact_id</td> -<td>Added in 4.7.0. String identifier representing the uuid of the user’s associated contact.</td> -</tr> -</tbody> -</table> -<h4 id="examples-11">Examples</h4> -<p>Get list of users:</p> -<pre tabindex="0"><code>GET /api/v2/users -</code></pre><pre tabindex="0"><code>HTTP/1.1 200 OK -Content-Type: application/json; charset=utf-8 -[ -{ -&#34;id&#34;: &#34;org.couchdb.user:admin&#34;, -&#34;rev&#34;: &#34;10-6486428924d11781c107ea74de6b63b6&#34;, -&#34;roles&#34;: [ &#34;admin&#34; ], -&#34;username&#34;: &#34;admin&#34; -}, -{ -&#34;id&#34;: &#34;org.couchdb.user:demo&#34;, -&#34;rev&#34;: &#34;14-8758c8493edcc6dac50366173fc3e24a&#34;, -&#34;roles&#34;: [ &#34;district_admin&#34;, &#34;data_user&#34; ], -&#34;fullname&#34;: &#34;Example User&#34;, -&#34;username&#34;: &#34;demo&#34;, -&#34;place&#34;: { -&#34;_id&#34;: &#34;eeb17d6d-5dde-c2c0-62c4a1a0ca17d38b&#34;, -&#34;type&#34;: &#34;district_hospital&#34;, -&#34;name&#34;: &#34;Sample District&#34;, -&#34;contact&#34;: { -&#34;_id&#34;: &#34;eeb17d6d-5dde-c2c0-62c4a1a0ca17fd17&#34;, -&#34;type&#34;: &#34;person&#34;, -&#34;name&#34;: &#34;Paul&#34;, -&#34;phone&#34;: &#34;+2868917046&#34; -} -}, -&#34;contact&#34;: { -&#34;_id&#34;: &#34;eeb17d6d-5dde-c2c0-62c4a1a0ca17fd17&#34;, -&#34;type&#34;: &#34;person&#34;, -&#34;name&#34;: &#34;Paul&#34;, -&#34;phone&#34;: &#34;+2868917046&#34; -} -} -] -</code></pre><p>Get users with a specific <code>facility_id</code>:</p> -<pre tabindex="0"><code>GET /api/v2/users?facility_id=eeb17d6d-5dde-c2c0-62c4a1a0ca17d38b -</code></pre><pre tabindex="0"><code>HTTP/1.1 200 OK -Content-Type: application/json; charset=utf-8 -[ -{ -&#34;id&#34;: &#34;org.couchdb.user:demo&#34;, -&#34;rev&#34;: &#34;14-8758c8493edcc6dac50366173fc3e24a&#34;, -&#34;roles&#34;: [ &#34;district_admin&#34;, &#34;data_user&#34; ], -&#34;fullname&#34;: &#34;Example User&#34;, -&#34;username&#34;: &#34;demo&#34;, -&#34;place&#34;: { -&#34;_id&#34;: &#34;eeb17d6d-5dde-c2c0-62c4a1a0ca17d38b&#34;, -&#34;type&#34;: &#34;district_hospital&#34;, -&#34;name&#34;: &#34;Sample District&#34;, -&#34;contact&#34;: { -&#34;_id&#34;: &#34;eeb17d6d-5dde-c2c0-62c4a1a0ca17fd17&#34;, -&#34;type&#34;: &#34;person&#34;, -&#34;name&#34;: &#34;Paul&#34;, -&#34;phone&#34;: &#34;+2868917046&#34; -} -}, -&#34;contact&#34;: { -&#34;_id&#34;: &#34;eeb17d6d-5dde-c2c0-62c4a1a0ca17fd17&#34;, -&#34;type&#34;: &#34;person&#34;, -&#34;name&#34;: &#34;Paul&#34;, -&#34;phone&#34;: &#34;+2868917046&#34; -} -} -] -</code></pre><p>Get users with a specific <code>contact_id</code>:</p> -<pre tabindex="0"><code>GET /api/v2/users?contact_id=eeb17d6d-5dde-c2c0-62c4a1a0ca17fd17 -</code></pre><pre tabindex="0"><code>HTTP/1.1 200 OK -Content-Type: application/json; charset=utf-8 -[ -{ -&#34;id&#34;: &#34;org.couchdb.user:demo&#34;, -&#34;rev&#34;: &#34;14-8758c8493edcc6dac50366173fc3e24a&#34;, -&#34;roles&#34;: [ &#34;district_admin&#34;, &#34;data_user&#34; ], -&#34;fullname&#34;: &#34;Example User&#34;, -&#34;username&#34;: &#34;demo&#34;, -&#34;place&#34;: { -&#34;_id&#34;: &#34;eeb17d6d-5dde-c2c0-62c4a1a0ca17d38b&#34;, -&#34;type&#34;: &#34;district_hospital&#34;, -&#34;name&#34;: &#34;Sample District&#34;, -&#34;contact&#34;: { -&#34;_id&#34;: &#34;eeb17d6d-5dde-c2c0-62c4a1a0ca17fd17&#34;, -&#34;type&#34;: &#34;person&#34;, -&#34;name&#34;: &#34;Paul&#34;, -&#34;phone&#34;: &#34;+2868917046&#34; -} -}, -&#34;contact&#34;: { -&#34;_id&#34;: &#34;eeb17d6d-5dde-c2c0-62c4a1a0ca17fd17&#34;, -&#34;type&#34;: &#34;person&#34;, -&#34;name&#34;: &#34;Paul&#34;, -&#34;phone&#34;: &#34;+2868917046&#34; -} -} -] -</code></pre><h3 id="get-apiv2usersusername">GET /api/v2/users/{{username}}</h3> -<p><em>Added in 4.7.0</em></p> -<p>Returns a user&rsquo;s profile data in JSON format.</p> -<h4 id="permissions-7">Permissions</h4> -<p><code>can_view_users</code></p> -<h4 id="examples-12">Examples</h4> -<p>Get a user by username:</p> -<pre tabindex="0"><code>GET /api/v2/users/demo -</code></pre><pre tabindex="0"><code>HTTP/1.1 200 OK -Content-Type: application/json; charset=utf-8 -{ -&#34;id&#34;: &#34;org.couchdb.user:demo&#34;, -&#34;rev&#34;: &#34;14-8758c8493edcc6dac50366173fc3e24a&#34;, -&#34;type&#34;: &#34;district-manager&#34;, -&#34;fullname&#34;: &#34;Example User&#34;, -&#34;username&#34;: &#34;demo&#34;, -&#34;place&#34;: { -&#34;_id&#34;: &#34;eeb17d6d-5dde-c2c0-62c4a1a0ca17d38b&#34;, -&#34;type&#34;: &#34;district_hospital&#34;, -&#34;name&#34;: &#34;Sample District&#34;, -&#34;contact&#34;: { -&#34;_id&#34;: &#34;eeb17d6d-5dde-c2c0-62c4a1a0ca17fd17&#34;, -&#34;type&#34;: &#34;person&#34;, -&#34;name&#34;: &#34;Paul&#34;, -&#34;phone&#34;: &#34;+2868917046&#34; -} -}, -&#34;contact&#34;: { -&#34;_id&#34;: &#34;eeb17d6d-5dde-c2c0-62c4a1a0ca17fd17&#34;, -&#34;type&#34;: &#34;person&#34;, -&#34;name&#34;: &#34;Paul&#34;, -&#34;phone&#34;: &#34;+2868917046&#34; -} -} -</code></pre><h3 id="post-apiv1users">POST /api/v1/users</h3> -<p>Create new users with a place and a contact.</p> -<p>Creating multiple users at once by passing an array of users was introduced in version 3.15. -All users need to meet the following requirements before any of them are created:</p> -<ul> -<li>All required fields are filled in</li> -<li>The password is at least 8 characters long and difficult to guess</li> -<li>The phone number is valid when <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/token_login/"><code>token_login</code></a> is enabled</li> -</ul> -<p>Users are created in parallel and the creation is not aborted even if one of the users fails to be created.</p> -<p>Passing a single user in the request&rsquo;s body will return a single object whereas -passing an array of users will return an array of objects as shown in the examples below.</p> -<h4 id="permissions-8">Permissions</h4> -<p><code>can_create_users</code></p> -<h4 id="examples-13">Examples</h4> -<h5 id="create-one-user">Create one user</h5> -<p>Create a new user that can authenticate with a username of &ldquo;mary&rdquo; and password -of &ldquo;secret&rdquo; that can submit reports and view or modify records associated to -their place. The place is created in the background and automatically linked -to the contact.</p> -<pre tabindex="0"><code>POST /api/v1/users -Content-Type: application/json -{ -&#34;password&#34;: &#34;secret&#34;, -&#34;username&#34;: &#34;mary&#34;, -&#34;type&#34;: &#34;district-manager&#34;, -&#34;place&#34;: { -&#34;name&#34;: &#34;Mary&#39;s Area&#34;, -&#34;type&#34;: &#34;health_center&#34;, -&#34;parent&#34;: &#34;d14e1c3d557761320b13a77e7806e8f8&#34; -}, -&#34;contact&#34;: { -&#34;name&#34;: &#34;Mary Anyango&#34;, -&#34;phone&#34;: &#34;+2868917046&#34; -} -} -</code></pre><pre tabindex="0"><code>HTTP/1.1 200 -Content-Type: application/json -{ -&#34;contact&#34;: { -&#34;id&#34;: &#34;65416b8ceb53ff88ac1847654501aeb3&#34;, -&#34;rev&#34;: &#34;1-0b74d219ae13137c1a06f03a0a52e187&#34; -}, -&#34;user-settings&#34;: { -&#34;id&#34;: &#34;org.couchdb.user:mary&#34;, -&#34;rev&#34;: &#34;1-6ac1d36b775143835f4af53f9895d7ae&#34; -}, -&#34;user&#34;: { -&#34;id&#34;: &#34;org.couchdb.user:mary&#34;, -&#34;rev&#34;: &#34;1-c3b82a0b47cfe68edd9284c89bebbae4&#34; -} -} -</code></pre><h5 id="create-many-users">Create many users</h5> -<p>Create two new users that can authenticate with a username and a password -that can submit reports and view or modify records associated to their place. -The place is created in the background and automatically linked to the contact.</p> -<pre tabindex="0"><code>POST /api/v1/users -Content-Type: application/json -[ -{ -&#34;password&#34;: &#34;secret&#34;, -&#34;username&#34;: &#34;mary&#34;, -&#34;type&#34;: &#34;district-manager&#34;, -&#34;place&#34;: { -&#34;name&#34;: &#34;Mary&#39;s Area&#34;, -&#34;type&#34;: &#34;health_center&#34;, -&#34;parent&#34;: &#34;d14e1c3d557761320b13a77e7806e8f8&#34; -}, -&#34;contact&#34;: { -&#34;name&#34;: &#34;Mary Anyango&#34;, -&#34;phone&#34;: &#34;+2868917046&#34; -} -}, -{ -&#34;password&#34;: &#34;secret&#34;, -&#34;username&#34;: &#34;bob&#34;, -&#34;type&#34;: &#34;district-manager&#34;, -&#34;place&#34;: { -&#34;name&#34;: &#34;Bob&#39;s Area&#34;, -&#34;type&#34;: &#34;health_center&#34;, -&#34;parent&#34;: &#34;d14e1c3d557761320b13a77e7806e8f8&#34; -}, -&#34;contact&#34;: { -&#34;name&#34;: &#34;Bob Johnson&#34;, -&#34;phone&#34;: &#34;+2868194607&#34; -} -} -] -</code></pre><pre tabindex="0"><code>HTTP/1.1 200 -Content-Type: application/json -[ -{ -&#34;contact&#34;: { -&#34;id&#34;: &#34;65416b8ceb53ff88ac1847654501aeb3&#34;, -&#34;rev&#34;: &#34;1-0b74d219ae13137c1a06f03a0a52e187&#34; -}, -&#34;user-settings&#34;: { -&#34;id&#34;: &#34;org.couchdb.user:mary&#34;, -&#34;rev&#34;: &#34;1-6ac1d36b775143835f4af53f9895d7ae&#34; -}, -&#34;user&#34;: { -&#34;id&#34;: &#34;org.couchdb.user:mary&#34;, -&#34;rev&#34;: &#34;1-c3b82a0b47cfe68edd9284c89bebbae4&#34; -} -}, -{ -&#34;contact&#34;: { -&#34;id&#34;: &#34;8d8a741c1cb441058e29a60ab7596bf2&#34;, -&#34;rev&#34;: &#34;1-acbc31712fd105eae3cd0806cd20a8f4&#34; -}, -&#34;user-settings&#34;: { -&#34;id&#34;: &#34;org.couchdb.user:bob&#34;, -&#34;rev&#34;: &#34;1-d8629838127accd531043f845c416ef6&#34; -}, -&#34;user&#34;: { -&#34;id&#34;: &#34;org.couchdb.user:bob&#34;, -&#34;rev&#34;: &#34;1-5eac2542c801ba6b518728f53d9276a0&#34; -} -} -] -</code></pre><h4 id="errors-1">Errors</h4> -<h5 id="response-when-the-username-already-exists">Response when the username already exists</h5> -<pre tabindex="0"><code>HTTP/1.1 400 Bad Request -Content-Type: application/json -{ -&#34;code&#34;: 400, -&#34;error&#34;: { -&#34;message&#34;: &#34;Username \&#34;mary\&#34; already taken.&#34;, -&#34;translationKey&#34;: &#34;username.taken&#34;, -&#34;translationParams&#34;: { -&#34;username&#34;: &#34;mary&#34; -} -} -} -</code></pre><h5 id="response-when-users-are-missing-required-fields">Response when users are missing required fields</h5> -<pre tabindex="0"><code>HTTP/1.1 400 Bad Request -Content-Type: application/json -{ -&#34;code&#34;: 400, -&#34;error&#34;: &#34;Missing required fields:\n\Missing fields username, password, type or roles for user at index 0\nMissing fields password, type or roles for user at index 1&#34;, -&#34;details&#34;: { -&#34;failingIndexes&#34;: [ -{ -&#34;fields&#34;: [ -&#34;username&#34;, -&#34;password&#34;, -&#34;type or roles&#34; -], -&#34;index&#34;: 0 -}, -{ -&#34;fields&#34;: [ -&#34;password&#34;, -&#34;type or roles&#34; -], -&#34;index&#34;: 1 -} -] -} -} -</code></pre><h5 id="response-when-some-users-failed-to-be-created">Response when some users failed to be created</h5> -<pre tabindex="0"><code>HTTP/1.1 200 -Content-Type: application/json -[ -{ -&#34;contact&#34;: { -&#34;id&#34;: &#34;65416b8ceb53ff88ac1847654501aeb3&#34;, -&#34;rev&#34;: &#34;1-0b74d219ae13137c1a06f03a0a52e187&#34; -}, -&#34;user-settings&#34;: { -&#34;id&#34;: &#34;org.couchdb.user:mary&#34;, -&#34;rev&#34;: &#34;1-6ac1d36b775143835f4af53f9895d7ae&#34; -}, -&#34;user&#34;: { -&#34;id&#34;: &#34;org.couchdb.user:mary&#34;, -&#34;rev&#34;: &#34;1-c3b82a0b47cfe68edd9284c89bebbae4&#34; -} -}, -{ -&#34;error&#34;: &#34;Failed to find place.&#34; -} -] -</code></pre><h3 id="post-apiv2users">POST /api/v2/users</h3> -<p><em>Added in 3.16.0</em></p> -<p>Create new users with a place and a contact from a CSV file.</p> -<p>Creating users from a CSV file behaves the same as passing a JSON array of users into the <a href="https://docs.communityhealthtoolkit.org/apps/reference/api/#post-apiv1users"><code>POST /api/v1/users</code></a> -where a row represents a user object and a column represents a user object property. -Columns with a <code>:excluded</code> suffix will be ignored, this allows providing a more user-friendly experience with -autocompletion on fields or dealing with names instead of long, unreadable ids.</p> -<p>In order to facilitate this process, we have made available a spreadsheet compatible with the <code>default</code> configuration of the CHT. -<a href="https://docs.google.com/spreadsheets/d/1yUenFP-5deQ0I9c-OYDTpbKYrkl3juv9djXoLLPoQ7Y/copy">Click here</a> to make a copy of the spreadsheet in Google Sheets. -<a href="https://docs.communityhealthtoolkit.org/apps/guides/data/users-bulk-load/">A guide</a> on how to import users with this spreadsheet from within the Admin Console is available -in case you don&rsquo;t want to interact with this API yourself.</p> -<h4 id="logging">Logging</h4> -<p>A log entry is created with each bulk import that contains the import status for each user and the import progress status -that gets updated throughout the import and finalized upon completion. -These entries are saved in the <a href="https://docs.communityhealthtoolkit.org/apps/guides/database/#medic-logs"><code>medic-logs</code></a> database and you can access them -by querying documents with a key that starts with <code>bulk-user-upload-</code>.</p> -<h4 id="headers-3">Headers</h4> -<table> -<thead> -<tr> -<th>Key</th> -<th>Value</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>Content-Type</td> -<td>text/csv</td> -<td>Processes form data in request body as JSON.</td> -</tr> -</tbody> -</table> -<h4 id="permissions-9">Permissions</h4> -<p><code>can_create_users</code></p> -<h4 id="example">Example</h4> -<p>Create two new users that can authenticate with a username and a password -that can submit reports and view or modify records associated to their place. -Along with a new user, a new contact and new place are created as well. -The new place will be a child of <code>place.parent</code> (see the UUID <code>fe4da0f9-7d65-4834-bb42-88a5239bbd3b</code> below) -and must already exist or else the new place will be an orphan record in the hierarchy and not show up in the GUI.</p> -<pre tabindex="0"><code>POST /api/v2/users -Content-Type: text/csv -contact.first_name,contact.last_name,contact.sex,contact.phone,email,contact.meta.created_by,token_login,contact.role,contact.type,contact.contact_type,contact.name,username,password,phone,place.parent,place.type,place.name,place.contact_type,type,fullname -Mary,Anyango,female,+2868917046,,,FALSE,chw,contact,person,Mary Anyango,mary,WrAGyGD9805,2868917046,fe4da0f9-7d65-4834-bb42-88a5239bbd3b,health_center,Mary Anyango&#39;s Health center,clinic,chw,Mary Anyango -Bob,Johnson,male,+2868194607,,,FALSE,chw,contact,person,Bob Johnson,bob,JzAEQzY2614,2868194607,fe4da0f9-7d65-4834-bb42-88a5239bbd3b,health_center,Bob Johnson&#39;s Health center,clinic,chw,Bob Johnson -</code></pre><pre tabindex="0"><code>HTTP/1.1 200 -Content-Type: application/json -[ -{ -&#34;contact&#34;: { -&#34;id&#34;: &#34;65416b8ceb53ff88ac1847654501aeb3&#34;, -&#34;rev&#34;: &#34;1-0b74d219ae13137c1a06f03a0a52e187&#34; -}, -&#34;user-settings&#34;: { -&#34;id&#34;: &#34;org.couchdb.user:mary&#34;, -&#34;rev&#34;: &#34;1-6ac1d36b775143835f4af53f9895d7ae&#34; -}, -&#34;user&#34;: { -&#34;id&#34;: &#34;org.couchdb.user:mary&#34;, -&#34;rev&#34;: &#34;1-c3b82a0b47cfe68edd9284c89bebbae4&#34; -} -}, -{ -&#34;contact&#34;: { -&#34;id&#34;: &#34;8d8a741c1cb441058e29a60ab7596bf2&#34;, -&#34;rev&#34;: &#34;1-acbc31712fd105eae3cd0806cd20a8f4&#34; -}, -&#34;user-settings&#34;: { -&#34;id&#34;: &#34;org.couchdb.user:bob&#34;, -&#34;rev&#34;: &#34;1-d8629838127accd531043f845c416ef6&#34; -}, -&#34;user&#34;: { -&#34;id&#34;: &#34;org.couchdb.user:bob&#34;, -&#34;rev&#34;: &#34;1-5eac2542c801ba6b518728f53d9276a0&#34; -} -} -] -</code></pre><h4 id="errors-2">Errors</h4> -<h5 id="response-when-the-username-already-exists-1">Response when the username already exists</h5> -<pre tabindex="0"><code>HTTP/1.1 400 Bad Request -Content-Type: application/json -[ -{ -&#34;error&#34;: &#34;Missing required fields: username&#34; -} -] -</code></pre><h5 id="response-when-users-are-missing-required-fields-1">Response when users are missing required fields</h5> -<pre tabindex="0"><code>HTTP/1.1 400 Bad Request -Content-Type: application/json -[ -{ -&#34;error&#34;: &#34;Missing required fields: username&#34; -}, -{ -&#34;error&#34;: &#34;Missing required fields: password&#34; -} -] -</code></pre><h5 id="response-when-some-users-failed-to-be-created-1">Response when some users failed to be created</h5> -<pre tabindex="0"><code>HTTP/1.1 200 -Content-Type: application/json -[ -{ -&#34;contact&#34;: { -&#34;id&#34;: &#34;65416b8ceb53ff88ac1847654501aeb3&#34;, -&#34;rev&#34;: &#34;1-0b74d219ae13137c1a06f03a0a52e187&#34; -}, -&#34;user-settings&#34;: { -&#34;id&#34;: &#34;org.couchdb.user:mary&#34;, -&#34;rev&#34;: &#34;1-6ac1d36b775143835f4af53f9895d7ae&#34; -}, -&#34;user&#34;: { -&#34;id&#34;: &#34;org.couchdb.user:mary&#34;, -&#34;rev&#34;: &#34;1-c3b82a0b47cfe68edd9284c89bebbae4&#34; -} -}, -{ -&#34;error&#34;: &#34;Failed to find place.&#34; -} -] -</code></pre><h3 id="post-apiv1usersusername">POST /api/v1/users/{{username}}</h3> -<p>Allows you to change property values on a user account. Properties listed above -are supported except for <code>contact.parent</code>. Creating or modifying people -through the user is not supported, see People section.</p> -<h4 id="permissions-10">Permissions</h4> -<p><code>can_update_users</code>, <code>can_update_places</code>, <code>can_update_people</code></p> -<h4 id="updating-yourself">Updating yourself</h4> -<p>You do not need any of the above permissions if the user you are modifying is yourself. However, you are not allowed to modify your <code>type</code>, <code>roles</code>, <code>contact</code> or <code>place</code>.</p> -<p>Further more, if you&rsquo;re updating your <code>password</code> you must be authenticating via Basic Auth (either the header or in the URL). This is to ensure the password is known at time of request, and no one is hijacking a cookie.</p> -<h4 id="url-parameters">URL Parameters</h4> -<table> -<thead> -<tr> -<th>Variable</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>username</td> -<td>String identifier used for authentication.</td> -</tr> -</tbody> -</table> -<h4 id="examples-14">Examples</h4> -<pre tabindex="0"><code>POST /api/v1/users/mary -Content-Type: application/json -{ -&#34;password&#34;: &#34;secret&#34;, -&#34;place&#34;: &#34;eeb17d6d-5dde-c2c0-62c4a1a0ca17e342&#34; -} -</code></pre><pre tabindex="0"><code>HTTP/1.1 200 OK -Content-Type: application/json -{ -&#34;user&#34;: { -&#34;id&#34;: &#34;org.couchdb.user:mary&#34;, -&#34;rev&#34;: &#34;23-858e01fafdfa0d367d798fe5b44751ff&#34; -}, -&#34;user-settings&#34;: { -&#34;id&#34;: &#34;org.couchdb.user:mary&#34;, -&#34;rev&#34;: &#34;17-c6d03b86d2d5d70f7270c85e67fea96d&#34; -} -} -</code></pre><h3 id="delete-apiv1usersusername">DELETE /api/v1/users/{{username}}</h3> -<p>Delete a user. Does not affect a person or place associated to a user.</p> -<h4 id="permissions-11">Permissions</h4> -<p><code>can_delete_users</code></p> -<h4 id="url-parameters-1">URL Parameters</h4> -<table> -<thead> -<tr> -<th>Variable</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>username</td> -<td>String identifier used for authentication.</td> -</tr> -</tbody> -</table> -<h4 id="examples-15">Examples</h4> -<pre tabindex="0"><code>DELETE /api/v1/users/mary -</code></pre><pre tabindex="0"><code>HTTP/1.1 200 OK -</code></pre><h3 id="get-apiv1users-info">GET /api/v1/users-info</h3> -<p>Returns the total number of documents an offline user would replicate (<code>total_docs</code>), the number of docs excluding tasks the user would replicate (<code>warn_docs</code>), along with a <code>warn</code> flag if this number exceeds the recommended limit (now set at 10 000).</p> -<p>When the authenticated requester has an offline role, it returns the requester doc count.</p> -<h4 id="example-1">Example</h4> -<pre tabindex="0"><code>GET /api/v1/users-info -H &#39;Cookie: AuthSession=OFFLINE_USER_SESSION;&#39; -</code></pre><pre tabindex="0"><code>HTTP/1.1 200 OK -Content-Type: application/json -{ -&#34;total_docs&#34;: 5678, -&#34;warn_docs&#34;: 4852, -&#34;warn&#34;: false, -&#34;limit: 10000 -} -</code></pre><p>When the requester has an online role, the following query parameters are accepted:</p> -<h5 id="query-parameters-7">Query Parameters</h5> -<table> -<thead> -<tr> -<th>Variable</th> -<th>Description</th> -<th>Required</th> -</tr> -</thead> -<tbody> -<tr> -<td>facility_id</td> -<td>String identifier representing the uuid of the user&rsquo;s facility</td> -<td>true</td> -</tr> -<tr> -<td>role</td> -<td>String identifier representing the user role - must be configured as an offline role. Accepts valid UTF-8 JSON array for multiple of roles.</td> -<td>true</td> -</tr> -<tr> -<td>contact_id</td> -<td>String identifier representing the uuid of the user&rsquo;s associated contact</td> -<td>false</td> -</tr> -</tbody> -</table> -<p>When requested as an online user, the number of tasks are never counted and never returned, so <code>warn_docs</code> is always equal to <code>total_docs</code>.</p> -<h6 id="example-2">Example</h6> -<pre tabindex="0"><code>GET /api/v1/users-info?facility_id={{facility_uuid}}&amp;role={{role}}&amp;contact_id={{contact_uuid}} -H &#39;Cookie: AuthSession=OFFLINE_USER_SESSION;&#39; -</code></pre><pre tabindex="0"><code>HTTP/1.1 200 OK -Content-Type: application/json -{ -&#34;total_docs&#34;: 10265, -&#34;warn_docs&#34;: 10265, -&#34;warn&#34;: true, -&#34;limit: 10000 -} -</code></pre><p>In case any of the required query parameters are omitted or the requested role is not configured as an offline role, the request will result in an error:</p> -<pre tabindex="0"><code>GET /api/v1/users-info?role={{online_role}} -H &#39;Cookie: AuthSession=OFFLINE_USER_SESSION;&#39; -</code></pre><pre tabindex="0"><code>HTTP/1.1 400 Bad Request -Content-Type: application/json -{ -&#34;code&#34;: 400, -&#34;error&#34;: &#34;Missing required query params: role and/or facility_id&#34; -} -</code></pre><h2 id="bulk-operations">Bulk Operations</h2> -<h3 id="post-apiv1bulk-delete">POST /api/v1/bulk-delete</h3> -<p>Bulk delete endpoint for deleting large numbers of documents. Docs will be batched into groups of 100 and will be sent sequentially to couch (new batch sent after the previous one has returned). The response will be chunked JSON (one batch at a time), so if you wish to get an indication of progress you will need to parse incomplete JSON (with a library such as <a href="https://github.com/indgov/partial-json-parser"><code>partial-json-parser</code></a>.</p> -<h4 id="permissions-12">Permissions</h4> -<p>Only available to online users.</p> -<h4 id="parameters-1">Parameters</h4> -<table> -<thead> -<tr> -<th>Parameter</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>docs</td> -<td>Array of JSON objects with <code>_id</code> properties</td> -</tr> -</tbody> -</table> -<p>An array of objects each with an <code>_id</code> property is required (rather than an array of strings representing ids) to ensure forwards compatibility if we choose to require that any additional document information (such as <code>_rev</code>) also be passed in to this endpoint.</p> -<h4 id="errors-3">Errors</h4> -<p>If an error is encountered part-way through the response (eg on the third batch), it&rsquo;s impossible to send new headers to indicate a 5xx error, so the connection will simply be terminated (as recommended here <a href="https://github.com/expressjs/express/issues/2700)">https://github.com/expressjs/express/issues/2700)</a>.</p> -<h4 id="examples-16">Examples</h4> -<pre tabindex="0"><code>POST /api/v1/bulk-delete -Content-Type: application/json -{ -&#34;docs&#34;: [ -{ &#34;_id&#34;: &#34;id1&#34; }, -{ &#34;_id&#34;: &#34;id2&#34; }, -... -{ &#34;_id&#34;: &#34;id150&#34; } -] -} -</code></pre><pre tabindex="0"><code>HTTP/1.1 200 OK -Content-Type: application/json -[ -[ -{ &#34;ok&#34;: true, &#34;id&#34;: &#34;id1&#34;, &#34;rev&#34;: &#34;1-rev1&#34; }, -{ &#34;ok&#34;: true, &#34;id&#34;: &#34;id2&#34;, &#34;rev&#34;: &#34;1-rev2&#34; }, -... -{ &#34;ok&#34;: true, &#34;id&#34;: &#34;id100&#34;, &#34;rev&#34;: &#34;1-rev100&#34; } -], -[ -{ &#34;ok&#34;: true, &#34;id&#34;: &#34;id101&#34;, &#34;rev&#34;: &#34;1-rev101&#34; }, -{ &#34;ok&#34;: true, &#34;id&#34;: &#34;id102&#34;, &#34;rev&#34;: &#34;1-rev102&#34; }, -... -{ &#34;ok&#34;: true, &#34;id&#34;: &#34;id150&#34;, &#34;rev&#34;: &#34;1-rev150&#34; } -] -] -</code></pre><h2 id="monitoring">Monitoring</h2> -<p>See the <a href="https://docs.communityhealthtoolkit.org/hosting/monitoring/">Monitoring and alerting on the CHT</a> page for how to use this API in production.</p> -<h3 id="get-apiv1monitoring">GET /api/v1/monitoring</h3> -<p><em>DEPRECATED: use <a href="#get-apiv2monitoring">/api/v2/monitoring</a></em></p> -<p>Used to retrieve a range of metrics about the instance. While the output is human-readable this is intended for automated monitoring allowing for tracking trends over time and alerting about potential issues.</p> -<h4 id="permissions-13">Permissions</h4> -<p>No permissions required.</p> -<h4 id="examples-17">Examples</h4> -<h5 id="json-format">JSON format</h5> -<pre tabindex="0"><code>curl http://localhost:5988/api/v1/monitoring -{&#34;version&#34;:{&#34;app&#34;:&#34;3.9.0&#34;,&#34;node&#34;:&#34;v10.16.0&#34;,&#34;couchdb&#34;:&#34;2.3.1&#34;},&#34;couchdb&#34;:{&#34;medic&#34;:{&#34;name&#34;:&#34;medic&#34;,&#34;update_sequence&#34;:5733,&#34;doc_count&#34;:278,&#34;doc_del_count&#34;:32,&#34;fragmentation&#34;:1.0283517758420173}... -</code></pre><h4 id="response-content">Response content</h4> -<table> -<thead> -<tr> -<th>JSON path</th> -<th>Type</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>version.app</code></td> -<td>String</td> -<td>The version of the webapp.</td> -</tr> -<tr> -<td><code>version.node</code></td> -<td>String</td> -<td>The version of NodeJS.</td> -</tr> -<tr> -<td><code>version.couchdb</code></td> -<td>String</td> -<td>The version of CouchDB.</td> -</tr> -<tr> -<td><code>couchdb.&lt;dbname&gt;.name</code></td> -<td>String</td> -<td>The name of the db, usually one of &ldquo;medic&rdquo;, &ldquo;medic-sentinel&rdquo;, &ldquo;medic-users-meta&rdquo;, &ldquo;_users&rdquo;.</td> -</tr> -<tr> -<td><code>couchdb.&lt;dbname&gt;.update_sequence</code></td> -<td>Number</td> -<td>The number of changes in the db.</td> -</tr> -<tr> -<td><code>couchdb.&lt;dbname&gt;.doc_count</code></td> -<td>Number</td> -<td>The number of docs in the db.</td> -</tr> -<tr> -<td><code>couchdb.&lt;dbname&gt;.doc_del_count</code></td> -<td>Number</td> -<td>The number of deleted docs in the db.</td> -</tr> -<tr> -<td><code>couchdb.&lt;dbname&gt;.fragmentation</code></td> -<td>Number</td> -<td>The fragmentation of the db, lower is better, &ldquo;1&rdquo; is no fragmentation.</td> -</tr> -<tr> -<td><code>date.current</code></td> -<td>Number</td> -<td>The current server date in millis since the epoch, useful for ensuring the server time is correct.</td> -</tr> -<tr> -<td><code>date.uptime</code></td> -<td>Number</td> -<td>How long API has been running in seconds.</td> -</tr> -<tr> -<td><code>sentinel.backlog</code></td> -<td>Number</td> -<td>Number of changes yet to be processed by Sentinel.</td> -</tr> -<tr> -<td><code>messaging.outgoing.state.due</code></td> -<td>Number</td> -<td>The number of messages due to be sent.</td> -</tr> -<tr> -<td><code>messaging.outgoing.state.scheduled</code></td> -<td>Number</td> -<td>The number of messages scheduled to be sent in the future.</td> -</tr> -<tr> -<td><code>messaging.outgoing.state.muted</code></td> -<td>Number</td> -<td>The number of messages that are muted and therefore will not be sent.</td> -</tr> -<tr> -<td><code>messaging.outgoing.state.delivered</code></td> -<td>Number</td> -<td>The number of messages that have been delivered or sent. As of 3.12.x.</td> -</tr> -<tr> -<td><code>messaging.outgoing.state.failed</code></td> -<td>Number</td> -<td>The number of messages have failed to be delivered. As of 3.12.x</td> -</tr> -<tr> -<td><code>outbound_push.backlog</code></td> -<td>Number</td> -<td>Number of changes yet to be processed by Outbound Push.</td> -</tr> -<tr> -<td><code>feedback.count</code></td> -<td>Number</td> -<td>Number of feedback docs created usually indicative of client side errors.</td> -</tr> -<tr> -<td><code>conflict.count</code></td> -<td>Number</td> -<td>Number of doc conflicts which need to be resolved manually.</td> -</tr> -<tr> -<td><code>replication_limit.count</code></td> -<td>Number</td> -<td>Number of users that exceeded the replication limit of documents.</td> -</tr> -<tr> -<td><code>connected_users.count</code></td> -<td>Number</td> -<td>Number of users that have connected to the api in a given number of days. The period defaults to 7 days but this can be changed by adding <code>connected_user_interval</code> as a query parameter e.g. <code>http://localhost:5988/api/v1/monitoring?connected_user_interval=14</code></td> -</tr> -</tbody> -</table> -<h4 id="errors-4">Errors</h4> -<ul> -<li>A metric of <code>&quot;&quot;</code> (for string values) or <code>-1</code> (for numeric values) indicates an error occurred while querying the metric - check the API logs for details.</li> -<li>If no response or an error response is received the instance is unreachable. Thus, this API can be used as an uptime monitoring endpoint.</li> -</ul> -<h3 id="get-apiv2monitoring">GET /api/v2/monitoring</h3> -<p>Available as of 3.12.x. -Used to retrieve a range of metrics about the instance. While the output is human-readable this is intended for automated monitoring allowing for tracking trends over time and alerting about potential issues.</p> -<h4 id="permissions-14">Permissions</h4> -<p>No permissions required.</p> -<h4 id="examples-18">Examples</h4> -<h5 id="json-format-1">JSON format</h5> -<pre tabindex="0"><code>curl http://localhost:5988/api/v2/monitoring -{&#34;version&#34;:{&#34;app&#34;:&#34;3.12.0&#34;,&#34;node&#34;:&#34;v10.16.0&#34;,&#34;couchdb&#34;:&#34;2.3.1&#34;},&#34;couchdb&#34;:{&#34;medic&#34;:{&#34;name&#34;:&#34;medic&#34;,&#34;update_sequence&#34;:5733,&#34;doc_count&#34;:278,&#34;doc_del_count&#34;:32,&#34;fragmentation&#34;:1.0283517758420173}... -</code></pre><h4 id="response-content-1">Response content</h4> -<table> -<thead> -<tr> -<th>JSON path</th> -<th>Type</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>version.app</code></td> -<td>String</td> -<td>The version of the webapp.</td> -</tr> -<tr> -<td><code>version.node</code></td> -<td>String</td> -<td>The version of NodeJS.</td> -</tr> -<tr> -<td><code>version.couchdb</code></td> -<td>String</td> -<td>The version of CouchDB.</td> -</tr> -<tr> -<td><code>couchdb.&lt;dbname&gt;.name</code></td> -<td>String</td> -<td>The name of the db, usually one of &ldquo;medic&rdquo;, &ldquo;medic-sentinel&rdquo;, &ldquo;medic-users-meta&rdquo;, &ldquo;_users&rdquo;.</td> -</tr> -<tr> -<td><code>couchdb.&lt;dbname&gt;.update_sequence</code></td> -<td>Number</td> -<td>The number of changes in the db.</td> -</tr> -<tr> -<td><code>couchdb.&lt;dbname&gt;.doc_count</code></td> -<td>Number</td> -<td>The number of docs in the db.</td> -</tr> -<tr> -<td><code>couchdb.&lt;dbname&gt;.doc_del_count</code></td> -<td>Number</td> -<td>The number of deleted docs in the db.</td> -</tr> -<tr> -<td><code>couchdb.&lt;dbname&gt;.fragmentation</code></td> -<td>Number</td> -<td>The fragmentation of the db, lower is better, &ldquo;1&rdquo; is no fragmentation.</td> -</tr> -<tr> -<td><code>date.current</code></td> -<td>Number</td> -<td>The current server date in millis since the epoch, useful for ensuring the server time is correct.</td> -</tr> -<tr> -<td><code>date.uptime</code></td> -<td>Number</td> -<td>How long API has been running.</td> -</tr> -<tr> -<td><code>sentinel.backlog</code></td> -<td>Number</td> -<td>Number of changes yet to be processed by Sentinel.</td> -</tr> -<tr> -<td><code>messaging.outgoing.total.due</code></td> -<td>Number</td> -<td>The number of messages due to be sent.</td> -</tr> -<tr> -<td><code>messaging.outgoing.total.scheduled</code></td> -<td>Number</td> -<td>The number of messages scheduled to be sent in the future.</td> -</tr> -<tr> -<td><code>messaging.outgoing.total.muted</code></td> -<td>Number</td> -<td>The number of messages that are muted and therefore will not be sent.</td> -</tr> -<tr> -<td><code>messaging.outgoing.total.delivered</code></td> -<td>Number</td> -<td>The number of messages that have been delivered or sent.</td> -</tr> -<tr> -<td><code>messaging.outgoing.total.failed</code></td> -<td>Number</td> -<td>The number of messages have failed to be delivered.</td> -</tr> -<tr> -<td><code>messaging.outgoing.seven_days.due</code></td> -<td>Number</td> -<td>The number of messages due to be sent in the last seven days.</td> -</tr> -<tr> -<td><code>messaging.outgoing.seven_days.scheduled</code></td> -<td>Number</td> -<td>The number of messages that were scheduled to be sent in the last seven days.</td> -</tr> -<tr> -<td><code>messaging.outgoing.seven_days.muted</code></td> -<td>Number</td> -<td>The number of messages that were due in the last seven days and are muted.</td> -</tr> -<tr> -<td><code>messaging.outgoing.seven_days.delivered</code></td> -<td>Number</td> -<td>The number of messages that were due in the last seven days and have been delivered or sent.</td> -</tr> -<tr> -<td><code>messaging.outgoing.seven_days.failed</code></td> -<td>Number</td> -<td>The number of messages that were due in the last seven days and have failed to be delivered.</td> -</tr> -<tr> -<td><code>messaging.outgoing.last_hundred.pending</code></td> -<td>Object</td> -<td>Counts within last 100 messages that have received status updates, and are one of the &ldquo;pending&rdquo; group statuses</td> -</tr> -<tr> -<td><code>messaging.outgoing.last_hundred.pending.pending</code></td> -<td>Number</td> -<td>Number of messages that are pending</td> -</tr> -<tr> -<td><code>messaging.outgoing.last_hundred.pending.forwarded-to-gateway</code></td> -<td>Number</td> -<td>Number of messages that are forwarded-to-gateway</td> -</tr> -<tr> -<td><code>messaging.outgoing.last_hundred.pending.received-by-gateway</code></td> -<td>Number</td> -<td>Number of messages that are received-by-gateway</td> -</tr> -<tr> -<td><code>messaging.outgoing.last_hundred.pending.forwarded-by-gateway</code></td> -<td>Number</td> -<td>Number of messages that are forwarded-by-gateway</td> -</tr> -<tr> -<td><code>messaging.outgoing.last_hundred.final</code></td> -<td>Object</td> -<td>Counts within last 100 messages that have received status updates, and are in one of the &ldquo;final&rdquo; group statuses</td> -</tr> -<tr> -<td><code>messaging.outgoing.last_hundred.final.sent</code></td> -<td>Number</td> -<td>Number of messages that are sent</td> -</tr> -<tr> -<td><code>messaging.outgoing.last_hundred.final.delivered</code></td> -<td>Number</td> -<td>Number of messages that are delivered</td> -</tr> -<tr> -<td><code>messaging.outgoing.last_hundred.final.failed</code></td> -<td>Number</td> -<td>Number of messages that are failed</td> -</tr> -<tr> -<td><code>messaging.outgoing.last_hundred.muted</code></td> -<td>Object</td> -<td>Counts within last 100 messages that have received status updates, and are in one of the &ldquo;muted&rdquo; group statuses</td> -</tr> -<tr> -<td><code>messaging.outgoing.last_hundred.final.denied</code></td> -<td>Number</td> -<td>Number of messages that are denied</td> -</tr> -<tr> -<td><code>messaging.outgoing.last_hundred.final.cleared</code></td> -<td>Number</td> -<td>Number of messages that are cleared</td> -</tr> -<tr> -<td><code>messaging.outgoing.last_hundred.final.muted</code></td> -<td>Number</td> -<td>Number of messages that are muted</td> -</tr> -<tr> -<td><code>messaging.outgoing.last_hundred.final.duplicate</code></td> -<td>Number</td> -<td>Number of messages that are duplicate</td> -</tr> -<tr> -<td><code>outbound_push.backlog</code></td> -<td>Number</td> -<td>Number of changes yet to be processed by Outbound Push.</td> -</tr> -<tr> -<td><code>feedback.count</code></td> -<td>Number</td> -<td>Number of feedback docs created usually indicative of client side errors.</td> -</tr> -<tr> -<td><code>conflict.count</code></td> -<td>Number</td> -<td>Number of doc conflicts which need to be resolved manually.</td> -</tr> -<tr> -<td><code>replication_limit.count</code></td> -<td>Number</td> -<td>Number of users that exceeded the replication limit of documents.</td> -</tr> -<tr> -<td><code>connected_users.count</code></td> -<td>Number</td> -<td>Number of users that have connected to the api in a given number of days. The period defaults to 7 days but this can be changed by adding <code>connected_user_interval</code> as a query parameter e.g. <code>http://localhost:5988/api/v2/monitoring?connected_user_interval=14</code></td> -</tr> -</tbody> -</table> -<h4 id="errors-5">Errors</h4> -<ul> -<li>A metric of <code>&quot;&quot;</code> (for string values) or <code>-1</code> (for numeric values) indicates an error occurred while querying the metric - check the API logs for details.</li> -<li>If no response or an error response is received the instance is unreachable. Thus, this API can be used as an uptime monitoring endpoint.</li> -</ul> -<h3 id="get-apiv1express-metrics">GET /api/v1/express-metrics</h3> -<p><em>Added in 4.3.0</em></p> -<p>Used to retrieve a range of metrics for monitoring CHT API&rsquo;s performance and internals. This API is used by <a href="https://docs.communityhealthtoolkit.org/core/overview/watchdog/">CHT Watchdog</a>.</p> -<p>The response is formatted for the <a href="https://prometheus.io/docs/concepts/data_model/">Prometheus Data Model</a>. The metrics exposed are defined by the <a href="https://www.npmjs.com/package/prometheus-api-metrics">prometheus-api-metrics package</a> and include optional default metrics and garbage collection metrics.</p> -<h4 id="permissions-15">Permissions</h4> -<p>No permissions required.</p> -<h2 id="upgrades">Upgrades</h2> -<p>All of these endpoints require the <code>can_configure</code> permission.</p> -<p>All of these endpoints are asynchronous. Progress can be followed by watching the <code>horti-upgrade</code> document and looking at the <code>log</code> property.</p> -<h3 id="post-apiv1upgrade">POST /api/v1/upgrade</h3> -<p>Performs a complete upgrade to the provided version. This is equivalent of calling <code>/api/v1/upgrade/stage</code> and then <code>/api/v1/upgrade/complete</code> once staging has finished.</p> -<h4 id="example-3">Example</h4> -<pre tabindex="0"><code>POST /api/v1/upgrade -{ -&#34;build&#34;: { -&#34;namespace&#34;: &#34;medic&#34;, -&#34;application&#34;: &#34;medic&#34;, -&#34;version&#34;: &#34;3.0.0-beta.1&#34; -} -} -</code></pre><p>For potential forwards compatibility, you must pass the <code>namespace</code> and <code>application</code> as <code>medic</code>.</p> -<p>The <code>version</code> should correspond to a release, pre-release or branch that has been pushed to our builds server, which is currently hard-coded to <code>https://staging.dev.medicmobile.org/builds</code>. This happens automatically upon a successful Continuous Integration run.</p> -<p>Calling this endpoint will eventually cause api and sentinel to restart.</p> -<p>It is expected that the caller ensures forwards or backwards compatibility is maintained between deployed versions. This endpoint does not stop you from &ldquo;upgrading&rdquo; to an earlier version, or a branch that is incompatible with your current state.</p> -<h3 id="post-apiv1upgradestage">POST /api/v1/upgrade/stage</h3> -<p>Stages an upgrade to the provided version. Does as much of the upgrade as possible without actually swapping versions over and restarting.</p> -<p>Parameters are the same as <code>/api/v1/upgrade</code>.</p> -<p>You know that an upgrade has been staged when the <code>horti-upgrade</code> document in CouchDB has <code>&quot;action&quot;: &quot;stage&quot;</code> and <code>&quot;staging_complete&quot;: true</code>.</p> -<h3 id="post-apiv1upgradecomplete">POST /api/v1/upgrade/complete</h3> -<p>Completes a staged upgrade. Throws a <code>404</code> if there is no upgrade in the staged position.</p> -<h2 id="hydrate">Hydrate</h2> -<p>Accepts a JSON array of document uuids and returns fully hydrated documents, in the same order in which they were requested. -When documents are not found, an entry with the missing uuid and an error is added instead. -Supports both GET and POST. -Only allowed for users with &ldquo;online&rdquo; roles.</p> -<h3 id="get-apiv1hydrate">GET /api/v1/hydrate</h3> -<h4 id="query-parameters-8">Query parameters</h4> -<table> -<thead> -<tr> -<th>Name</th> -<th>Required</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>doc_ids</td> -<td>true</td> -<td>A JSON array of document uuids</td> -</tr> -</tbody> -</table> -<h4 id="example-4">Example</h4> -<pre tabindex="0"><code>GET /api/v1/hydrate?doc_ids=[&#34;id1&#34;,&#34;missingId&#34;,&#34;id3&#34;] -</code></pre><pre tabindex="0"><code>HTTP/1.1 200 OK -Content-Type: application/json -[ -{ &#34;id&#34;: &#34;id1&#34;, &#34;doc&#34;: { &lt;...the hydrated document...&gt; } }, -{ &#34;id&#34;: &#34;missingId1&#34;, &#34;error&#34;: &#34;not_found&#34; }, -{ &#34;id&#34;: &#34;id3&#34;, &#34;doc&#34;: { &lt;...the hydrated document...&gt; } }, -] -</code></pre><h3 id="post-apiv1hydrate">POST /api/v1/hydrate</h3> -<h4 id="parameters-2">Parameters</h4> -<table> -<thead> -<tr> -<th>Name</th> -<th>Required</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>doc_ids</td> -<td>true</td> -<td>A JSON array of document uuids</td> -</tr> -</tbody> -</table> -<h4 id="example-5">Example</h4> -<pre tabindex="0"><code>POST /api/v1/hydrate -Content-Type: application/json -{ -&#34;doc_ids&#34;: [&#34;id1&#34;,&#34;missingId&#34;,&#34;id3&#34;] -} -</code></pre><pre tabindex="0"><code>HTTP/1.1 200 OK -Content-Type: application/json -[ -{ &#34;id&#34;: &#34;id1&#34;, &#34;doc&#34;: { &lt;...the hydrated document...&gt; } }, -{ &#34;id&#34;: &#34;missingId&#34;, &#34;error&#34;: &#34;not_found&#34; }, -{ &#34;id&#34;: &#34;id3&#34;, &#34;doc&#34;: { &lt;...the hydrated document...&gt; } }, -] -</code></pre><h2 id="contacts-by-phone">Contacts by phone</h2> -<p><em>Added in 3.10.0</em></p> -<p>Accepts a phone number parameter and returns fully hydrated contacts that match the requested phone number. -If multiple contacts are found, all are returned. When no matches are found, a 404 error is returned. -Supports both GET and POST. -Only allowed for users with &ldquo;online&rdquo; roles.</p> -<h3 id="get-apiv1contacts-by-phone">GET /api/v1/contacts-by-phone</h3> -<h4 id="query-parameters-9">Query parameters</h4> -<table> -<thead> -<tr> -<th>Name</th> -<th>Required</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>phone</td> -<td>true</td> -<td>A URL encoded string representing a phone number</td> -</tr> -</tbody> -</table> -<h4 id="example-6">Example</h4> -<pre tabindex="0"><code>GET /api/v1/contacts-by-phone?phone=&#34;%2B40-(744)-999-999&#34; -</code></pre><pre tabindex="0"><code>HTTP/1.1 200 OK -Content-Type: application/json -{ -&#34;ok&#34;: true, -&#34;docs&#34;: { -{ &lt;... first hydrated matching contact found ...&gt; }, -{ &lt;... second hydrated matching contact found ...&gt; }, -... -} -} -</code></pre><h3 id="post-apiv1contacts-by-phone">POST /api/v1/contacts-by-phone</h3> -<h4 id="parameters-3">Parameters</h4> -<table> -<thead> -<tr> -<th>Name</th> -<th>Required</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>phone</td> -<td>true</td> -<td>A string representing a phone number</td> -</tr> -</tbody> -</table> -<h4 id="example-7">Example</h4> -<pre tabindex="0"><code>POST /api/v1/contacts-by-phone -Content-Type: application/json -{ -&#34;phone&#34;: &#34;+40 (21) 222-3333&#34; -} -</code></pre><pre tabindex="0"><code>HTTP/1.1 200 OK -Content-Type: application/json -{ -&#34;ok&#34;: true, -&#34;docs&#34;: { -{ &lt;... first hydrated matching contact found ...&gt; }, -{ &lt;... second hydrated matching contact found ...&gt; }, -... -} -} -</code></pre><h2 id="replication-limit">Replication Limit</h2> -<p><em>Added in 3.11.0</em></p> -<p>Returns the quantity of documents that were replicated by each user. -Accepts filtering by user name, when not provided, it returns all users. -Supports GET. -Only allowed for users with &ldquo;_admin&rdquo; role.</p> -<h3 id="get-apiv1users-doc-count">GET /api/v1/users-doc-count</h3> -<h4 id="query-parameters-10">Query parameters</h4> -<table> -<thead> -<tr> -<th>Name</th> -<th>Required</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>user</td> -<td>false</td> -<td>User&rsquo;s name</td> -</tr> -</tbody> -</table> -<h4 id="example-8">Example</h4> -<pre tabindex="0"><code>GET /api/v1/users-doc-count?user=mary&#34; -</code></pre><pre tabindex="0"><code>HTTP/1.1 200 OK -Content-Type: application/json -{ -&#34;limit&#34;: 10000, -&#34;users&#34;: { -&#34;_id&#34;: &#34;replication-count-mary&#34;, -&#34;_rev&#34;: &#34;5-cd3252e852ae075da216c3c3fe461291&#34;, -&#34;user&#34;: &#34;mary&#34;, -&#34;date&#34;: 1595328973273, -&#34;count&#34;: 58 -} -} -</code></pre><h2 id="credentials">Credentials</h2> -<p>Securely store credentials for authentication with third party systems such as SMS aggregators and HMIS. Certain CHT services rely on these credentials when enabled.</p> -<h3 id="put-apiv1credentials">PUT /api/v1/credentials</h3> -<p><em>Added in 4.0.0</em></p> -<p>Provide the credential key as a URL parameter and the password in the request body, for example, to set a credential with key &ldquo;mykey&rdquo; and password &ldquo;my pass&rdquo; use the following command.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>curl -X PUT -H <span style="color:#4e9a06">&#34;Content-Type: text/plain&#34;</span> https://&lt;user&gt;:&lt;pass&gt;@&lt;domain&gt;/api/v1/credentials/mykey -d <span style="color:#4e9a06">&#39;my pass&#39;</span> -</span></span></code></pre></div><h3 id="3x-api">3.x API</h3> -<p>For 3.x deployments credentials are stored in <a href="https://docs.couchdb.org/en/stable/api/server/configuration.html">CouchDB configuration</a> in a custom <code>medic-credentials</code> section.</p> -<p>To add the credential to the admin config you need to PUT the value using curl, for example, to set a credential with key &ldquo;mykey&rdquo; and password &ldquo;my pass&rdquo; use the following command.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>curl -X PUT https://&lt;user&gt;:&lt;pass&gt;@&lt;domain&gt;/_node/_local/_config/medic-credentials/mykey -d <span style="color:#4e9a06">&#39;&#34;my pass&#34;&#39;</span> -</span></span></code></pre></div><p>You can also add it via Fauxton:</p> -<ul> -<li>Navigate to the Config screen at <code>https://&lt;domain&gt;/_utils/#/_config</code></li> -<li>Click <code>Add Option</code></li> -<li>The <code>Section</code> should be <code>medic-credentials</code>, the <code>Name</code> should be (in this example) <code>mykey</code> and the value should be the password</li> -<li>Click <code>Create</code></li> -<li>You should then be able to see your credential in the list of configuration shown</li> -</ul>Apps: forms/https://docs.communityhealthtoolkit.org/apps/reference/forms/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/forms/Apps: resources/https://docs.communityhealthtoolkit.org/apps/reference/resources/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/resources/ -<h2 id="icons">Icons</h2> -<p>Apps can be customized by defining the icons to use for tasks, targets, and contacts.</p> -<p>Add icons to the <code>resources</code> folder, and include them by name in the <code>resources.json</code> file as the following example:</p> -<pre tabindex="0"><code> { -&#34;icon-risk&#34;: &#34;icon-healthcare-warning@2x.png&#34;, -&#34;icon-treatment&#34;: &#34;icon-healthcare-medicine@2x.png&#34;, -&#34;medic-clinic&#34;: &#34;medic-family.svg&#34;, -&#34;medic-district-hospital&#34;: &#34;medic-family.svg&#34;, -&#34;medic-health-center&#34;: &#34;medic-chw-area.svg&#34;, -&#34;medic-person&#34;: &#34;medic-person.svg&#34; -} -</code></pre> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/design/icons/">Icon Library</a></p> -<p>The folder and files structure would look like this:</p> -<pre tabindex="0"><code>./ -resources.json -/resources -icon-healthcare-warning@2x.png -icon-healthcare-medicine@2x.png -medic-family.svg -medic-family.svg -medic-chw-area.svg -medic-person.svg -</code></pre><p>Finally run the command: <code>cht --local upload-resources</code></p> -<h2 id="branding">Branding</h2> -<p>The site title, favicon, and header logo are configurable. The location to change these are in the Admin console on the images page under the branding tab.</p> -<p>Another way to configure these options is by using the <code>cht-conf</code>, add the favicon and the header logo in the <code>branding</code> folder, then include the options in the <code>branding.json</code> as the following example:</p> -<pre tabindex="0"><code> { -&#34;title&#34;: &#34;My Clinic&#34;, -&#34;resources&#34;: { -&#34;logo&#34;: &#34;logo.png&#34;, -&#34;favicon&#34;: &#34;favicon.ico&#34; -} -} -</code></pre><p>The folder and files structure would look like this:</p> -<pre tabindex="0"><code>./ -branding.json -/branding -logo.png -favicon.ico -</code></pre><p>Finally run the command: <code>cht --local upload-branding</code></p> -<h2 id="partner-logos">Partner logos</h2> -<p>Adding your partner logos can be done in the Admin console on the images page under the Partners tab. This will add partner logos on the about page.</p> -<p>Another way is by using the <code>cht-conf</code>, add the partners logo in the <code>partners</code> folder, then include them in the <code>partners.json</code> as the following example:</p> -<pre tabindex="0"><code> { -&#34;resources&#34;: { -&#34;partnerA&#34;: &#34;parnerA.png&#34;, -&#34;partnerB&#34;: &#34;parnerB.png&#34; -} -} -</code></pre><p>The folder and files structure would look like this:</p> -<pre tabindex="0"><code>./ -partners.json -/partners -parnerA.png -parnerB.png -</code></pre><p>Finally run the command: <code>cht --local upload-partners</code></p>Apps: translations/https://docs.communityhealthtoolkit.org/apps/reference/translations/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/translations/ -<p>Given that CHT apps are used around the world, the Core Framework was designed with localization in mind. The Core Framework itself is available in English, French, Hindi, Nepali, Spanish, Swahili, and Indonesian.<br> -In the <code>app_settings.json</code> file the default language for the application is set by the <code>locale</code> property, along with a separate default language for outgoing messages that are sent via SMS with the <code>locale_outgoing</code> property.<br> -Additionally, languages available to the user can be enabled and disabled through the <code>languages</code> property which contains an array of objects. These objects should contain the <code>locale</code> and <code>enabled</code> properties representing respectively the 2 or 3 letter language code and whether that language should be enabled.</p> -<div class="alert alert-info" role="alert"> -<h4 class="alert-heading">Note</h4> -Please open <a href="https://github.com/medic/cht-core/issues/new">an issue</a> if you are interested in translating the app into a different language, as we can work together to make that language available to the community. -</div> -<h2 id="translations">Translations</h2> -<p>To modify some labels in the app add the key and modified label in a custom translations file in the <code>translations</code> folder. All the properties files use the format <code>messages-{language-code}.properties</code>, where the language code is the same 2-letter code used to identify the language in the application. For instance, for English, we would have a <code>translations/messages-en.properties</code> file.</p> -<p>New elements in CHT apps, such as tasks, targets, profiles, and forms should be localized as well. These labels should be included in the same custom translations properties file. If a translation is missing for the user&rsquo;s language it will use that of the default language.</p> -<p>Here is an example, including both a modified label, and a new one:</p> -<h3 id="translationsmessages-language-codeproperties"><code>translations/messages-{language-code}.properties</code></h3> -<pre tabindex="0"><code> [Application Text] -contact.type.district_hospital = Community -targets.assessments.title = Assessments Completed -</code></pre><h2 id="forms">Forms</h2> -<p>Translations for XForms are defined within the forms themselves. The XLSForm notation is <a href="http://xlsform.org/en/#multiple-language-support">documented here</a>, and would use the corresponding 2-character language codes.</p> -<h2 id="reports">Reports</h2> -<p>Submitted forms are shown on the Reports tab, with each value in the report displayed alongside a label. The label for each value is represented by a key in the <code>report.{form-name}.{field-name}</code> format, which can be translated by including the key and translation in the <a href="#translations">language files</a>. If the label is omitted in the translation the full key will show in the app.</p> -<div class="alert alert-info" role="alert"> -<h4 class="alert-heading">Note</h4> -To hide report fields from showing on the Reports view altogether, the containing group or field must be included as <code>hidden_fields</code>, as per the <a href="https://docs.communityhealthtoolkit.org/apps/reference/forms/app/#properties">form properties file</a>. -</div> -<h2 id="build">Build</h2> -<p>Custom translations from the properties files are added to the app with the <code>upload-custom-translations</code> action.</p> -<p><code>cht --local upload-custom-translations</code></p> -<p>Updated translations from forms need to be added with the actions to upload forms.</p> -<p><code>cht --local upload-contact-forms upload-app-forms</code></p>Apps: app_settings.jsonhttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/ -<p>The settings which control CHT apps are defined in the <code>app_settings.json</code> file, and stored in the <code>settings</code> doc in the database. Some settings can be modified in the <a href="https://docs.communityhealthtoolkit.org/apps/features/admin/"><strong>App Management</strong></a> app, which updates the same settings file in the database.</p> -<p>The settings get compiled into the <code>app_settings.json</code> file with the <code>compile-app-settings</code> action in the <code>cht-conf</code> tool. -Manually configurable settings are added to the <code>app_settings</code> folder at the root of the config folder. -The <code>app_settings/base_settings.json</code> file can be manually edited to modify individual settings. -<a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/forms/"><code>forms</code></a>, <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/schedules/"><code>schedules</code></a>, -and <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/assetlinks/"><code>assetlinks</code></a> sections can be defined in separate files named -<code>app_settings/forms.json</code>, <code>app_settings/schedules.json</code>, and <code>app_settings/assetlinks.json</code> respectively with the settings -in the files overriding what might be already present in the <code>app_settings/base_settings.json</code> or <code>app_settings.json</code> files.</p> -<p>Most sections are described on their own in the <a href="https://docs.communityhealthtoolkit.org/apps/reference/">Reference Documentation</a>.</p> -<h2 id="build">Build</h2> -<p>To include your settings into your app, you must compile them to include modular components, then upload them to your instance.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>cht --local compile-app-settings backup-app-settings upload-app-settings -</span></span></code></pre></div><h2 id="optional-settings">Optional Settings</h2> -<p>The following settings do not need to be specified. They should only be defined when the default implementation needs to be changed.</p> -<h3 id="app_settingsjson"><code>app_settings.json</code></h3> -<table> -<thead> -<tr> -<th>Setting</th> -<th>Description</th> -<th>Default</th> -<th>Version</th> -</tr> -</thead> -<tbody> -<tr> -<td>phone_validation</td> -<td><ul><li>&ldquo;full&rdquo;: full validation of a phone number for a region using length and prefix information.</li><li>&ldquo;partial&rdquo;: quickly guesses whether a number is a possible phone number by using only the length information, much faster than a full validation.</li><li>&ldquo;none&rdquo;: allows almost any values but still fails for any phone that contains a-z chars.</li></ul></td> -<td>&ldquo;full&rdquo;</td> -<td>3.1.0</td> -</tr> -<tr> -<td>uhc.contacts_default_sort</td> -<td><ul><li>&ldquo;alpha&rdquo;: Sort contacts alphanumerically</li><li>&ldquo;last_visited_date&rdquo;: sort contacts by the date they were most recently visited.</li></ul></td> -<td>&ldquo;alpha&rdquo;</td> -<td>2.18.0</td> -</tr> -<tr> -<td>uhc.visit_count.month_start_date</td> -<td>The date of each month when the visit count is reset to 0.</td> -<td>1</td> -<td>2.18.0</td> -</tr> -<tr> -<td>uhc.visit_count.visit_count_goal</td> -<td>The monthly visit count goal.</td> -<td>0</td> -<td>2.18.0</td> -</tr> -<tr> -<td>outgoing_deny_list</td> -<td>All outgoing messages will be denied (unsent) if the recipient phone number starts with an entry in this list. A comma delimited list. (eg. <code>outgoing_deny_list=&quot;253,ORANGE&quot;</code> will deny all messages sent to <code>253 543 4448</code> and <code>ORANGE NET</code>)</td> -<td>&quot;&quot;</td> -<td></td> -</tr> -<tr> -<td>outgoing_deny_shorter_than</td> -<td>Deny all messages to recipient phone numbers which are shorter than this value. Intended to avoid <a href="https://docs.communityhealthtoolkit.org/apps/guides/messaging/message-loops/">message loops</a> with short codes used by gateways (eg. <code>60396</code>). An integer.</td> -<td>6</td> -<td>3.3.0</td> -</tr> -<tr> -<td>outgoing_deny_with_alphas</td> -<td>When <code>true</code>, deny all messages to recipient phone numbers containing letters (eg. <code>Safaricom</code>). Intended to avoid <a href="https://docs.communityhealthtoolkit.org/apps/guides/messaging/message-loops/">message loops</a> with non-numeric senders used by gateways. A boolean.</td> -<td>true</td> -<td>3.3.0</td> -</tr> -<tr> -<td>outgoing_deny_with_alphas</td> -<td>When <code>true</code>, deny all messages to recipient phone numbers containing letters (eg. <code>Safaricom</code>). Intended to avoid <a href="https://docs.communityhealthtoolkit.org/apps/guides/messaging/message-loops/">message loops</a> with non-numeric senders used by gateways. A boolean.</td> -<td>true</td> -<td>3.3.0</td> -</tr> -<tr> -<td>task_day_limit</td> -<td>The number of days before a task is due to show the due date.</td> -<td>4</td> -<td>3.9.0</td> -</tr> -<tr> -<td>app_url</td> -<td>The URL of the app, eg: &ldquo;<a href="https://demo.app.medicmobile.org">https://demo.app.medicmobile.org</a>&rdquo;</td> -<td></td> -<td>3.10.0</td> -</tr> -<tr> -<td>task_days_overdue</td> -<td>Display number of overdue days in tasks list</td> -<td>false</td> -<td>3.13.0</td> -</tr> -<tr> -<td>languages</td> -<td>Array of objects with <code>locale</code> and <code>enabled</code> properties representing respectively the 2 or 3 letter language code and whether that language should be enabled. <br/>If unset it falls back to the previous behavior of relying on the <code>enabled</code> property of each translation document <code>messages-XX.properties</code>. This fallback behavior is now deprecated and will be removed in the next major version (5.0). This <code>languages</code> configuration property will be required for CHT 5.0+.</td> -<td></td> -<td>4.2.0</td> -</tr> -<tr> -<td>place_hierarchy_types</td> -<td>Array of contact types&rsquo; IDs, should match the ones defined in <code>contact_types</code>. This is used to define the Place Filter&rsquo;s options in Reports tab.</td> -<td></td> -<td>2.15.0</td> -</tr> -<tr> -<td>assetlinks</td> -<td>Array of <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/assetlinks/">Digital Asset Links</a> definitions. This is used to associate your CHT instance&rsquo;s domain to your Android app to verify app links.</td> -<td></td> -<td>4.7.0</td> -</tr> -</tbody> -</table> -<h2 id="sms-workflows">SMS Workflows</h2> -<p>Workflows involving SMS are configured by defining <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/schedules/">schedules</a>, <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/registrations/">registrations</a>, <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/patient_reports/">patient reports</a>, and <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/accept_case_reports/">case reports</a>. Schedules of automated messages can be sent from the server at specified times in the future, and reports can be associated to contacts. Forms can also be configured to clear the schedule, or silence it for a period of time. -As of <code>3.11.0</code>, places are valid subjects for any SMS workflows.</p> -<h2 id="sms-recipient-resolution">SMS recipient resolution</h2> -<p>An outgoing SMS message configuration has the following fields:</p> -<table> -<thead> -<tr> -<th>property</th> -<th>description</th> -<th>required</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>translation_key</code></td> -<td>The translation key of the message to send out. Available in 2.15+.</td> -<td>yes</td> -</tr> -<tr> -<td><code>messages</code></td> -<td>(<strong>deprecated</strong>) Array of message objects, each with <code>content</code> and <code>locale</code> properties. From 2.15 on use <code>translation_key</code> instead.</td> -<td>no</td> -</tr> -<tr> -<td><code>recipient</code></td> -<td>Recipient of the message.</td> -<td>no</td> -</tr> -</tbody> -</table> -<h3 id="recipient-values-and-resolutions"><code>recipient</code> values and resolutions:</h3> -<table> -<thead> -<tr> -<th>value</th> -<th>resolves to</th> -</tr> -</thead> -<tbody> -<tr> -<td><em>empty</em></td> -<td>submitter</td> -</tr> -<tr> -<td><code>reporting_unit</code></td> -<td>submitter</td> -</tr> -<tr> -<td><code>parent</code></td> -<td>primary contact of the subject&rsquo;s/submitter&rsquo;s place&rsquo;s parent (<code>patient.parent.parent.contact</code>)</td> -</tr> -<tr> -<td><code>grandparent</code></td> -<td>primary contact of the subject&rsquo;s/submitter&rsquo;s place&rsquo;s grandparent (<code>patient.parent.parent.parent.contact</code>)</td> -</tr> -<tr> -<td><code>clinic</code></td> -<td>primary contact of the <code>clinic</code> in the subject&rsquo;s/submitter&rsquo;s lineage</td> -</tr> -<tr> -<td><code>health_center</code></td> -<td>primary contact of the <code>health_center</code> in the subject&rsquo;s/submitter&rsquo;s lineage</td> -</tr> -<tr> -<td><code>district</code></td> -<td>primary contact of the <code>district_hospital</code> in the subject&rsquo;s/submitter&rsquo;s lineage</td> -</tr> -<tr> -<td><code>ancestor:&lt;contact_type&gt;</code></td> -<td>primary contact of the place of the requested type in the subject&rsquo;s/submitter&rsquo;s lineage</td> -</tr> -<tr> -<td><code>link:&lt;tag&gt;</code></td> -<td>Linked doc that has requested <code>tag</code> in the subject&rsquo;s / submitter&rsquo;s lineage (direct mapping, not to primary contact). <em>As of 3.10.x</em></td> -</tr> -<tr> -<td><code>link:&lt;contact_type&gt;</code></td> -<td>primary contact of the place of the requested <code>contact_type</code> in the subject&rsquo;s/submitter&rsquo;s lineage. <em>As of 3.10.x</em></td> -</tr> -<tr> -<td><em>custom object path</em></td> -<td>a direct object path in the <a href="#message-context">message context object</a> eg: <code>patient.parent.contact.other_phone</code></td> -</tr> -<tr> -<td><em>valid phone number</em></td> -<td>requested phone number</td> -</tr> -</tbody> -</table> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -<ul> -<li>if <code>recipient</code> resolution does not yield a phone number, it will default to submitter&rsquo;s phone number</li> -<li>if there is no submitter phone number available, the actual <code>recipient</code> property value will be used</li> -<li>when mapping a contact phone number, subject (<code>patient</code> and/or <code>place</code>) lineage and <code>linked_docs</code> take precedence over <code>submitter</code> lineage and <code>linked_docs</code>.</li> -<li>except for <code>link:&lt;tag&gt;</code>, phone numbers are resolved to the primary contacts of the requested places. <code>linked_docs</code> hydration is shallow, so the primary contact of the linked doc will not be available.</li> -</ul> -</div> -<h3 id="message-context">Message context</h3> -<p>The message context object consists of:</p> -<table> -<thead> -<tr> -<th>property</th> -<th>value description</th> -</tr> -</thead> -<tbody> -<tr> -<td><em>every property from the original report</em></td> -<td>unchanged unless specified below</td> -</tr> -<tr> -<td><em>every <code>fields</code> property from the original report</em></td> -<td>eg: if the report has <code>fields.test = 'test'</code> then <code>context.test = 'test'</code></td> -</tr> -<tr> -<td>patient</td> -<td>deeply hydrated patient contact (resolved from <code>patient_id</code>, <code>fields.patient_id</code> or <code>fields.patient_uuid</code>)</td> -</tr> -<tr> -<td>patient_name</td> -<td>patient&rsquo;s name</td> -</tr> -<tr> -<td>place</td> -<td>deeply hydrated place document (resolved from <code>place_id</code> or <code>fields.place_id</code>)</td> -</tr> -<tr> -<td>contact</td> -<td>deeply hydrated submitter contact</td> -</tr> -<tr> -<td>parent</td> -<td>deeply hydrated <code>health_center</code> type document from the subject&rsquo;s or submitter&rsquo;s lineage</td> -</tr> -<tr> -<td>grandparent</td> -<td>deeply hydrated <code>district_hospital</code> type document from the subject&rsquo;s or submitter&rsquo;s lineage</td> -</tr> -<tr> -<td>clinic</td> -<td>deeply hydrated <code>clinic</code> type document from the subject&rsquo;s or submitter&rsquo;s lineage</td> -</tr> -<tr> -<td>health_center</td> -<td>deeply hydrated <code>health_center</code> type document from the subject&rsquo;s or submitter&rsquo;s lineage</td> -</tr> -<tr> -<td>district_hospital</td> -<td>deeply hydrated <code>district_hospital</code> type document from the subject&rsquo;s or submitter&rsquo;s lineage</td> -</tr> -</tbody> -</table> -<h2 id="variables">Variables</h2> -<p>Outgoing messages can use <a href="https://mustache.github.io/mustache.5.html">Mustache templating</a> to insert variables and specify data formats. -All <a href="#message-context">message context fields</a> are available as variables.</p> -<h3 id="code-sample">Code sample</h3> -<p>The following translation label includes the <code>name</code> field of <code>contact</code>, along with the submitted <code>patient_name</code> field, and the generated <code>patient_id</code> field.</p> -<h4 id="translationsmessages-enproperties"><code>translations/messages-en.properties</code></h4> -<pre tabindex="0"><code>messages.registration.report_accepted = Thank you {{contact.name}} for registering {{patient_name}}. Their ID is {{patient_id}}. -</code></pre><h3 id="date-format-filters">Date Format Filters</h3> -<p>The following filter functions are available for formatting dates.</p> -<table> -<thead> -<tr> -<th>filter</th> -<th>description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>date</code></td> -<td>Displays dates according to the <code>date_format</code> specified in app_settings. See <a href="https://momentjs.com/docs/#/parsing/string-format/">doc for Moment.js format</a> for details.</td> -</tr> -<tr> -<td><code>datetime</code></td> -<td>Displays dates according to the <code>reported_date_format</code> specified in app_settings. See <a href="https://momentjs.com/docs/#/parsing/string-format/">doc for Moment.js format</a> for details.</td> -</tr> -<tr> -<td><code>bikram_sambat_date</code></td> -<td>Displays dates in Bikram Sambat calendar (most commonly used calendar in Nepal). Currently display format is hardcoded to e.g. &ldquo;१५ चैत २०७३&rdquo;.</td> -</tr> -</tbody> -</table> -<h2 id="validations">Validations</h2> -<p>Validation rules are code fragments used to determine if some input is valid. For example, to say a field is only valid if the value has at least five characters, you would use the lenMin(5). They are used in <code>registrations[].validations.list[].rule</code> and <code>patient_reports[].validations.list[].rule</code> to determine if an incoming report is accepted. A report is accepted as valid only if all rules return <code>true</code>. If any validation rule returns <code>false</code> then the report is marked as invalid, and a message is automatically sent to the submitter. The content for the message is set in the <code>translation_key</code> associated to each rule. If a report fails multiple validations then each message will be sent. These can be combined into a single SMS by specifying <code>&quot;*.validations.join_responses&quot; : true</code>.</p> -<h3 id="operators">Operators</h3> -<p>The available operators are:</p> -<table> -<thead> -<tr> -<th>Operator</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>&amp;&amp;</td> -<td>and</td> -</tr> -<tr> -<td>||</td> -<td>or</td> -</tr> -<tr> -<td>!</td> -<td>not</td> -</tr> -<tr> -<td>a ? b : c</td> -<td>ternary, ie: if &lsquo;a&rsquo; is true, then check &lsquo;b&rsquo;, otherwise check &lsquo;c&rsquo;</td> -</tr> -<tr> -<td>()</td> -<td>nested blocks, eg: &lsquo;a &amp;&amp; (b</td> -</tr> -</tbody> -</table> -<h3 id="rules">Rules</h3> -<p>Validation settings may consist of Pupil.js rules and rules specific to the CHT. -These two types of rules cannot be combined as part of the same rule.</p> -<p>Not OK: -<code>rule: &quot;regex(\d{5}) &amp;&amp; unique('patient_id')&quot;</code></p> -<p>OK: -<code>rule: &quot;regex(\d{5}) &amp;&amp; max(11111)&quot;</code></p> -<p>If for example you want to validate that patient_id is 5 numbers and it -is unique (or some other custom validation) you need to define two -validation configs/separate rules in your settings. Example validation -settings:</p> -<pre tabindex="0"><code>[ -{ -property: &#34;patient_id&#34;, -rule: &#34;regex(\d{5})&#34;, -message: [{ -content: &#34;Patient ID must be 5 numbers.&#34;, -locale: &#34;en&#34; -}] -}, -{ -property: &#34;patient_id&#34;, -rule: &#34;unique(&#39;patient_id&#39;)&#34;, -message: [{ -content: &#34;Patient ID must be unique.&#34;, -locale: &#34;en&#34; -}] -} -] -</code></pre><p><code>validate()</code> modifies the property value of the second item to -<code>patient_id_unique</code> so that pupil.validate() still returns a valid -result. Then we process the result once more to extract the custom -validation results and error messages.</p> -<h4 id="pupiljs-validation-functions">Pupil.js validation functions</h4> -<p>Available validation functions are available in the <a href="https://github.com/medic/cht-core/tree/master/shared-libs/validation/src/pupil#validation-functions">Pupil documentation</a>.</p> -<p>The following functions are available by default:</p> -<table> -<thead> -<tr> -<th>Function</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>equals</code></td> -<td>Comparison</td> -</tr> -<tr> -<td><code>iEquals</code></td> -<td>Case insensitive comparison</td> -</tr> -<tr> -<td><code>sEquals</code></td> -<td>Type sensitive equals</td> -</tr> -<tr> -<td><code>siEquals</code></td> -<td>Type sensitive case insensitive equals</td> -</tr> -<tr> -<td><code>lenMin</code></td> -<td>Minimum length</td> -</tr> -<tr> -<td><code>lenMax</code></td> -<td>Maximum length</td> -</tr> -<tr> -<td><code>lenEquals</code></td> -<td>Exact length</td> -</tr> -<tr> -<td><code>min</code></td> -<td>Minimum value</td> -</tr> -<tr> -<td><code>max</code></td> -<td>Maximum value</td> -</tr> -<tr> -<td><code>between</code></td> -<td>Minimum and maximum value</td> -</tr> -<tr> -<td><code>in</code></td> -<td>One of the provided values</td> -</tr> -<tr> -<td><code>required</code></td> -<td>Must have a value</td> -</tr> -<tr> -<td><code>optional</code></td> -<td>Always valid</td> -</tr> -<tr> -<td><code>numeric</code></td> -<td>Numbers only</td> -</tr> -<tr> -<td><code>integer</code></td> -<td>Integer numbers only</td> -</tr> -<tr> -<td><code>alpha</code></td> -<td>Letters only</td> -</tr> -<tr> -<td><code>alphaNumeric</code></td> -<td>Numbers and letters only</td> -</tr> -<tr> -<td><code>email</code></td> -<td>Email address format</td> -</tr> -<tr> -<td><code>regex</code></td> -<td>A custom regular expression</td> -</tr> -<tr> -<td><code>equalsTo</code></td> -<td>Compare to another field by its key</td> -</tr> -</tbody> -</table> -<h5 id="sample-usage">Sample usage</h5> -<p>For case-insensitive comparison <code>iEquals</code> function in Pupil, -And you can use <code>||</code> for logical OR. Find documentation on these rules in the <a href="https://github.com/medic/cht-core/tree/master/shared-libs/validation/src/pupil#rule-strings">Pupil documentation</a>.</p> -<p>So you can do this : -<code>rule: 'iEquals(&quot;mary&quot;) || iEquals(&quot;john&quot;)'</code> -matches &ldquo;mary&rdquo;, &ldquo;Mary&rdquo;, &ldquo;john&rdquo;, &ldquo;John&rdquo;, &ldquo;JOhN&rdquo;, etc. Not &ldquo;maryjohn&rdquo;</p> -<h4 id="cht-validation-functions">CHT validation functions</h4> -<table> -<thead> -<tr> -<th>Function</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>unique(*fields)</code></td> -<td>Returns <code>true</code> if no existing valid report has the same value for all of the listed fields. Fields are compared to those at the top level and within <code>fields</code> for every report doc. To include the form type use <code>'form'</code> as one of the fields. Eg <code>unique('form', 'patient_id')</code> checks that this form was never submitted for this patient.</td> -</tr> -<tr> -<td><code>uniquePhone(field)</code></td> -<td>Returns <code>true</code> if contact with the phone number already exists. <em>Added in 4.3.0</em></td> -</tr> -<tr> -<td><code>validPhone(field)</code></td> -<td>Returns <code>true</code> if the field is a valid phone number else returns false. <em>Added in 4.3.1</em></td> -</tr> -<tr> -<td><code>uniqueWithin(*fields, time_period)</code></td> -<td>Same as <code>unique</code> but the last argument is the time period in which to search. Eg <code>uniqueWithin('form', 'patient_id', '1 week')</code> checks that the same form wasn&rsquo;t submitted for this patient in the past week.</td> -</tr> -<tr> -<td><code>exists(form_id, field)</code></td> -<td>Returns <code>true</code> if a report matches the <code>form_id</code> and value for <code>field</code>. This is useful to check that a patient was registered for a service before reporting about it. Eg <code>exists('REG', 'patient_id')</code> checks that a <code>REG</code> form was already submitted for a patient. As of 2.12 most uses of this function are obsolete because checking for a valid <code>patient_id</code> is done automatically by the <code>accept_patient_report</code> transition using <code>registration_not_found</code> in the <code>messages.event_type</code>.</td> -</tr> -<tr> -<td><code>isISOWeek(weekFieldName[, yearFieldName])</code></td> -<td>Returns <code>true</code> if the current report has a week value that is less or equal to the number of ISO weeks of the current year or the year value of the same report. The first parameter is the field name for the week and the second parameter is the field name for the year: <code>isISOWeek('week', 'year')</code>. If the second parameter is not specified, then the current year is used: <code>isISOWeek('week')</code>.</td> -</tr> -<tr> -<td><code>isAfter(duration)</code></td> -<td>Returns <code>true</code> if the date property comes after today plus the duration. For this to work, the property should be a date type. e.g. <code>isAfter('2 weeks')</code> checks that the date property is at least two weeks in the future from today. Negative values are also supported.</td> -</tr> -<tr> -<td><code>isBefore(duration)</code></td> -<td>Returns <code>true</code> if the date property comes before today minus the duration. For this to work, the property should be a date type. e.g. <code>isBefore('2 weeks')</code> checks that the date property is at least two weeks in the past from today. Negative values are also supported.</td> -</tr> -</tbody> -</table>Apps: contact-summary.templated.jshttps://docs.communityhealthtoolkit.org/apps/reference/contact-page/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/contact-page/ -<p>Contact profile pages display basic information about the contact along with their history and upcoming tasks. -A contact&rsquo;s profile page is defined by the <a href="#contact-summary">Fields</a>, <a href="#condition-cards">Cards</a>, and <a href="#care-guides">Care Guides</a> available.</p> -<p>Helper variables and functions for the contact summary can be defined in <code>contact-summary-extras.js</code>. There are several variables available to inspect to generate the summary information:</p> -<table> -<thead> -<tr> -<th>Variable</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>contact</code></td> -<td>The currently selected contact. This has minimal stubs for the <code>contact.parent</code>, so if you want to refer to a property on the parent use <code>lineage</code> below.</td> -</tr> -<tr> -<td><code>reports</code></td> -<td>An array of reports for the contact or for any of the contact&rsquo;s <code>person</code> children. Note that if the contact has more than 500 reports, only the 500 with the latest <code>reported_date</code> values will be provided. Prior to version <code>4.7.0</code>, only 50 reports were provided.</td> -</tr> -<tr> -<td><code>lineage</code></td> -<td>An array of the contact&rsquo;s parents (2.13+), eg <code>lineage[0]</code> is the parent, <code>lineage[1]</code> is the grandparent, etc. Each lineage entry has full information for the contact, so you can use <code>lineage[1].contact.phone</code>. <code>lineage</code> will include only those contacts which are visible to the logged in profile</td> -</tr> -<tr> -<td><code>targetDoc</code></td> -<td>Doc with <a href="https://docs.communityhealthtoolkit.org/core/overview/db-schema/#targets"><code>target</code></a> document of the contact, hydrated with the config information of every target it contains a value for. If there is no target document available (for example when viewing a contact that does not upload targets), this value will be <code>undefined</code>. This value might also be <code>undefined</code> if the contact has not yet synced the current target document. Added in <code>3.9.0</code>.</td> -</tr> -<tr> -<td><code>uhcStats</code></td> -<td>Object containing UHC stats information. Added in <code>v3.12.0</code></td> -</tr> -<tr> -<td><code>uhcStats.uhcInterval</code></td> -<td>Object containing the start and end date of UHC reporting period, it is calculated from the <code>uhc.visit_count.month_start_date</code> defined in the <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/#app_settingsjson">app settings</a>.</td> -</tr> -<tr> -<td><code>uhcStats.uhcInterval.start</code></td> -<td>Timestamp, start date of the UHC reporting period.</td> -</tr> -<tr> -<td><code>uhcStats.uhcInterval.end</code></td> -<td>Timestamp, end date of the UHC reporting period.</td> -</tr> -<tr> -<td><code>uhcStats.homeVisits</code></td> -<td>Object containing the contact&rsquo;s home visits stats. The <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/hierarchy/#app_settingsjson-contact_types">contact&rsquo;s type</a> should have <code>count_visits</code> enabled and the <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/#app_settingsjson">UHC visit count settings</a> should be defined, additionally this information is only available for users that have <code>can_view_uhc_stats</code> permission and that are not System Administrators.</td> -</tr> -<tr> -<td><code>uhcStats.homeVisits.lastVisitedDate</code></td> -<td>Timestamp, date of contact&rsquo;s last home visit.</td> -</tr> -<tr> -<td><code>uhcStats.homeVisits.count</code></td> -<td>Number of contact&rsquo;s home visits in the current reporting interval.</td> -</tr> -<tr> -<td><code>uhcStats.homeVisits.countGoal</code></td> -<td>Number, home visits goal, defined in the <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/#app_settingsjson">UHC visit count settings</a>.</td> -</tr> -<tr> -<td><code>cht</code></td> -<td>Object containing the <a href="#cht-api">CHT API</a> for contact summary, targets and tasks. Added in <code>v3.12.0</code></td> -</tr> -</tbody> -</table> -<h2 id="cht-api">CHT API</h2> -<p><em>Introduced in v3.12.0</em></p> -<p>Provides CHT-Core Framework&rsquo;s functions to contact summary, targets and tasks. The API is available in the <code>cht</code> reserved variable under the <code>v1</code> version.</p> -<table> -<thead> -<tr> -<th>Function</th> -<th>Arguments</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>hasPermissions(permissions, userRoles, chtPermissionsSettings)</td> -<td><code>permissions</code>: String or array of permission name(s).<br><code>userRoles</code>: (Optional) Array of user roles. Default to the current logged in user.<br><code>chtPermissionsSettings</code>: (Optional) Object of configured permissions in CHT-Core&rsquo;s settings. Default to the current instance&rsquo;s configured permissions.</td> -<td>Returns true if the user has the permission(s), otherwise returns false.</td> -</tr> -<tr> -<td>hasAnyPermission(permissionsGroups, userRoles, chtPermissionsSettings)</td> -<td><code>permissionsGroups</code>: Array of groups of permission name(s).<br><code>userRoles</code>: (Optional) Array of user roles. Default to the current logged in user.<br><code>chtPermissionsSettings</code>: (Optional) Object of configured permissions in CHT-Core&rsquo;s settings. Default to the current instance&rsquo;s configured permissions.</td> -<td>Returns true if the user has all the permissions of any of the provided groups, otherwise returns false.</td> -</tr> -<tr> -<td>getExtensionLib(name)</td> -<td><code>name</code>: String of script name</td> -<td>Returns an executable function identified by the given name configured as <a href="https://docs.communityhealthtoolkit.org/apps/reference/extension-libs/">extension-libs</a>.</td> -</tr> -</tbody> -</table> -<h3 id="cht-apis-code-samples">CHT API&rsquo;s code samples</h3> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">canEdit</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">cht</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">v1</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">hasPermissions</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;can_edit&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">canManagePlaces</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">cht</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">v1</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">hasPermissions</span><span style="color:#000;font-weight:bold">([</span><span style="color:#4e9a06">&#39;can_create_places&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;can_update_places&#39;</span><span style="color:#000;font-weight:bold">]);</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">hasAnyGroup</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">cht</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">v1</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">hasAnyPermission</span><span style="color:#000;font-weight:bold">([</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;can_view_messages&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;can_view_message_action&#39;</span><span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;can_view_reports&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;can_verify_reports&#39;</span><span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">]);</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">averageFn</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">cht</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">v1</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getExtensionLib</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;average.js&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span></code></pre></div> -<h2 id="contact-summary">Contact Summary</h2> -<p>Each field that can be shown on a contact&rsquo;s profile is defined as an object in the <code>fields</code> array of <code>contact-summary.templated.js</code>. The properties for each object determine how and when the field is shown.</p> -<h3 id="contact-summarytemplatedjs-fields"><code>contact-summary.templated.js .fields[]</code></h3> -<!-- If you change this table, update the duplicate descriptions in ### Cards --> -<table> -<thead> -<tr> -<th>property</th> -<th>type</th> -<th>description</th> -<th>required</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>label</code></td> -<td><code>string</code></td> -<td>A translation key which is shown with the field.</td> -<td>yes</td> -</tr> -<tr> -<td><code>icon</code></td> -<td><code>string</code></td> -<td>The name of the icon to display beside this field, as defined through the Configuration &gt; Icons page.</td> -<td>no</td> -</tr> -<tr> -<td><code>value</code></td> -<td><code>string</code></td> -<td>The value shown for the field.</td> -<td>yes</td> -</tr> -<tr> -<td><code>filter</code></td> -<td><code>string</code></td> -<td>The display filter to apply to the value, eg: <code>{ value: '2005-10-09', filter: 'age' }</code> will render as &ldquo;11 years&rdquo;. Common filters are: <code>age</code>, <code>phone</code>, <code>weeksPregnant</code>, <code>relativeDate</code>, <code>relativeDay</code>, <code>fullDate</code>, <code>simpleDate</code>, <code>simpleDateTime</code>, <code>lineage</code>, <code>resourceIcon</code>, <code>translate</code>. For the complete list of filters, and more details on what each does, check out the code in <a href="https://github.com/medic/cht-core/tree/3.15.x/webapp/src/ts/pipes"><code>webapp/src/ts/pipes</code> dir</a>.</td> -<td>no</td> -</tr> -<tr> -<td><code>width</code></td> -<td><code>integer</code></td> -<td>The horizontal space for the field. Common values are 12 for full width, 6 for half width, or 3 for quarter width. Default 12.</td> -<td>no</td> -</tr> -<tr> -<td><code>translate</code></td> -<td><code>boolean</code></td> -<td>Whether or not to translate the <code>value</code>. Defaults to false.</td> -<td>no</td> -</tr> -<tr> -<td><code>context</code></td> -<td><code>object</code></td> -<td>When <code>translate: true</code> and <code>value</code> uses <a href="https://angular-translate.github.io/docs/#/guide/06_variable-replacement">translation variables</a>, this value should provide the translation variables.</td> -<td>no</td> -</tr> -<tr> -<td><code>appliesIf</code></td> -<td><code>function()</code> or <code>boolean</code></td> -<td>Return true if the field should be shown.</td> -<td>no</td> -</tr> -<tr> -<td><code>appliesToType</code></td> -<td><code>string[]</code></td> -<td>Filters the contacts for which <code>appliesIf</code> will be evaluated. For example, <code>['person']</code> or <code>['clinic', 'health_center']</code>. It defaults to all types if it is not defined.</td> -<td>no</td> -</tr> -</tbody> -</table> -<!-- TODO: See [How to configure profile pages]() for an example. --> -<h2 id="condition-cards">Condition Cards</h2> -<p>Each condition card is defined as a card object in the <code>cards</code> array of <code>contact-summary.templated.js</code>. The properties for each object determine how and when the card and its fields are shown.</p> -<h3 id="contact-summarytemplatedjs-cards"><code>contact-summary.templated.js .cards[]</code></h3> -<!-- If you change the field data in this table, update the duplicate descriptions in ### Fields --> -<table> -<thead> -<tr> -<th>property</th> -<th>type</th> -<th>description</th> -<th>required</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>label</code></td> -<td><code>translation key</code></td> -<td>Label on top of card.</td> -<td>yes</td> -</tr> -<tr> -<td><code>appliesToType</code></td> -<td><code>string[]</code></td> -<td>A filter, so <code>appliesIf</code> is called only if the contact&rsquo;s type matches one or more of the elements. For example, <code>['person']</code>. Please, note that <code>['report']</code> is also allowed to create a report card. But, you cannot use it in conjunction with a contact&rsquo;s type. It defaults to all types if it is not defined.</td> -<td>no</td> -</tr> -<tr> -<td><code>appliesIf</code></td> -<td><code>function(report)</code> or <code>boolean</code></td> -<td>Return true if the field should be shown.</td> -<td>no</td> -</tr> -<tr> -<td><code>modifyContext</code></td> -<td><code>function(context, report)</code></td> -<td>Used to modify or add data which is passed as input to forms filled from the contact page.</td> -<td>no</td> -</tr> -<tr> -<td><code>fields</code></td> -<td><code>Array[]</code> of fields</td> -<td>The content of the card.</td> -<td>yes</td> -</tr> -<tr> -<td><code>fields[n].appliesIf</code></td> -<td><code>boolean</code> or <code>function(report)</code></td> -<td>Same as Fields.appliesIf above.</td> -<td></td> -</tr> -<tr> -<td><code>fields[n].label</code></td> -<td><code>string</code> or <code>function(report)</code></td> -<td>Label shown with the field.</td> -<td>yes</td> -</tr> -<tr> -<td><code>fields[n].icon</code></td> -<td><code>string</code> or <code>function(report)</code></td> -<td>The name of the icon to display beside this field, as defined through the Configuration &gt; Icons page.</td> -<td>no</td> -</tr> -<tr> -<td><code>fields[n].value</code></td> -<td><code>string</code> or <code>function(report)</code></td> -<td>The value shown for the field.</td> -<td>yes</td> -</tr> -<tr> -<td><code>fields[n].filter</code></td> -<td><code>string</code> or <code>function(report)</code></td> -<td>The display filter to apply to the value, eg: <code>{ value: '2005-10-09', filter: 'age' }</code> will render as &ldquo;11 years&rdquo;. Common filters are: <code>age</code>, <code>phone</code>, <code>weeksPregnant</code>, <code>relativeDate</code>, <code>relativeDay</code>, <code>fullDate</code>, <code>simpleDate</code>, <code>simpleDateTime</code>, <code>lineage</code>, <code>resourceIcon</code>, <code>translate</code>. For the complete list of filters, and more details on what each does, check out the code in <a href="https://github.com/medic/cht-core/tree/3.15.x/webapp/src/ts/pipes"><code>webapp/src/ts/pipes</code> dir</a>.</td> -<td>no</td> -</tr> -<tr> -<td><code>fields[n].width</code></td> -<td><code>integer</code> or <code>function(report)</code></td> -<td>The horizontal space for the field. Common values are 12 for full width, 6 for half width, or 3 for quarter width. Default 12.</td> -<td>no</td> -</tr> -<tr> -<td><code>fields[n].translate</code></td> -<td><code>boolean</code> or <code>function(report)</code></td> -<td>Whether or not to translate the <code>value</code>. Defaults to false.</td> -<td>no</td> -</tr> -<tr> -<td><code>fields[n].context</code></td> -<td><code>object</code></td> -<td>When <code>translate: true</code> and <code>value</code> uses <a href="https://angular-translate.github.io/docs/#/guide/06_variable-replacement">translation variables</a>, this value should provide the translation variables. Only supports properties <code>count</code> and <code>total</code> on cards.</td> -<td>no</td> -</tr> -</tbody> -</table> -<!-- TODO: See [How to configure profile pages]() for an example. --> -<h2 id="care-guides">Care Guides</h2> -<p>Each care guide accessible from a contact profile is defined as an <a href="https://docs.communityhealthtoolkit.org/apps/reference/forms/app/">App Form</a>. Context information can be provided to forms via the <code>context</code> object of <code>contact-summary.templated.js</code>.</p> -<p>To show an App Form on a contact&rsquo;s profile, the form&rsquo;s <code>expression</code> field in its properties file must evaluate to true for that contact. The context information from the profile is accessible as the variable <code>summary</code>.</p> -<p>The context data is also available directly within the app forms&rsquo; XForm calculations, as <code>instance('contact-summary')/context/${variable}</code>. For instance if <code>context.is_pregnant</code> is used in the contact summary, it can be accessed in an XForm field&rsquo;s calculation as <code>instance('contact-summary')/context/is_pregnant</code>. Note that these fields are not available when editing a previously completed form, or when accessing the form from outside of the profile page.</p> -<!-- TODO: See [How to configure profile pages]() and [How to build app forms]() for examples and more information. --> -<h2 id="code-samples">Code samples</h2> -<p>The following samples show how fields, cards, and care guide context comes together in the <code>contact-summary.templated.js</code> file.</p> -<h3 id="contact-summarytemplatedjs"><code>contact-summary.templated.js</code></h3> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#000">module</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">exports</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">context</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">use_cases</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">anc</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">isCoveredByUseCaseInLineage</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">lineage</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;anc&#39;</span><span style="color:#000;font-weight:bold">),</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">pnc</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">isCoveredByUseCaseInLineage</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">lineage</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;pnc&#39;</span><span style="color:#000;font-weight:bold">),</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#000">fields</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;person&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;patient_id&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">patient_id</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">width</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">4</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;person&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.age&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">date_of_birth</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">width</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">filter</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;age&#39;</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;person&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.parent&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">lineage</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">filter</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;lineage&#39;</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;!person&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">appliesIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">lineage</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">];</span> <span style="color:#000;font-weight:bold">},</span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.parent&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">lineage</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">filter</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;lineage&#39;</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#000">cards</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.profile.pregnancy&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;report&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">extras</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">isActivePregnancy</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">fields</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.profile.edd&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">report</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">fields</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">edd_8601</span><span style="color:#000;font-weight:bold">;</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">filter</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;relativeDay&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">width</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">12</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.profile.visit&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.profile.visits.of&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">translate</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">context</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">count</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">extras</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getSubsequentVisits</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">length</span><span style="color:#000;font-weight:bold">;</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">total</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">width</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">6</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.profile.risk.title&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">extras</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">isHighRiskPregnancy</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">?</span> <span style="color:#4e9a06">&#39;high&#39;</span> <span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;normal&#39;</span><span style="color:#000;font-weight:bold">;</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">translate</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">width</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">5</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">extras</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">isHighRiskPregnancy</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">?</span> <span style="color:#4e9a06">&#39;risk&#39;</span> <span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;&#39;</span><span style="color:#000;font-weight:bold">;</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">modifyContext</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">ctx</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">ctx</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">pregnant</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">;</span> <span style="color:#8f5902;font-style:italic">// don&#39;t show Create Pregnancy Report button -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">};</span> -</span></span></code></pre></div><h3 id="contact-summary-extrasjs"><code>contact-summary-extras.js</code></h3> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#000">module</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">exports</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">isActivePregnancy</span> <span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// ... -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">isCoveredByUseCaseInLineage</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">lineage</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">usecase</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// ... -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">isHighRiskPregnancy</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// ... -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">getSubsequentVisits</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// ... -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">};</span> -</span></span></code></pre></div><h2 id="build">Build</h2> -<p>To update the Contact profiles for an app, changes must be compiled into <code>app-settings</code>, then uploaded.</p> -<p><code>cht --local compile-app-settings backup-app-settings upload-app-settings</code></p>Apps: extension-libs/https://docs.communityhealthtoolkit.org/apps/reference/extension-libs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/extension-libs/ -<p><em>Introduced in v4.2.0</em></p> -<h2 id="introduction">Introduction</h2> -<p>Extension libraries are blocks of code that are cached with the CHT web application giving app developers a powerful tool to extend the CHT. This is an advanced feature and requires an app developer with some software development experience.</p> -<p>An example of a use for this feature is to provide a function to calculate a risk score based on a machine learning model. The function can then be called passing in values from app forms and return the result to be stored with the report.</p> -<h3 id="library">Library</h3> -<p>The first step is to create the js file to return the function that will be called by the web application. Create a new file using this template:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#000">module</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">exports</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">function</span><span style="color:#000;font-weight:bold">(</span><span style="color:#8f5902;font-style:italic">/* parameters */</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">result</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>Now populate the function as needed. For complex functions, it is recommended to use third party libraries (such as momentjs, lodash, etc) and use a bundler (eg: <a href="https://webpack.js.org/">webpack</a>) to make it easy to build a single file. It&rsquo;s recommended to use development best practices such as linting, unit tests, and minification to ensure quality and small download size.</p> -<h4 id="xpath-functions">xpath functions</h4> -<p>To call the function from within a form the parameters and return value will need to have a very specific structure to work with Enketo xforms.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;t&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#a40000">&lt;type&gt;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;v&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#a40000">&lt;value&gt;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>Where the type is one of &ldquo;bool&rdquo;, &ldquo;num&rdquo;, &ldquo;str&rdquo;, &ldquo;date&rdquo;, or &ldquo;arr&rdquo;. For example, to calculate the average of two inputs, you would use:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">getValue</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">function</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">obj</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">let</span> <span style="color:#000">val</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">obj</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">t</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;arr&#39;</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">val</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">obj</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">v</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">obj</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">v</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">length</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">obj</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">v</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">];</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#204a87;font-weight:bold">else</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">val</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">obj</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">v</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#000">val</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">parsed</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87">parseInt</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">val</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">textContent</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#204a87">isNaN</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">parsed</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">?</span> <span style="color:#0000cf;font-weight:bold">0</span> <span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">parsed</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">};</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#000">module</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">exports</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">function</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">first</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">second</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">average</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">getValue</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">first</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">+</span> <span style="color:#000">getValue</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">second</span><span style="color:#000;font-weight:bold">))</span> <span style="color:#ce5c00;font-weight:bold">/</span> <span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">t</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;num&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">v</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">average</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">};</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="uploading-to-the-cht">Uploading to the CHT</h3> -<p>Create a folder within the project configuration to contain the libraries, for example:</p> -<pre tabindex="0"><code>./extension-libs -average.js -calculate-risk-score.js -</code></pre><p>Now run the command: <code>cht upload-extension-libs</code></p> -<p>This will create or update a document to CouchDB with an ID of <code>extension-libs</code> with each of the configured scripts attached. Once this is created the webapp service worker will be updated so the libraries are cached on the phone ready for use offline.</p> -<h3 id="invoking-the-function">Invoking the function</h3> -<h4 id="cht-api">CHT API</h4> -<p>The function will now be available via the CHT API for <a href="https://docs.communityhealthtoolkit.org/apps/reference/tasks/">tasks</a>, <a href="https://docs.communityhealthtoolkit.org/apps/reference/targets/">targets</a>, and <a href="https://docs.communityhealthtoolkit.org/apps/reference/contact-page/">contact summary</a> configurations.</p> -<h4 id="cht-xpath-functions">CHT xPath functions</h4> -<p>To execute the function from within an xform use the <a href="https://docs.communityhealthtoolkit.org/apps/reference/forms/app/#chtextension-lib"><code>cht:extension-lib</code> xpath function</a>.</p>Apps: targets.jshttps://docs.communityhealthtoolkit.org/apps/reference/targets/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/targets/ -<p><img src="counts.png" alt="Percentages"></p> -<p><img src="percentages.png" alt="Percentages"></p> -<p>All targets are defined in the <code>targets.js</code> file as an array of objects according to the Targets schema defined below. Each object corresponds to a target widget that shows in the app. The order of objects in the array defines the display order of widgets on the Targets tab. The properties of the object are used to define when the target should appear, what it should look like, and the values it will display.</p> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/features/targets/">Targets Overview</a></p> -<h2 id="targetsjs"><code>targets.js</code></h2> -<table> -<thead> -<tr> -<th>property</th> -<th>type</th> -<th>description</th> -<th>required</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>id</code></td> -<td><code>string</code></td> -<td>An identifier for the target.</td> -<td>yes, unique</td> -</tr> -<tr> -<td><code>icon</code></td> -<td><code>string</code></td> -<td>The icon to show alongside the target. Should correspond with a value defined in <code>resources.json</code>.</td> -<td>no</td> -</tr> -<tr> -<td><code>translation_key</code></td> -<td><code>translation key</code></td> -<td>Translation key for the title of this target.</td> -<td>no, but recommended</td> -</tr> -<tr> -<td><code>subtitle_translation_key</code></td> -<td><code>translation key</code></td> -<td>Translation key for the subtitle of this target. If none supplied the subtitle will be blank.</td> -<td>no</td> -</tr> -<tr> -<td><code>percentage_count_translation_key</code></td> -<td><code>translation key</code></td> -<td>Translation key for the percentage value detail shown at the bottom of the target, eg &ldquo;(5 of 6 deliveries)&rdquo;. The translation context has <code>pass</code> and <code>total</code> variables available. If none supplied this defaults to <code>targets.count.default</code>.</td> -<td>no</td> -</tr> -<tr> -<td><code>context</code></td> -<td><code>string</code></td> -<td>A string containing a JavaScript expression. This widget will only be shown if the expression evaluates to true. Details of the current user is available through the variable <code>user</code>.</td> -<td>no</td> -</tr> -<tr> -<td><code>type</code></td> -<td><code>'count'</code> or <code>'percent'</code></td> -<td>The type of the widget.</td> -<td>yes</td> -</tr> -<tr> -<td><code>goal</code></td> -<td><code>integer</code></td> -<td>For targets with <code>type: 'percent'</code>, an integer from 0 to 100. For <code>type: 'count'</code>, any positive number. If there is no goal, put -1.</td> -<td>yes</td> -</tr> -<tr> -<td><code>appliesTo</code></td> -<td><code>'contacts'</code> or <code>'reports'</code></td> -<td>Do you want to count reports or contacts? This attribute controls the behavior of other attributes herein.</td> -<td>yes</td> -</tr> -<tr> -<td><code>appliesToType</code></td> -<td>If <code>appliesTo: 'reports'</code>, an array of form codes. If <code>appliesTo: 'contacts'</code>, an array of contact types.</td> -<td>Filters the contacts or reports for which <code>appliesIf</code> will be evaluated. For example, <code>['person']</code> or <code>['clinic', 'health_center']</code>. For example, <code>['pregnancy']</code> or <code>['P', 'pregnancy']</code>.</td> -<td>no</td> -</tr> -<tr> -<td><code>appliesIf</code></td> -<td><code>function(contact, report)</code></td> -<td>If <code>appliesTo: 'contacts'</code>, this function is invoked once per contact and <code>report</code> is undefined. If <code>appliesTo: 'reports'</code>, this function is invoked once per report. Return true to count this document. For <code>type: 'percent'</code>, this controls the denominator.</td> -<td>no</td> -</tr> -<tr> -<td><code>passesIf</code></td> -<td><code>function(contact, report)</code></td> -<td>For <code>type: 'percent'</code>, return true to increment the numerator.</td> -<td>yes, if <code>type: 'percent'</code>. Forbidden when <code>groupBy</code> is defined.</td> -</tr> -<tr> -<td><code>date</code></td> -<td><code>'reported'</code> or <code>'now'</code> or <code>function(contact, report)</code></td> -<td>When <code>'reported'</code>, the target will count documents with a <code>reported_date</code> within the current month. When <code>'now'</code>, target includes all documents. A function can be used to indicate when the document should be counted. When this property is undefined or the value is null the default is &rsquo;now'.</td> -<td>no</td> -</tr> -<tr> -<td><code>idType</code></td> -<td><code>'report'</code> or <code>'contact'</code> or <code>function(contact, report)</code></td> -<td>The target&rsquo;s values are incremented once per unique ID. To count individual contacts that have one or more reports that apply, use <code>'contact'</code>. Use <code>'report'</code> to count all reports, even if there are multiple that apply for a single contact. If you need more than a single count for each applying contact or report then a custom function can be used returning an array with unique IDs — one element for each count.</td> -<td>no</td> -</tr> -<tr> -<td><code>groupBy</code></td> -<td><code>function(contact, report)</code> returning string</td> -<td>Allows for target ids to be aggregated and scored in groups. Not required for most targets. Use with passesIfGroupCount.</td> -<td>no</td> -</tr> -<tr> -<td><code>passesIfGroupCount</code></td> -<td><code>object</code></td> -<td>The criteria to determine if the target ids within a group should be counted as passing</td> -<td>yes when <code>groupBy</code> is defined</td> -</tr> -<tr> -<td><code>passesIfGroupCount.gte</code></td> -<td><code>number</code></td> -<td>The group should be counted as passing if the number of target ids in the group is greater-than-or-equal-to this value</td> -<td>yes when <code>groupBy</code> is defined</td> -</tr> -<tr> -<td><code>dhis</code></td> -<td><code>object</code> or <code>object[]</code></td> -<td>Settings relevant to the <a href="https://docs.communityhealthtoolkit.org/apps/guides/integrations/dhis2-aggregate/#configuration">DHIS2 Integration</a></td> -<td>no</td> -</tr> -<tr> -<td><code>dhis[n].dataElement</code></td> -<td><code>string</code></td> -<td>The hash id of a data element configured in the DHIS2 data set you&rsquo;re integrating with</td> -<td>yes</td> -</tr> -<tr> -<td><code>dhis[n].dataSet</code></td> -<td><code>string</code></td> -<td>The hash id of the data set that contains the data element you&rsquo;re integrating with. If this is left undefined, the data element will appear in all data sets.</td> -<td>no</td> -</tr> -<tr> -<td><code>visible</code></td> -<td><code>boolean</code></td> -<td>Whether the target is visible in the targets page. <strong>Default: true</strong></td> -<td>no</td> -</tr> -<tr> -<td><code>aggregate</code></td> -<td><code>boolean</code></td> -<td>As of 3.9, defines whether the target will be displayed on the TargetAggregates page</td> -<td>no</td> -</tr> -</tbody> -</table> -<h2 id="utils">Utils</h2> -<p>Utility functions in the Core Framework can make common tasks much easier. These are available only for Tasks and Targets. To use the function call <code>Utils.&lt;function-name&gt;(&lt;params&gt;)</code>, for example <code>Utils.addDate(report.reported_date, 10)</code>.</p> -<table> -<thead> -<tr> -<th>Name</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>isTimely(date, event)</code></td> -<td>Returns true if the given date is after the start date and before the end date of the event.</td> -</tr> -<tr> -<td><code>addDate(date, days)</code></td> -<td>Returns a new Date set to midnight the given number of days after the given date. If no date is given the date defaults to today.</td> -</tr> -<tr> -<td><code>getLmpDate(doc)</code></td> -<td>Attempts to work out the LMP from the given doc. If no LMP is given it defaults to four weeks before the reported_date.</td> -</tr> -<tr> -<td><code>getSchedule(name)</code></td> -<td>Returns the task schedule with the given name from the configuration.</td> -</tr> -<tr> -<td><code>getMostRecentTimestamp(reports, form)</code></td> -<td>Returns the reported_date of the most recent of the reports with form ID matching the given form.</td> -</tr> -<tr> -<td><code>getMostRecentReport(reports, form)</code></td> -<td>Like <code>getMostRecentTimestamp</code> but returns the report, not just the reported_date. From CHT v3.14.0, it also accepts an array of forms.</td> -</tr> -<tr> -<td><code>isFormSubmittedInWindow(reports, form, start, end)</code></td> -<td>Returns true if any of the given reports are for the given form and were reported after start and before end.</td> -</tr> -<tr> -<td><code>isFirstReportNewer(firstReport, secondReport)</code></td> -<td>Returns true if the firstReport was reported before the secondReport.</td> -</tr> -<tr> -<td><code>isDateValid(date)</code></td> -<td>Returns true if the given date is a validate JavaScript Date.</td> -</tr> -<tr> -<td><code>now()</code></td> -<td>Returns the current Date.</td> -</tr> -<tr> -<td><code>getField(report, fieldPath)</code></td> -<td>Returns the value of the specified fieldPath. The fieldPath is a period separated json path.</td> -</tr> -<tr> -<td><code>MS_IN_DAY</code></td> -<td>A constant for the number of milliseconds in a day.</td> -</tr> -</tbody> -</table> -<p>Please open <a href="https://github.com/medic/cht-core/issues/new">an issue</a> if you&rsquo;d like other functions included.</p> -<h2 id="cht-api">CHT API</h2> -<p><em>Introduced in v3.12.0</em></p> -<p>Provides CHT-Core Framework&rsquo;s functions to contact summary, targets and tasks. The API is available in the <code>cht</code> reserved variable under the <code>v1</code> version.</p> -<table> -<thead> -<tr> -<th>Function</th> -<th>Arguments</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>hasPermissions(permissions, userRoles, chtPermissionsSettings)</td> -<td><code>permissions</code>: String or array of permission name(s).<br><code>userRoles</code>: (Optional) Array of user roles. Default to the current logged in user.<br><code>chtPermissionsSettings</code>: (Optional) Object of configured permissions in CHT-Core&rsquo;s settings. Default to the current instance&rsquo;s configured permissions.</td> -<td>Returns true if the user has the permission(s), otherwise returns false.</td> -</tr> -<tr> -<td>hasAnyPermission(permissionsGroups, userRoles, chtPermissionsSettings)</td> -<td><code>permissionsGroups</code>: Array of groups of permission name(s).<br><code>userRoles</code>: (Optional) Array of user roles. Default to the current logged in user.<br><code>chtPermissionsSettings</code>: (Optional) Object of configured permissions in CHT-Core&rsquo;s settings. Default to the current instance&rsquo;s configured permissions.</td> -<td>Returns true if the user has all the permissions of any of the provided groups, otherwise returns false.</td> -</tr> -<tr> -<td>getExtensionLib(name)</td> -<td><code>name</code>: String of script name</td> -<td>Returns an executable function identified by the given name configured as <a href="https://docs.communityhealthtoolkit.org/apps/reference/extension-libs/">extension-libs</a>.</td> -</tr> -</tbody> -</table> -<h3 id="cht-apis-code-samples">CHT API&rsquo;s code samples</h3> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">canEdit</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">cht</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">v1</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">hasPermissions</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;can_edit&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">canManagePlaces</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">cht</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">v1</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">hasPermissions</span><span style="color:#000;font-weight:bold">([</span><span style="color:#4e9a06">&#39;can_create_places&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;can_update_places&#39;</span><span style="color:#000;font-weight:bold">]);</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">hasAnyGroup</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">cht</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">v1</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">hasAnyPermission</span><span style="color:#000;font-weight:bold">([</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;can_view_messages&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;can_view_message_action&#39;</span><span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;can_view_reports&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;can_verify_reports&#39;</span><span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">]);</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">averageFn</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">cht</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">v1</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getExtensionLib</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;average.js&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span></code></pre></div> -<h2 id="code-samples">Code Samples</h2> -<p>This sample <code>targets.js</code> generates three widgets, and uses functions written in the <code>targets-extras.js</code> file.</p> -<h3 id="targetsjs-1"><code>targets.js</code></h3> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">isHealthyDelivery</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">countReportsSubmittedInWindow</span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">require</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;./targets-extras&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#000">module</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">exports</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// BIRTHS THIS MONTH -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;births-this-month&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;count&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;infant&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">goal</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.births.title&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">subtitle_translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.this_month.subtitle&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesTo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;reports&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">isHealthyDelivery</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">date</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;reported&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// % DELIVERIES ALL TIME WITH 1+ VISITS -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;delivery-with-min-1-visit&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;percent&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;nurse&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">goal</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">100</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.delivery_1_visit.title&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">subtitle_translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.all_time.subtitle&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesTo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;reports&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">idType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;report&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">isHealthyDelivery</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">passesIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">c</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">r</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">var</span> <span style="color:#000">visits</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">countReportsSubmittedInWindow</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">c</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reports</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">antenatalForms</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">r</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reported_date</span> <span style="color:#ce5c00;font-weight:bold">-</span> <span style="color:#000">MAX_DAYS_IN_PREGNANCY</span><span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">MS_IN_DAY</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">r</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reported_date</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">visits</span> <span style="color:#ce5c00;font-weight:bold">&gt;</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">date</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;now&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;2-home-visits-per-family&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;home-visit&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;percent&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">goal</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">100</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">`target.2-home-visits-per-family`</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">context</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;user.role === &#34;chw&#34;&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">date</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;reported&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesTo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contacts&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;person&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">idType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">contact</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// Determines the target ids which will be in the group. -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#8f5902;font-style:italic">// eg. &#34;family1~2000-02-15&#34; and &#34;family1~2000-02-16&#34; -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">householdVisitDates</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">new</span> <span style="color:#000">Set</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reports</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">map</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000">toDateString</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reported_date</span><span style="color:#000;font-weight:bold">)));</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">familyId</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">_id</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#204a87">Array</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">from</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">householdVisitDates</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">map</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">date</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#4e9a06">`</span><span style="color:#4e9a06">${</span><span style="color:#000">familyId</span><span style="color:#4e9a06">}</span><span style="color:#4e9a06">~</span><span style="color:#4e9a06">${</span><span style="color:#000">date</span><span style="color:#4e9a06">}</span><span style="color:#4e9a06">`</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">groupBy</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">contact</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">_id</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">passesIfGroupCount</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">gte</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">2</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">]</span> -</span></span></code></pre></div><h3 id="targets-extrasjs"><code>targets-extras.js</code></h3> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#000">module</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">exports</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">isHealthyDelivery</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">c</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">r</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">r</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">form</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;D&#39;</span> <span style="color:#ce5c00;font-weight:bold">||</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">r</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">form</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;delivery&#39;</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">r</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">fields</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">pregnancy_outcome</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;healthy&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#000">countReportsSubmittedInWindow</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">reports</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">form</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">start</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">end</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">var</span> <span style="color:#000">reportsFound</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">reports</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">forEach</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">function</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">r</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">form</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">indexOf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">r</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">form</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">&gt;=</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">r</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reported_date</span> <span style="color:#ce5c00;font-weight:bold">&gt;=</span> <span style="color:#000">start</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">r</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reported_date</span> <span style="color:#ce5c00;font-weight:bold">&lt;=</span> <span style="color:#000">end</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">reportsFound</span><span style="color:#ce5c00;font-weight:bold">++</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">});</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">reportsFound</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">};</span> -</span></span></code></pre></div><h2 id="build">Build</h2> -<p>To build your targets into your app, you must compile them into app-settings, then upload them to your instance.</p> -<p><code>cht --local compile-app-settings backup-app-settings upload-app-settings</code></p>Apps: tasks.jshttps://docs.communityhealthtoolkit.org/apps/reference/tasks/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/tasks/ -<p><img src="task-with-description.png" alt="task"></p> -<p>Task generation is configured in the <code>tasks.js</code> file. This file is a JavaScript module which defines an array of objects conforming to the Task schema detailed below. When defining tasks, all the data about contacts on the device (both people and places) along with all their reports are available. Tasks are available only for users of type &ldquo;restricted to their place&rdquo;. Tasks can pull in fields from reports and pass data as inputs to the form that opens when the task is selected, enabling richer user experiences.</p> -<p>Task generation occurs on the client periodically and creates documents which track the status of the task over time. To avoid performance issues the developer needs to be conscious about generating too many tasks. For example, to remind a user to do something every day, you could generate one task for each day and fill up the user&rsquo;s device. The recommended approach is to only generate the tasks for the near future, or only once the previous task is resolved. To limit the impact of this misconfiguration, the CHT will only generate tasks that can be completed between 60 days in the past, and (as of 4.0.0) 180 days in the future.</p> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/features/tasks/">Tasks Overview</a></p> -<h2 id="tasksjs"><code>tasks.js</code></h2> -<table> -<thead> -<tr> -<th>property</th> -<th>type</th> -<th>description</th> -<th>required</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>name</code></td> -<td><code>string</code></td> -<td>A unique identifier for the task. Used for querying task completeness.</td> -<td>yes, unique</td> -</tr> -<tr> -<td><code>icon</code></td> -<td><code>string</code></td> -<td>The icon to show alongside the task. Should correspond with a value defined in <code>resources.json</code>.</td> -<td>no</td> -</tr> -<tr> -<td><code>title</code></td> -<td><code>translation key</code></td> -<td>The title of the task (labeled above).</td> -<td>yes</td> -</tr> -<tr> -<td><code>appliesTo</code></td> -<td><code>'contacts'</code> or <code>'reports'</code></td> -<td>Do you want to emit one task per report, or one task per contact? See <a href="https://docs.communityhealthtoolkit.org/apps/guides/tasks/task-schema-parameters/">Understanding the Parameters in the Task Schema</a>.</td> -<td>yes</td> -</tr> -<tr> -<td><code>appliesIf</code></td> -<td><code>function(contact, report)</code></td> -<td>If <code>appliesTo: 'contacts'</code>, this function is invoked once per contact and <code>report</code> is undefined. If <code>appliesTo: 'reports'</code>, this function is invoked once per report. Return true if the task should appear for the given documents. See <a href="https://docs.communityhealthtoolkit.org/apps/guides/tasks/task-schema-parameters/">Understanding the Parameters in the Task Schema</a>.</td> -<td>no</td> -</tr> -<tr> -<td><code>appliesToType</code></td> -<td><code>string[]</code></td> -<td>Filters the contacts or reports for which <code>appliesIf</code> will be evaluated. If <code>appliesTo: 'reports'</code>, this is an array of form codes. If <code>appliesTo: 'contacts'</code>, this is an array of contact types. For example, <code>['person']</code> or <code>['clinic', 'health_center']</code>. For example, <code>['pregnancy']</code> or <code>['P', 'pregnancy']</code>. See <a href="https://docs.communityhealthtoolkit.org/apps/guides/tasks/task-schema-parameters/">Understanding the Parameters in the Task Schema</a>.</td> -<td>no</td> -</tr> -<tr> -<td><code>contactLabel</code></td> -<td><code>string</code> or <code>function(contact, report)</code></td> -<td>Controls the label describing the subject of the task. Defaults to the name of the contact (<code>contact.contact.name</code>).</td> -<td>no</td> -</tr> -<tr> -<td><code>resolvedIf</code></td> -<td><code>function(contact, report, event, dueDate)</code></td> -<td>Return true to mark the task as &ldquo;resolved&rdquo;. A resolved task uses memory on the phone, but is not displayed.</td> -<td>no, if any <code>actions[n].type</code> is <code>'report'</code></td> -</tr> -<tr> -<td><code>events</code></td> -<td><code>object[]</code></td> -<td>An event is used to specify the timing of the task.</td> -<td>yes</td> -</tr> -<tr> -<td><code>events[n].id</code></td> -<td><code>string</code></td> -<td>A descriptive identifier. Used for querying task completeness.</td> -<td>yes if task has multiple events, unique</td> -</tr> -<tr> -<td><code>events[n].days</code></td> -<td><code>integer</code></td> -<td>Number of days after the doc&rsquo;s <code>reported_date</code> that the event is due</td> -<td>yes, if <code>dueDate</code> is not set</td> -</tr> -<tr> -<td><code>events[n].dueDate</code></td> -<td><code>function(event, contact, report)</code></td> -<td>Returns a <code>Date</code> object for the day when this event is due.</td> -<td>yes, if <code>days</code> is not set</td> -</tr> -<tr> -<td><code>events[n].start</code></td> -<td><code>integer</code></td> -<td>Number of days to show the task before it is due.</td> -<td>yes</td> -</tr> -<tr> -<td><code>events[n].end</code></td> -<td><code>integer</code></td> -<td>Number of days to show the task after it is due.</td> -<td>yes</td> -</tr> -<tr> -<td><code>actions</code></td> -<td><code>object[]</code></td> -<td>The actions (forms) that a user can access after clicking on a task. If you put multiple forms here, the user will see a task summary screen where they can select which action they would like to complete.</td> -<td>yes</td> -</tr> -<tr> -<td><code>actions[n].type</code></td> -<td><code>'report'</code> or <code>'contact'</code></td> -<td>When <code>'report'</code>, the action opens the given form. When <code>'contact'</code>, the action redirects to a contact&rsquo;s profile page. Defaults to &lsquo;report&rsquo;.</td> -<td>no</td> -</tr> -<tr> -<td><code>actions[n].form</code></td> -<td><code>string</code></td> -<td>The code of the form that should open when you select the action.</td> -<td>yes, if <code>actions[n].type</code> is <code>'report'</code></td> -</tr> -<tr> -<td><code>actions[n].label</code></td> -<td><code>translation key</code></td> -<td>The label that should appear on the task summary screen if multiple actions are present.</td> -<td>no</td> -</tr> -<tr> -<td><code>actions[n].modifyContent</code></td> -<td><code>function (content, contact, report, event)</code></td> -<td>Set the values on the content object to control the data which will be passed as <code>inputs</code> to the form which opens when the action is selected. See <a href="https://docs.communityhealthtoolkit.org/apps/guides/tasks/pass-data-to-form/">Passing data from a task into the app form</a>.</td> -<td>no</td> -</tr> -<tr> -<td><code>priority</code></td> -<td><code>object</code> or <code>function(contact, report)</code> returning object of same schema</td> -<td>Controls the &ldquo;high risk&rdquo; line seen above.</td> -<td>no</td> -</tr> -<tr> -<td><code>priority.level</code></td> -<td><code>high</code> or <code>medium</code></td> -<td>Tasks that are <code>high</code> will display a high risk icon with the task. Default: <code>medium</code>.</td> -<td>no</td> -</tr> -<tr> -<td><code>priority.label</code></td> -<td><code>translation key</code></td> -<td>Text shown with the task associated to the risk level.</td> -<td>no</td> -</tr> -</tbody> -</table> -<h2 id="utils">Utils</h2> -<p>Utility functions in the Core Framework can make common tasks much easier. These are available only for Tasks and Targets. To use the function call <code>Utils.&lt;function-name&gt;(&lt;params&gt;)</code>, for example <code>Utils.addDate(report.reported_date, 10)</code>.</p> -<table> -<thead> -<tr> -<th>Name</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>isTimely(date, event)</code></td> -<td>Returns true if the given date is after the start date and before the end date of the event.</td> -</tr> -<tr> -<td><code>addDate(date, days)</code></td> -<td>Returns a new Date set to midnight the given number of days after the given date. If no date is given the date defaults to today.</td> -</tr> -<tr> -<td><code>getLmpDate(doc)</code></td> -<td>Attempts to work out the LMP from the given doc. If no LMP is given it defaults to four weeks before the reported_date.</td> -</tr> -<tr> -<td><code>getSchedule(name)</code></td> -<td>Returns the task schedule with the given name from the configuration.</td> -</tr> -<tr> -<td><code>getMostRecentTimestamp(reports, form)</code></td> -<td>Returns the reported_date of the most recent of the reports with form ID matching the given form.</td> -</tr> -<tr> -<td><code>getMostRecentReport(reports, form)</code></td> -<td>Like <code>getMostRecentTimestamp</code> but returns the report, not just the reported_date. From CHT v3.14.0, it also accepts an array of forms.</td> -</tr> -<tr> -<td><code>isFormSubmittedInWindow(reports, form, start, end)</code></td> -<td>Returns true if any of the given reports are for the given form and were reported after start and before end.</td> -</tr> -<tr> -<td><code>isFirstReportNewer(firstReport, secondReport)</code></td> -<td>Returns true if the firstReport was reported before the secondReport.</td> -</tr> -<tr> -<td><code>isDateValid(date)</code></td> -<td>Returns true if the given date is a validate JavaScript Date.</td> -</tr> -<tr> -<td><code>now()</code></td> -<td>Returns the current Date.</td> -</tr> -<tr> -<td><code>getField(report, fieldPath)</code></td> -<td>Returns the value of the specified fieldPath. The fieldPath is a period separated json path.</td> -</tr> -<tr> -<td><code>MS_IN_DAY</code></td> -<td>A constant for the number of milliseconds in a day.</td> -</tr> -</tbody> -</table> -<p>Please open <a href="https://github.com/medic/cht-core/issues/new">an issue</a> if you&rsquo;d like other functions included.</p> -<h2 id="cht-api">CHT API</h2> -<p><em>Introduced in v3.12.0</em></p> -<p>Provides CHT-Core Framework&rsquo;s functions to contact summary, targets and tasks. The API is available in the <code>cht</code> reserved variable under the <code>v1</code> version.</p> -<table> -<thead> -<tr> -<th>Function</th> -<th>Arguments</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>hasPermissions(permissions, userRoles, chtPermissionsSettings)</td> -<td><code>permissions</code>: String or array of permission name(s).<br><code>userRoles</code>: (Optional) Array of user roles. Default to the current logged in user.<br><code>chtPermissionsSettings</code>: (Optional) Object of configured permissions in CHT-Core&rsquo;s settings. Default to the current instance&rsquo;s configured permissions.</td> -<td>Returns true if the user has the permission(s), otherwise returns false.</td> -</tr> -<tr> -<td>hasAnyPermission(permissionsGroups, userRoles, chtPermissionsSettings)</td> -<td><code>permissionsGroups</code>: Array of groups of permission name(s).<br><code>userRoles</code>: (Optional) Array of user roles. Default to the current logged in user.<br><code>chtPermissionsSettings</code>: (Optional) Object of configured permissions in CHT-Core&rsquo;s settings. Default to the current instance&rsquo;s configured permissions.</td> -<td>Returns true if the user has all the permissions of any of the provided groups, otherwise returns false.</td> -</tr> -<tr> -<td>getExtensionLib(name)</td> -<td><code>name</code>: String of script name</td> -<td>Returns an executable function identified by the given name configured as <a href="https://docs.communityhealthtoolkit.org/apps/reference/extension-libs/">extension-libs</a>.</td> -</tr> -</tbody> -</table> -<h3 id="cht-apis-code-samples">CHT API&rsquo;s code samples</h3> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">canEdit</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">cht</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">v1</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">hasPermissions</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;can_edit&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">canManagePlaces</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">cht</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">v1</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">hasPermissions</span><span style="color:#000;font-weight:bold">([</span><span style="color:#4e9a06">&#39;can_create_places&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;can_update_places&#39;</span><span style="color:#000;font-weight:bold">]);</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">hasAnyGroup</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">cht</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">v1</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">hasAnyPermission</span><span style="color:#000;font-weight:bold">([</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;can_view_messages&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;can_view_message_action&#39;</span><span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;can_view_reports&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;can_verify_reports&#39;</span><span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">]);</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">averageFn</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">cht</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">v1</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getExtensionLib</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;average.js&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span></code></pre></div> -<h2 id="code-samples">Code samples</h2> -<h3 id="basic-task">Basic task</h3> -<p>This sample <code>tasks.js</code> generates two postnatal-visit tasks for each delivery form. The tasks are due 7 and 14 days after the delivery report was submitted. Each task is displayed for 2 days before the due date and 2 days after the due date.</p> -<h4 id="tasksjs-1">tasks.js</h4> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#000">module</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">exports</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;mother-child&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">title</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;task.postnatal_followup&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesTo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;reports&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> <span style="color:#4e9a06">&#39;delivery&#39;</span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">actions</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">form</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;postnatal_visit&#39;</span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">events</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;postnatal-followup-1&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">days</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">7</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">start</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">end</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;postnatal-followup-2&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">days</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">14</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">start</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">end</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">resolvedIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">report</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">event</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">dueDate</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">Utils</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">isFormSubmittedInWindow</span><span style="color:#000;font-weight:bold">(</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reports</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#39;delivery&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">Utils</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">addDate</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">dueDate</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#000">event</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">start</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">getTime</span><span style="color:#000;font-weight:bold">(),</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">Utils</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">addDate</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">dueDate</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">event</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">end</span> <span style="color:#ce5c00;font-weight:bold">+</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">getTime</span><span style="color:#000;font-weight:bold">()</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">];</span> -</span></span></code></pre></div><h3 id="tasks-with-functions">Tasks with functions</h3> -<p>These samples show more complex tasks which use functions kept in a separate <code>nools-extras.js</code> file</p> -<h4 id="tasksjs-2"><code>tasks.js</code></h4> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">extras</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">require</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;./nools-extras&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">isFormFromArraySubmittedInWindow</span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">extras</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#000">module</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">exports</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// PNC TASK 1: If a home delivery, needs clinic tasks -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;mother-child&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">title</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;task.postnatal_followup.title&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesTo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;reports&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> <span style="color:#4e9a06">&#39;D&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;delivery&#39;</span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">c</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">r</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">isCoveredByUseCase</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">c</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;pnc&#39;</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">r</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">fields</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">r</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">fields</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">delivery_code</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">r</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">fields</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">delivery_code</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">toUpperCase</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#ce5c00;font-weight:bold">!==</span> <span style="color:#4e9a06">&#39;F&#39;</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">actions</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">form</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#4e9a06">&#39;postnatal_visit&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// Pass content that will be used within the task form -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">modifyContent</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">content</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">c</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">r</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">event</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">content</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">delivery_place</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#4e9a06">&#39;home&#39;</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">content</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">event_id</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">event</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">id</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">events</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;postnatal-visit&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">days</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">start</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">end</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">priority</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">level</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;high&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">locale</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#4e9a06">&#39;en&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">content</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#4e9a06">&#39;Home Birth&#39;</span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">resolvedIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">c</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">r</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">event</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">dueDate</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// Resolved if there a visit report received in time window or a newer pregnancy -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">r</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reported_date</span> <span style="color:#ce5c00;font-weight:bold">&lt;</span> <span style="color:#000">extras</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getNewestDeliveryTimestamp</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">c</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">||</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">r</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reported_date</span> <span style="color:#ce5c00;font-weight:bold">&lt;</span> <span style="color:#000">extras</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getNewestPregnancyTimestamp</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">c</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">||</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">isFormFromArraySubmittedInWindow</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">c</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reports</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">extras</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">postnatalForms</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">Utils</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">addDate</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">dueDate</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#000">event</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">start</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">getTime</span><span style="color:#000;font-weight:bold">(),</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">Utils</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">addDate</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">dueDate</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">event</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">end</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">getTime</span><span style="color:#000;font-weight:bold">());</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// Option 1a: Place-based task: Family survey when place is created, then every 6 months -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;family&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">title</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;task.family_survey.title&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesTo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contacts&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> <span style="color:#4e9a06">&#39;clinic&#39;</span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">actions</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">form</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#4e9a06">&#39;family_survey&#39;</span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">events</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;family-survey&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">days</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">start</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">end</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">14</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">resolvedIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">c</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">r</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">event</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">dueDate</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// Resolved if there a family survey received in time window -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">isFormFromArraySubmittedInWindow</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">c</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reports</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;family_survey&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">Utils</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">addDate</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">dueDate</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#000">event</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">start</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">getTime</span><span style="color:#000;font-weight:bold">(),</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">Utils</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">addDate</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">dueDate</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">event</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">end</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">getTime</span><span style="color:#000;font-weight:bold">());</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// Regular check for infants -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;infant&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">title</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;task.infant.title&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesTo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contacts&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> <span style="color:#4e9a06">&#39;person&#39;</span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">actions</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">form</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#4e9a06">&#39;infant_assessment&#39;</span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">events</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;infant_asssessment-q1&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">days</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">91</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">start</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">7</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">end</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">14</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;infant_asssessment-q2&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">days</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">182</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">start</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">7</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">end</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">14</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;infant_asssessment-q3&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">days</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">273</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">start</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">7</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">end</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">14</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;infant_asssessment-q4&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">days</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">365</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">start</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">7</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">end</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">14</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// Option 2: Place-based task: Family survey every 6 months -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;family&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">title</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;task.family_survey.title&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesTo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contacts&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> <span style="color:#4e9a06">&#39;clinic&#39;</span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">extras</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">needsFamilySurvey</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#8f5902;font-style:italic">// function returns true if family doesn&#39;t have survey in previous 6 months -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">actions</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">form</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#4e9a06">&#39;family_survey&#39;</span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">events</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;family-survey&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">start</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">end</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">14</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">dueDate</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">extras</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getNextFamilySurveyDate</span> <span style="color:#8f5902;font-style:italic">// function gets expected date of next family survey -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">resolvedIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">c</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">r</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">event</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">dueDate</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// Resolved if there a family survey received in time window -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">isFormFromArraySubmittedInWindow</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">c</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reports</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;family_survey&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">Utils</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">addDate</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">dueDate</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#000">event</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">start</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">getTime</span><span style="color:#000;font-weight:bold">(),</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">Utils</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">addDate</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">dueDate</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">event</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">end</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">getTime</span><span style="color:#000;font-weight:bold">());</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">]</span> -</span></span></code></pre></div><h4 id="nools-extrasjs"><code>nools-extras.js</code></h4> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#000">module</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">exports</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">isCoveredByUseCase</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">usecase</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// ... -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">getNewestDeliveryTimestamp</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">c</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// ... -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">getNewestPregnancyTimestamp</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">c</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// ... -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">isFormFromArraySubmittedInWindow</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">reports</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">formsArray</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">startTime</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">endTime</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// ... -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">};</span> -</span></span></code></pre></div><h3 id="default-resolvedif-method">Default resolvedIf method</h3> -<p>If the <code>resolvedIf</code> is undefined in an action of type <code>report</code>, then <code>resolvedIf</code> is going to default to <code>defaultResolvedIf</code> method.</p> -<p>The <code>defaultResolvedIf</code> method returns <code>true</code> if it finds any report assigned to the contact that matches the <code>form</code> defined in the action of type <code>report</code>. Only the reports submitted during a specific time period are considered:</p> -<ul> -<li>For a contact-based task, the period is the same as the task window period i.e. when the task is visible.</li> -<li>For a report based task, the period is determined between <code>start</code> and <code>end</code> as: -<ul> -<li><code>start</code>: the latest date between start of the task window and one millisecond after the report&rsquo;s reported date</li> -<li><code>end</code>: end of the task window</li> -</ul> -</li> -</ul> -<p>You can also use <code>this.definition.defaultResolvedIf</code> inside the <code>resolvedIf</code> definition and optionally add more conditions:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#000">resolvedIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">report</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">event</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">dueDate</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#204a87;font-weight:bold">this</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">definition</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">defaultResolvedIf</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">report</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">event</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">dueDate</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">otherConditions</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h2 id="build">Build</h2> -<p>To build your tasks into your app, you must compile them into app-settings, then upload them to your instance.</p> -<p><code>cht --local compile-app-settings backup-app-settings upload-app-settings</code></p>Apps: CHT APIhttps://docs.communityhealthtoolkit.org/apps/reference/_partial_cht_api/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/_partial_cht_api/ -<p><em>Introduced in v3.12.0</em></p> -<p>Provides CHT-Core Framework&rsquo;s functions to contact summary, targets and tasks. The API is available in the <code>cht</code> reserved variable under the <code>v1</code> version.</p> -<table> -<thead> -<tr> -<th>Function</th> -<th>Arguments</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>hasPermissions(permissions, userRoles, chtPermissionsSettings)</td> -<td><code>permissions</code>: String or array of permission name(s).<br><code>userRoles</code>: (Optional) Array of user roles. Default to the current logged in user.<br><code>chtPermissionsSettings</code>: (Optional) Object of configured permissions in CHT-Core&rsquo;s settings. Default to the current instance&rsquo;s configured permissions.</td> -<td>Returns true if the user has the permission(s), otherwise returns false.</td> -</tr> -<tr> -<td>hasAnyPermission(permissionsGroups, userRoles, chtPermissionsSettings)</td> -<td><code>permissionsGroups</code>: Array of groups of permission name(s).<br><code>userRoles</code>: (Optional) Array of user roles. Default to the current logged in user.<br><code>chtPermissionsSettings</code>: (Optional) Object of configured permissions in CHT-Core&rsquo;s settings. Default to the current instance&rsquo;s configured permissions.</td> -<td>Returns true if the user has all the permissions of any of the provided groups, otherwise returns false.</td> -</tr> -<tr> -<td>getExtensionLib(name)</td> -<td><code>name</code>: String of script name</td> -<td>Returns an executable function identified by the given name configured as <a href="https://docs.communityhealthtoolkit.org/apps/reference/extension-libs/">extension-libs</a>.</td> -</tr> -</tbody> -</table> -<h3 id="cht-apis-code-samples">CHT API&rsquo;s code samples</h3> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">canEdit</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">cht</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">v1</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">hasPermissions</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;can_edit&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">canManagePlaces</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">cht</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">v1</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">hasPermissions</span><span style="color:#000;font-weight:bold">([</span><span style="color:#4e9a06">&#39;can_create_places&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;can_update_places&#39;</span><span style="color:#000;font-weight:bold">]);</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">hasAnyGroup</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">cht</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">v1</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">hasAnyPermission</span><span style="color:#000;font-weight:bold">([</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;can_view_messages&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;can_view_message_action&#39;</span><span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;can_view_reports&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;can_verify_reports&#39;</span><span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">]);</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">averageFn</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">cht</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">v1</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getExtensionLib</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;average.js&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span></code></pre></div>Apps: Utils Functionshttps://docs.communityhealthtoolkit.org/apps/reference/_partial_utils/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/_partial_utils/ -<p>Utility functions in the Core Framework can make common tasks much easier. These are available only for Tasks and Targets. To use the function call <code>Utils.&lt;function-name&gt;(&lt;params&gt;)</code>, for example <code>Utils.addDate(report.reported_date, 10)</code>.</p> -<table> -<thead> -<tr> -<th>Name</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>isTimely(date, event)</code></td> -<td>Returns true if the given date is after the start date and before the end date of the event.</td> -</tr> -<tr> -<td><code>addDate(date, days)</code></td> -<td>Returns a new Date set to midnight the given number of days after the given date. If no date is given the date defaults to today.</td> -</tr> -<tr> -<td><code>getLmpDate(doc)</code></td> -<td>Attempts to work out the LMP from the given doc. If no LMP is given it defaults to four weeks before the reported_date.</td> -</tr> -<tr> -<td><code>getSchedule(name)</code></td> -<td>Returns the task schedule with the given name from the configuration.</td> -</tr> -<tr> -<td><code>getMostRecentTimestamp(reports, form)</code></td> -<td>Returns the reported_date of the most recent of the reports with form ID matching the given form.</td> -</tr> -<tr> -<td><code>getMostRecentReport(reports, form)</code></td> -<td>Like <code>getMostRecentTimestamp</code> but returns the report, not just the reported_date. From CHT v3.14.0, it also accepts an array of forms.</td> -</tr> -<tr> -<td><code>isFormSubmittedInWindow(reports, form, start, end)</code></td> -<td>Returns true if any of the given reports are for the given form and were reported after start and before end.</td> -</tr> -<tr> -<td><code>isFirstReportNewer(firstReport, secondReport)</code></td> -<td>Returns true if the firstReport was reported before the secondReport.</td> -</tr> -<tr> -<td><code>isDateValid(date)</code></td> -<td>Returns true if the given date is a validate JavaScript Date.</td> -</tr> -<tr> -<td><code>now()</code></td> -<td>Returns the current Date.</td> -</tr> -<tr> -<td><code>getField(report, fieldPath)</code></td> -<td>Returns the value of the specified fieldPath. The fieldPath is a period separated json path.</td> -</tr> -<tr> -<td><code>MS_IN_DAY</code></td> -<td>A constant for the number of milliseconds in a day.</td> -</tr> -</tbody> -</table> -<p>Please open <a href="https://github.com/medic/cht-core/issues/new">an issue</a> if you&rsquo;d like other functions included.</p> \ No newline at end of file +Reference Documentation on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/reference/Recent content in Reference Documentation on Community Health ToolkitHugo -- gohugo.ioenAPI to interact with CHT Applicationshttps://docs.communityhealthtoolkit.org/apps/reference/api/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/api/This page covers the endpoints to use when integrating with the CHT server. If there isn&rsquo;t an endpoint that provides the function or data you need, direct access to the database is possible via the CouchDB API. Access to the PostgreSQL database may also prove useful for data analysis. If additional endpoints would be helpful, please make suggestions via a GitHub issue. +Settings GET /api/v1/settings PUT /api/v1/settings Query Parameters Export GET /api/v2/export/dhis GET /api/v2/export/reports Query parameters GET /api/v2/export/messages Output Examples GET /api/v2/export/feedback Query Parameters GET /api/v2/export/contacts Output Query parameters GET /api/v2/export/user-devices Output Forms GET /api/v1/forms Headers Examples GET /api/v1/forms/{{id}}.resources/https://docs.communityhealthtoolkit.org/apps/reference/resources/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/resources/Icons Apps can be customized by defining the icons to use for tasks, targets, and contacts. +Add icons to the resources folder, and include them by name in the resources.json file as the following example: +{ &#34;icon-risk&#34;: &#34;icon-healthcare-warning@2x.png&#34;, &#34;icon-treatment&#34;: &#34;icon-healthcare-medicine@2x.png&#34;, &#34;medic-clinic&#34;: &#34;medic-family.svg&#34;, &#34;medic-district-hospital&#34;: &#34;medic-family.svg&#34;, &#34;medic-health-center&#34;: &#34;medic-chw-area.svg&#34;, &#34;medic-person&#34;: &#34;medic-person.svg&#34; } See Also: Icon Library +The folder and files structure would look like this: +./ resources.json /resources icon-healthcare-warning@2x.png icon-healthcare-medicine@2x.png medic-family.svg medic-family.svg medic-chw-area.svg medic-person.translations/https://docs.communityhealthtoolkit.org/apps/reference/translations/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/translations/Given that CHT apps are used around the world, the Core Framework was designed with localization in mind. The Core Framework itself is available in English, French, Hindi, Nepali, Spanish, Swahili, and Indonesian. +In the app_settings.json file the default language for the application is set by the locale property, along with a separate default language for outgoing messages that are sent via SMS with the locale_outgoing property. +Additionally, languages available to the user can be enabled and disabled through the languages property which contains an array of objects.contact-summary.templated.jshttps://docs.communityhealthtoolkit.org/apps/reference/contact-page/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/contact-page/Contact profile pages display basic information about the contact along with their history and upcoming tasks. A contact&rsquo;s profile page is defined by the Fields, Cards, and Care Guides available. +Helper variables and functions for the contact summary can be defined in contact-summary-extras.js. There are several variables available to inspect to generate the summary information: +Variable Description contact The currently selected contact. This has minimal stubs for the contact.parent, so if you want to refer to a property on the parent use lineage below.extension-libs/https://docs.communityhealthtoolkit.org/apps/reference/extension-libs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/extension-libs/Introduced in v4.2.0 +Introduction Extension libraries are blocks of code that are cached with the CHT web application giving app developers a powerful tool to extend the CHT. This is an advanced feature and requires an app developer with some software development experience. +An example of a use for this feature is to provide a function to calculate a risk score based on a machine learning model. The function can then be called passing in values from app forms and return the result to be stored with the report.targets.jshttps://docs.communityhealthtoolkit.org/apps/reference/targets/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/targets/All targets are defined in the targets.js file as an array of objects according to the Targets schema defined below. Each object corresponds to a target widget that shows in the app. The order of objects in the array defines the display order of widgets on the Targets tab. The properties of the object are used to define when the target should appear, what it should look like, and the values it will display.tasks.jshttps://docs.communityhealthtoolkit.org/apps/reference/tasks/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/tasks/Task generation is configured in the tasks.js file. This file is a JavaScript module which defines an array of objects conforming to the Task schema detailed below. When defining tasks, all the data about contacts on the device (both people and places) along with all their reports are available. Tasks are available only for users of type &ldquo;restricted to their place&rdquo;. Tasks can pull in fields from reports and pass data as inputs to the form that opens when the task is selected, enabling richer user experiences.CHT APIhttps://docs.communityhealthtoolkit.org/apps/reference/_partial_cht_api/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/_partial_cht_api/Introduced in v3.12.0 +Provides CHT-Core Framework&rsquo;s functions to contact summary, targets and tasks. The API is available in the cht reserved variable under the v1 version. +Function Arguments Description hasPermissions(permissions, userRoles, chtPermissionsSettings) permissions: String or array of permission name(s). +userRoles: (Optional) Array of user roles. Default to the current logged in user. +chtPermissionsSettings: (Optional) Object of configured permissions in CHT-Core&rsquo;s settings. Default to the current instance&rsquo;s configured permissions. Returns true if the user has the permission(s), otherwise returns false.Utils Functionshttps://docs.communityhealthtoolkit.org/apps/reference/_partial_utils/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/_partial_utils/Utility functions in the Core Framework can make common tasks much easier. These are available only for Tasks and Targets. To use the function call Utils.&lt;function-name&gt;(&lt;params&gt;), for example Utils.addDate(report.reported_date, 10). +Name Description isTimely(date, event) Returns true if the given date is after the start date and before the end date of the event. addDate(date, days) Returns a new Date set to midnight the given number of days after the given date. \ No newline at end of file diff --git a/apps/reference/resources/index.html b/apps/reference/resources/index.html index 6aecf2deed..46a8f13199 100644 --- a/apps/reference/resources/index.html +++ b/apps/reference/resources/index.html @@ -1,9 +1,9 @@ -resources/ | Community Health Toolkit +resources/ | Community Health Toolkit

    resources/

    Graphics: Used for custom branding, logos, and icons

    Icons

    Apps can be customized by defining the icons to use for tasks, targets, and contacts.

    Add icons to the resources folder, and include them by name in the resources.json file as the following example:

     {
    + Create project issue

    resources/

    Graphics: Used for custom branding, logos, and icons

    Icons

    Apps can be customized by defining the icons to use for tasks, targets, and contacts.

    Add icons to the resources folder, and include them by name in the resources.json file as the following example:

     {
         "icon-risk": "icon-healthcare-warning@2x.png",
         "icon-treatment": "icon-healthcare-medicine@2x.png",
         "medic-clinic": "medic-family.svg",
    @@ -341,7 +341,8 @@
             parnerA.png
             parnerB.png
     

    Finally run the command: cht --local upload-partners

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/reference/targets/index.html b/apps/reference/targets/index.html index e4dddd9794..6db41bd949 100644 --- a/apps/reference/targets/index.html +++ b/apps/reference/targets/index.html @@ -1,9 +1,9 @@ -targets.js | Community Health Toolkit +targets.js | Community Health Toolkit

    targets.js

    Targets: Definition of target widgets calculated and seen in the app

    Percentages

    Percentages

    All targets are defined in the targets.js file as an array of objects according to the Targets schema defined below. Each object corresponds to a target widget that shows in the app. The order of objects in the array defines the display order of widgets on the Targets tab. The properties of the object are used to define when the target should appear, what it should look like, and the values it will display.

    See Also: Targets Overview

    targets.js

    propertytypedescriptionrequired
    idstringAn identifier for the target.yes, unique
    iconstringThe icon to show alongside the target. Should correspond with a value defined in resources.json.no
    translation_keytranslation keyTranslation key for the title of this target.no, but recommended
    subtitle_translation_keytranslation keyTranslation key for the subtitle of this target. If none supplied the subtitle will be blank.no
    percentage_count_translation_keytranslation keyTranslation key for the percentage value detail shown at the bottom of the target, eg “(5 of 6 deliveries)”. The translation context has pass and total variables available. If none supplied this defaults to targets.count.default.no
    contextstringA string containing a JavaScript expression. This widget will only be shown if the expression evaluates to true. Details of the current user is available through the variable user.no
    type'count' or 'percent'The type of the widget.yes
    goalintegerFor targets with type: 'percent', an integer from 0 to 100. For type: 'count', any positive number. If there is no goal, put -1.yes
    appliesTo'contacts' or 'reports'Do you want to count reports or contacts? This attribute controls the behavior of other attributes herein.yes
    appliesToTypeIf appliesTo: 'reports', an array of form codes. If appliesTo: 'contacts', an array of contact types.Filters the contacts or reports for which appliesIf will be evaluated. For example, ['person'] or ['clinic', 'health_center']. For example, ['pregnancy'] or ['P', 'pregnancy'].no
    appliesIffunction(contact, report)If appliesTo: 'contacts', this function is invoked once per contact and report is undefined. If appliesTo: 'reports', this function is invoked once per report. Return true to count this document. For type: 'percent', this controls the denominator.no
    passesIffunction(contact, report)For type: 'percent', return true to increment the numerator.yes, if type: 'percent'. Forbidden when groupBy is defined.
    date'reported' or 'now' or function(contact, report)When 'reported', the target will count documents with a reported_date within the current month. When 'now', target includes all documents. A function can be used to indicate when the document should be counted. When this property is undefined or the value is null the default is ’now'.no
    idType'report' or 'contact' or function(contact, report)The target’s values are incremented once per unique ID. To count individual contacts that have one or more reports that apply, use 'contact'. Use 'report' to count all reports, even if there are multiple that apply for a single contact. If you need more than a single count for each applying contact or report then a custom function can be used returning an array with unique IDs — one element for each count.no
    groupByfunction(contact, report) returning stringAllows for target ids to be aggregated and scored in groups. Not required for most targets. Use with passesIfGroupCount.no
    passesIfGroupCountobjectThe criteria to determine if the target ids within a group should be counted as passingyes when groupBy is defined
    passesIfGroupCount.gtenumberThe group should be counted as passing if the number of target ids in the group is greater-than-or-equal-to this valueyes when groupBy is defined
    dhisobject or object[]Settings relevant to the DHIS2 Integrationno
    dhis[n].dataElementstringThe hash id of a data element configured in the DHIS2 data set you’re integrating withyes
    dhis[n].dataSetstringThe hash id of the data set that contains the data element you’re integrating with. If this is left undefined, the data element will appear in all data sets.no
    visiblebooleanWhether the target is visible in the targets page. Default: trueno
    aggregatebooleanAs of 3.9, defines whether the target will be displayed on the TargetAggregates pageno

    Utils

    Utility functions in the Core Framework can make common tasks much easier. These are available only for Tasks and Targets. To use the function call Utils.<function-name>(<params>), for example Utils.addDate(report.reported_date, 10).

    NameDescription
    isTimely(date, event)Returns true if the given date is after the start date and before the end date of the event.
    addDate(date, days)Returns a new Date set to midnight the given number of days after the given date. If no date is given the date defaults to today.
    getLmpDate(doc)Attempts to work out the LMP from the given doc. If no LMP is given it defaults to four weeks before the reported_date.
    getSchedule(name)Returns the task schedule with the given name from the configuration.
    getMostRecentTimestamp(reports, form)Returns the reported_date of the most recent of the reports with form ID matching the given form.
    getMostRecentReport(reports, form)Like getMostRecentTimestamp but returns the report, not just the reported_date. From CHT v3.14.0, it also accepts an array of forms.
    isFormSubmittedInWindow(reports, form, start, end)Returns true if any of the given reports are for the given form and were reported after start and before end.
    isFirstReportNewer(firstReport, secondReport)Returns true if the firstReport was reported before the secondReport.
    isDateValid(date)Returns true if the given date is a validate JavaScript Date.
    now()Returns the current Date.
    getField(report, fieldPath)Returns the value of the specified fieldPath. The fieldPath is a period separated json path.
    MS_IN_DAYA constant for the number of milliseconds in a day.

    Please open an issue if you’d like other functions included.

    CHT API

    Introduced in v3.12.0

    Provides CHT-Core Framework’s functions to contact summary, targets and tasks. The API is available in the cht reserved variable under the v1 version.

    FunctionArgumentsDescription
    hasPermissions(permissions, userRoles, chtPermissionsSettings)permissions: String or array of permission name(s).
    userRoles: (Optional) Array of user roles. Default to the current logged in user.
    chtPermissionsSettings: (Optional) Object of configured permissions in CHT-Core’s settings. Default to the current instance’s configured permissions.
    Returns true if the user has the permission(s), otherwise returns false.
    hasAnyPermission(permissionsGroups, userRoles, chtPermissionsSettings)permissionsGroups: Array of groups of permission name(s).
    userRoles: (Optional) Array of user roles. Default to the current logged in user.
    chtPermissionsSettings: (Optional) Object of configured permissions in CHT-Core’s settings. Default to the current instance’s configured permissions.
    Returns true if the user has all the permissions of any of the provided groups, otherwise returns false.
    getExtensionLib(name)name: String of script nameReturns an executable function identified by the given name configured as extension-libs.

    CHT API’s code samples

    const canEdit = cht.v1.hasPermissions('can_edit');
    + Create project issue

    targets.js

    Targets: Definition of target widgets calculated and seen in the app

    Percentages

    Percentages

    All targets are defined in the targets.js file as an array of objects according to the Targets schema defined below. Each object corresponds to a target widget that shows in the app. The order of objects in the array defines the display order of widgets on the Targets tab. The properties of the object are used to define when the target should appear, what it should look like, and the values it will display.

    See Also: Targets Overview

    targets.js

    propertytypedescriptionrequired
    idstringAn identifier for the target.yes, unique
    iconstringThe icon to show alongside the target. Should correspond with a value defined in resources.json.no
    translation_keytranslation keyTranslation key for the title of this target.no, but recommended
    subtitle_translation_keytranslation keyTranslation key for the subtitle of this target. If none supplied the subtitle will be blank.no
    percentage_count_translation_keytranslation keyTranslation key for the percentage value detail shown at the bottom of the target, eg “(5 of 6 deliveries)”. The translation context has pass and total variables available. If none supplied this defaults to targets.count.default.no
    contextstringA string containing a JavaScript expression. This widget will only be shown if the expression evaluates to true. Details of the current user is available through the variable user.no
    type'count' or 'percent'The type of the widget.yes
    goalintegerFor targets with type: 'percent', an integer from 0 to 100. For type: 'count', any positive number. If there is no goal, put -1.yes
    appliesTo'contacts' or 'reports'Do you want to count reports or contacts? This attribute controls the behavior of other attributes herein.yes
    appliesToTypeIf appliesTo: 'reports', an array of form codes. If appliesTo: 'contacts', an array of contact types.Filters the contacts or reports for which appliesIf will be evaluated. For example, ['person'] or ['clinic', 'health_center']. For example, ['pregnancy'] or ['P', 'pregnancy'].no
    appliesIffunction(contact, report)If appliesTo: 'contacts', this function is invoked once per contact and report is undefined. If appliesTo: 'reports', this function is invoked once per report. Return true to count this document. For type: 'percent', this controls the denominator.no
    passesIffunction(contact, report)For type: 'percent', return true to increment the numerator.yes, if type: 'percent'. Forbidden when groupBy is defined.
    date'reported' or 'now' or function(contact, report)When 'reported', the target will count documents with a reported_date within the current month. When 'now', target includes all documents. A function can be used to indicate when the document should be counted. When this property is undefined or the value is null the default is ’now'.no
    idType'report' or 'contact' or function(contact, report)The target’s values are incremented once per unique ID. To count individual contacts that have one or more reports that apply, use 'contact'. Use 'report' to count all reports, even if there are multiple that apply for a single contact. If you need more than a single count for each applying contact or report then a custom function can be used returning an array with unique IDs — one element for each count.no
    groupByfunction(contact, report) returning stringAllows for target ids to be aggregated and scored in groups. Not required for most targets. Use with passesIfGroupCount.no
    passesIfGroupCountobjectThe criteria to determine if the target ids within a group should be counted as passingyes when groupBy is defined
    passesIfGroupCount.gtenumberThe group should be counted as passing if the number of target ids in the group is greater-than-or-equal-to this valueyes when groupBy is defined
    dhisobject or object[]Settings relevant to the DHIS2 Integrationno
    dhis[n].dataElementstringThe hash id of a data element configured in the DHIS2 data set you’re integrating withyes
    dhis[n].dataSetstringThe hash id of the data set that contains the data element you’re integrating with. If this is left undefined, the data element will appear in all data sets.no
    visiblebooleanWhether the target is visible in the targets page. Default: trueno
    aggregatebooleanAs of 3.9, defines whether the target will be displayed on the TargetAggregates pageno

    Utils

    Utility functions in the Core Framework can make common tasks much easier. These are available only for Tasks and Targets. To use the function call Utils.<function-name>(<params>), for example Utils.addDate(report.reported_date, 10).

    NameDescription
    isTimely(date, event)Returns true if the given date is after the start date and before the end date of the event.
    addDate(date, days)Returns a new Date set to midnight the given number of days after the given date. If no date is given the date defaults to today.
    getLmpDate(doc)Attempts to work out the LMP from the given doc. If no LMP is given it defaults to four weeks before the reported_date.
    getSchedule(name)Returns the task schedule with the given name from the configuration.
    getMostRecentTimestamp(reports, form)Returns the reported_date of the most recent of the reports with form ID matching the given form.
    getMostRecentReport(reports, form)Like getMostRecentTimestamp but returns the report, not just the reported_date. From CHT v3.14.0, it also accepts an array of forms.
    isFormSubmittedInWindow(reports, form, start, end)Returns true if any of the given reports are for the given form and were reported after start and before end.
    isFirstReportNewer(firstReport, secondReport)Returns true if the firstReport was reported before the secondReport.
    isDateValid(date)Returns true if the given date is a validate JavaScript Date.
    now()Returns the current Date.
    getField(report, fieldPath)Returns the value of the specified fieldPath. The fieldPath is a period separated json path.
    MS_IN_DAYA constant for the number of milliseconds in a day.

    Please open an issue if you’d like other functions included.

    CHT API

    Introduced in v3.12.0

    Provides CHT-Core Framework’s functions to contact summary, targets and tasks. The API is available in the cht reserved variable under the v1 version.

    FunctionArgumentsDescription
    hasPermissions(permissions, userRoles, chtPermissionsSettings)permissions: String or array of permission name(s).
    userRoles: (Optional) Array of user roles. Default to the current logged in user.
    chtPermissionsSettings: (Optional) Object of configured permissions in CHT-Core’s settings. Default to the current instance’s configured permissions.
    Returns true if the user has the permission(s), otherwise returns false.
    hasAnyPermission(permissionsGroups, userRoles, chtPermissionsSettings)permissionsGroups: Array of groups of permission name(s).
    userRoles: (Optional) Array of user roles. Default to the current logged in user.
    chtPermissionsSettings: (Optional) Object of configured permissions in CHT-Core’s settings. Default to the current instance’s configured permissions.
    Returns true if the user has all the permissions of any of the provided groups, otherwise returns false.
    getExtensionLib(name)name: String of script nameReturns an executable function identified by the given name configured as extension-libs.

    CHT API’s code samples

    const canEdit = cht.v1.hasPermissions('can_edit');
     const canManagePlaces = cht.v1.hasPermissions(['can_create_places', 'can_update_places']);
     const hasAnyGroup = cht.v1.hasAnyPermission([
         ['can_view_messages', 'can_view_message_action'], 
    @@ -387,7 +387,8 @@
     Features >
     Targets

    Dashboards to track metrics for an individual CHW or for an entire health facility

    Design System > Best Practices

    This document covers the configuration best practices of forms, tasks, targets, and contact profiles when building your own community health app.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/reference/tasks/index.html b/apps/reference/tasks/index.html index a81de768a8..27e3f66390 100644 --- a/apps/reference/tasks/index.html +++ b/apps/reference/tasks/index.html @@ -1,9 +1,9 @@ -tasks.js | Community Health Toolkit +tasks.js | Community Health Toolkit

    tasks.js

    Tasks: Definition of tasks shown to app users

    task

    Task generation is configured in the tasks.js file. This file is a JavaScript module which defines an array of objects conforming to the Task schema detailed below. When defining tasks, all the data about contacts on the device (both people and places) along with all their reports are available. Tasks are available only for users of type “restricted to their place”. Tasks can pull in fields from reports and pass data as inputs to the form that opens when the task is selected, enabling richer user experiences.

    Task generation occurs on the client periodically and creates documents which track the status of the task over time. To avoid performance issues the developer needs to be conscious about generating too many tasks. For example, to remind a user to do something every day, you could generate one task for each day and fill up the user’s device. The recommended approach is to only generate the tasks for the near future, or only once the previous task is resolved. To limit the impact of this misconfiguration, the CHT will only generate tasks that can be completed between 60 days in the past, and (as of 4.0.0) 180 days in the future.

    See Also: Tasks Overview

    tasks.js

    propertytypedescriptionrequired
    namestringA unique identifier for the task. Used for querying task completeness.yes, unique
    iconstringThe icon to show alongside the task. Should correspond with a value defined in resources.json.no
    titletranslation keyThe title of the task (labeled above).yes
    appliesTo'contacts' or 'reports'Do you want to emit one task per report, or one task per contact? See Understanding the Parameters in the Task Schema.yes
    appliesIffunction(contact, report)If appliesTo: 'contacts', this function is invoked once per contact and report is undefined. If appliesTo: 'reports', this function is invoked once per report. Return true if the task should appear for the given documents. See Understanding the Parameters in the Task Schema.no
    appliesToTypestring[]Filters the contacts or reports for which appliesIf will be evaluated. If appliesTo: 'reports', this is an array of form codes. If appliesTo: 'contacts', this is an array of contact types. For example, ['person'] or ['clinic', 'health_center']. For example, ['pregnancy'] or ['P', 'pregnancy']. See Understanding the Parameters in the Task Schema.no
    contactLabelstring or function(contact, report)Controls the label describing the subject of the task. Defaults to the name of the contact (contact.contact.name).no
    resolvedIffunction(contact, report, event, dueDate)Return true to mark the task as “resolved”. A resolved task uses memory on the phone, but is not displayed.no, if any actions[n].type is 'report'
    eventsobject[]An event is used to specify the timing of the task.yes
    events[n].idstringA descriptive identifier. Used for querying task completeness.yes if task has multiple events, unique
    events[n].daysintegerNumber of days after the doc’s reported_date that the event is dueyes, if dueDate is not set
    events[n].dueDatefunction(event, contact, report)Returns a Date object for the day when this event is due.yes, if days is not set
    events[n].startintegerNumber of days to show the task before it is due.yes
    events[n].endintegerNumber of days to show the task after it is due.yes
    actionsobject[]The actions (forms) that a user can access after clicking on a task. If you put multiple forms here, the user will see a task summary screen where they can select which action they would like to complete.yes
    actions[n].type'report' or 'contact'When 'report', the action opens the given form. When 'contact', the action redirects to a contact’s profile page. Defaults to ‘report’.no
    actions[n].formstringThe code of the form that should open when you select the action.yes, if actions[n].type is 'report'
    actions[n].labeltranslation keyThe label that should appear on the task summary screen if multiple actions are present.no
    actions[n].modifyContentfunction (content, contact, report, event)Set the values on the content object to control the data which will be passed as inputs to the form which opens when the action is selected. See Passing data from a task into the app form.no
    priorityobject or function(contact, report) returning object of same schemaControls the “high risk” line seen above.no
    priority.levelhigh or mediumTasks that are high will display a high risk icon with the task. Default: medium.no
    priority.labeltranslation keyText shown with the task associated to the risk level.no

    Utils

    Utility functions in the Core Framework can make common tasks much easier. These are available only for Tasks and Targets. To use the function call Utils.<function-name>(<params>), for example Utils.addDate(report.reported_date, 10).

    NameDescription
    isTimely(date, event)Returns true if the given date is after the start date and before the end date of the event.
    addDate(date, days)Returns a new Date set to midnight the given number of days after the given date. If no date is given the date defaults to today.
    getLmpDate(doc)Attempts to work out the LMP from the given doc. If no LMP is given it defaults to four weeks before the reported_date.
    getSchedule(name)Returns the task schedule with the given name from the configuration.
    getMostRecentTimestamp(reports, form)Returns the reported_date of the most recent of the reports with form ID matching the given form.
    getMostRecentReport(reports, form)Like getMostRecentTimestamp but returns the report, not just the reported_date. From CHT v3.14.0, it also accepts an array of forms.
    isFormSubmittedInWindow(reports, form, start, end)Returns true if any of the given reports are for the given form and were reported after start and before end.
    isFirstReportNewer(firstReport, secondReport)Returns true if the firstReport was reported before the secondReport.
    isDateValid(date)Returns true if the given date is a validate JavaScript Date.
    now()Returns the current Date.
    getField(report, fieldPath)Returns the value of the specified fieldPath. The fieldPath is a period separated json path.
    MS_IN_DAYA constant for the number of milliseconds in a day.

    Please open an issue if you’d like other functions included.

    CHT API

    Introduced in v3.12.0

    Provides CHT-Core Framework’s functions to contact summary, targets and tasks. The API is available in the cht reserved variable under the v1 version.

    FunctionArgumentsDescription
    hasPermissions(permissions, userRoles, chtPermissionsSettings)permissions: String or array of permission name(s).
    userRoles: (Optional) Array of user roles. Default to the current logged in user.
    chtPermissionsSettings: (Optional) Object of configured permissions in CHT-Core’s settings. Default to the current instance’s configured permissions.
    Returns true if the user has the permission(s), otherwise returns false.
    hasAnyPermission(permissionsGroups, userRoles, chtPermissionsSettings)permissionsGroups: Array of groups of permission name(s).
    userRoles: (Optional) Array of user roles. Default to the current logged in user.
    chtPermissionsSettings: (Optional) Object of configured permissions in CHT-Core’s settings. Default to the current instance’s configured permissions.
    Returns true if the user has all the permissions of any of the provided groups, otherwise returns false.
    getExtensionLib(name)name: String of script nameReturns an executable function identified by the given name configured as extension-libs.

    CHT API’s code samples

    const canEdit = cht.v1.hasPermissions('can_edit');
    + Create project issue

    tasks.js

    Tasks: Definition of tasks shown to app users

    task

    Task generation is configured in the tasks.js file. This file is a JavaScript module which defines an array of objects conforming to the Task schema detailed below. When defining tasks, all the data about contacts on the device (both people and places) along with all their reports are available. Tasks are available only for users of type “restricted to their place”. Tasks can pull in fields from reports and pass data as inputs to the form that opens when the task is selected, enabling richer user experiences.

    Task generation occurs on the client periodically and creates documents which track the status of the task over time. To avoid performance issues the developer needs to be conscious about generating too many tasks. For example, to remind a user to do something every day, you could generate one task for each day and fill up the user’s device. The recommended approach is to only generate the tasks for the near future, or only once the previous task is resolved. To limit the impact of this misconfiguration, the CHT will only generate tasks that can be completed between 60 days in the past, and (as of 4.0.0) 180 days in the future.

    See Also: Tasks Overview

    tasks.js

    propertytypedescriptionrequired
    namestringA unique identifier for the task. Used for querying task completeness.yes, unique
    iconstringThe icon to show alongside the task. Should correspond with a value defined in resources.json.no
    titletranslation keyThe title of the task (labeled above).yes
    appliesTo'contacts' or 'reports'Do you want to emit one task per report, or one task per contact? See Understanding the Parameters in the Task Schema.yes
    appliesIffunction(contact, report)If appliesTo: 'contacts', this function is invoked once per contact and report is undefined. If appliesTo: 'reports', this function is invoked once per report. Return true if the task should appear for the given documents. See Understanding the Parameters in the Task Schema.no
    appliesToTypestring[]Filters the contacts or reports for which appliesIf will be evaluated. If appliesTo: 'reports', this is an array of form codes. If appliesTo: 'contacts', this is an array of contact types. For example, ['person'] or ['clinic', 'health_center']. For example, ['pregnancy'] or ['P', 'pregnancy']. See Understanding the Parameters in the Task Schema.no
    contactLabelstring or function(contact, report)Controls the label describing the subject of the task. Defaults to the name of the contact (contact.contact.name).no
    resolvedIffunction(contact, report, event, dueDate)Return true to mark the task as “resolved”. A resolved task uses memory on the phone, but is not displayed.no, if any actions[n].type is 'report'
    eventsobject[]An event is used to specify the timing of the task.yes
    events[n].idstringA descriptive identifier. Used for querying task completeness.yes if task has multiple events, unique
    events[n].daysintegerNumber of days after the doc’s reported_date that the event is dueyes, if dueDate is not set
    events[n].dueDatefunction(event, contact, report)Returns a Date object for the day when this event is due.yes, if days is not set
    events[n].startintegerNumber of days to show the task before it is due.yes
    events[n].endintegerNumber of days to show the task after it is due.yes
    actionsobject[]The actions (forms) that a user can access after clicking on a task. If you put multiple forms here, the user will see a task summary screen where they can select which action they would like to complete.yes
    actions[n].type'report' or 'contact'When 'report', the action opens the given form. When 'contact', the action redirects to a contact’s profile page. Defaults to ‘report’.no
    actions[n].formstringThe code of the form that should open when you select the action.yes, if actions[n].type is 'report'
    actions[n].labeltranslation keyThe label that should appear on the task summary screen if multiple actions are present.no
    actions[n].modifyContentfunction (content, contact, report, event)Set the values on the content object to control the data which will be passed as inputs to the form which opens when the action is selected. See Passing data from a task into the app form.no
    priorityobject or function(contact, report) returning object of same schemaControls the “high risk” line seen above.no
    priority.levelhigh or mediumTasks that are high will display a high risk icon with the task. Default: medium.no
    priority.labeltranslation keyText shown with the task associated to the risk level.no

    Utils

    Utility functions in the Core Framework can make common tasks much easier. These are available only for Tasks and Targets. To use the function call Utils.<function-name>(<params>), for example Utils.addDate(report.reported_date, 10).

    NameDescription
    isTimely(date, event)Returns true if the given date is after the start date and before the end date of the event.
    addDate(date, days)Returns a new Date set to midnight the given number of days after the given date. If no date is given the date defaults to today.
    getLmpDate(doc)Attempts to work out the LMP from the given doc. If no LMP is given it defaults to four weeks before the reported_date.
    getSchedule(name)Returns the task schedule with the given name from the configuration.
    getMostRecentTimestamp(reports, form)Returns the reported_date of the most recent of the reports with form ID matching the given form.
    getMostRecentReport(reports, form)Like getMostRecentTimestamp but returns the report, not just the reported_date. From CHT v3.14.0, it also accepts an array of forms.
    isFormSubmittedInWindow(reports, form, start, end)Returns true if any of the given reports are for the given form and were reported after start and before end.
    isFirstReportNewer(firstReport, secondReport)Returns true if the firstReport was reported before the secondReport.
    isDateValid(date)Returns true if the given date is a validate JavaScript Date.
    now()Returns the current Date.
    getField(report, fieldPath)Returns the value of the specified fieldPath. The fieldPath is a period separated json path.
    MS_IN_DAYA constant for the number of milliseconds in a day.

    Please open an issue if you’d like other functions included.

    CHT API

    Introduced in v3.12.0

    Provides CHT-Core Framework’s functions to contact summary, targets and tasks. The API is available in the cht reserved variable under the v1 version.

    FunctionArgumentsDescription
    hasPermissions(permissions, userRoles, chtPermissionsSettings)permissions: String or array of permission name(s).
    userRoles: (Optional) Array of user roles. Default to the current logged in user.
    chtPermissionsSettings: (Optional) Object of configured permissions in CHT-Core’s settings. Default to the current instance’s configured permissions.
    Returns true if the user has the permission(s), otherwise returns false.
    hasAnyPermission(permissionsGroups, userRoles, chtPermissionsSettings)permissionsGroups: Array of groups of permission name(s).
    userRoles: (Optional) Array of user roles. Default to the current logged in user.
    chtPermissionsSettings: (Optional) Object of configured permissions in CHT-Core’s settings. Default to the current instance’s configured permissions.
    Returns true if the user has all the permissions of any of the provided groups, otherwise returns false.
    getExtensionLib(name)name: String of script nameReturns an executable function identified by the given name configured as extension-libs.

    CHT API’s code samples

    const canEdit = cht.v1.hasPermissions('can_edit');
     const canManagePlaces = cht.v1.hasPermissions(['can_create_places', 'can_update_places']);
     const hasAnyGroup = cht.v1.hasAnyPermission([
         ['can_view_messages', 'can_view_message_action'], 
    @@ -471,7 +471,8 @@
     Concepts >
     Workflows

    Building connections between people, actions, and data systems

    Design System > Best Practices

    This document covers the configuration best practices of forms, tasks, targets, and contact profiles when building your own community health app.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/reference/translations/index.html b/apps/reference/translations/index.html index 83c026dbb8..dfd11a0253 100644 --- a/apps/reference/translations/index.html +++ b/apps/reference/translations/index.html @@ -1,9 +1,9 @@ -translations/ | Community Health Toolkit +translations/ | Community Health Toolkit

    translations/

    Localization: Localized labels for CHT applications

    Given that CHT apps are used around the world, the Core Framework was designed with localization in mind. The Core Framework itself is available in English, French, Hindi, Nepali, Spanish, Swahili, and Indonesian.
    In the app_settings.json file the default language for the application is set by the locale property, along with a separate default language for outgoing messages that are sent via SMS with the locale_outgoing property.
    Additionally, languages available to the user can be enabled and disabled through the languages property which contains an array of objects. These objects should contain the locale and enabled properties representing respectively the 2 or 3 letter language code and whether that language should be enabled.

    Translations

    To modify some labels in the app add the key and modified label in a custom translations file in the translations folder. All the properties files use the format messages-{language-code}.properties, where the language code is the same 2-letter code used to identify the language in the application. For instance, for English, we would have a translations/messages-en.properties file.

    New elements in CHT apps, such as tasks, targets, profiles, and forms should be localized as well. These labels should be included in the same custom translations properties file. If a translation is missing for the user’s language it will use that of the default language.

    Here is an example, including both a modified label, and a new one:

    translations/messages-{language-code}.properties

        [Application Text]
    + Create project issue

    translations/

    Localization: Localized labels for CHT applications

    Given that CHT apps are used around the world, the Core Framework was designed with localization in mind. The Core Framework itself is available in English, French, Hindi, Nepali, Spanish, Swahili, and Indonesian.
    In the app_settings.json file the default language for the application is set by the locale property, along with a separate default language for outgoing messages that are sent via SMS with the locale_outgoing property.
    Additionally, languages available to the user can be enabled and disabled through the languages property which contains an array of objects. These objects should contain the locale and enabled properties representing respectively the 2 or 3 letter language code and whether that language should be enabled.

    Translations

    To modify some labels in the app add the key and modified label in a custom translations file in the translations folder. All the properties files use the format messages-{language-code}.properties, where the language code is the same 2-letter code used to identify the language in the application. For instance, for English, we would have a translations/messages-en.properties file.

    New elements in CHT apps, such as tasks, targets, profiles, and forms should be localized as well. These labels should be included in the same custom translations properties file. If a translation is missing for the user’s language it will use that of the default language.

    Here is an example, including both a modified label, and a new one:

    translations/messages-{language-code}.properties

        [Application Text]
         contact.type.district_hospital = Community
         targets.assessments.title = Assessments Completed
     

    Forms

    Translations for XForms are defined within the forms themselves. The XLSForm notation is documented here, and would use the corresponding 2-character language codes.

    Reports

    Submitted forms are shown on the Reports tab, with each value in the report displayed alongside a label. The label for each value is represented by a key in the report.{form-name}.{field-name} format, which can be translated by including the key and translation in the language files. If the label is omitted in the translation the full key will show in the app.

    Build

    Custom translations from the properties files are added to the app with the upload-custom-translations action.

    cht --local upload-custom-translations

    Updated translations from forms need to be added with the actions to upload forms.

    cht --local upload-contact-forms upload-app-forms

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/tutorials/_partial_docker_setup/index.html b/apps/tutorials/_partial_docker_setup/index.html index 3bc1219b08..fc94b50aca 100644 --- a/apps/tutorials/_partial_docker_setup/index.html +++ b/apps/tutorials/_partial_docker_setup/index.html @@ -1,4 +1,4 @@ -Local Docker Setup | Community Health Toolkit +Local Docker Setup | Community Health Toolkit

    Local Docker Setup

    Local Docker Setup

    curl -fsSL get.docker.com -o get-docker.sh && sh get-docker.sh
     # OPTIONAL: Allow user to run Docker without sudo
     dockerd-rootless-setuptool.sh install
    @@ -319,7 +319,8 @@
     . ~/.$(basename $SHELL)rc
     

    Download and install Docker Desktop or Colima.

    Download and install Docker Desktop.

    Restart your entire machine to finish initializing Docker.

    After restarting, verify Docker is running as expected. Run the simple hello-world Docker container. This should output “Hello from Docker!” as well as some other intro text:

    docker run hello-world
     
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/tutorials/app-forms/index.html b/apps/tutorials/app-forms/index.html index 0036a140f5..54e79d3b6c 100644 --- a/apps/tutorials/app-forms/index.html +++ b/apps/tutorials/app-forms/index.html @@ -1,9 +1,9 @@ -Building App Forms | Community Health Toolkit +Building App Forms | Community Health Toolkit

    Building App Forms

    Building CHT app forms

    App forms allow users to submit reports from Android devices

    This tutorial will take you through how to build App forms for CHT applications, including:

    • Authoring forms in Excel, Google sheets or other spreadsheet applications.
    • Converting XLSForms to XForms
    • Uploading XForms to CHT

    You will be building assessment workflow that allows Community Health Workers to conduct a health assessment for children under the age of 5.

    Brief Overview of Key Concepts

    App forms serve as actions within the app.

    XLSForm is a form standard created to help simplify the authoring of forms in Excel.

    XForm is a CHT-enhanced version of the ODK XForm standard.

    Required Resources

    You should have a functioning CHT instance with cht-conf installed locally and a project folder set up already.

    Implementation Steps

    Create a new spread sheet in Google sheets or other preferred editor like Excel or Open Office. Name the spreadsheet assessment. The final file name should be assessment.xlsx.

    Create 2 additional sheets. Rename the sheets survey, choices and settings.

    1. Define XLS Survey/Form Fields

    Create the following columns in the survey sheet and then add the following rows that are populated automatically before the form is rendered to the user. These fields are usually hidden by default but can be accessed to display certain information about the person being assessed:

    typenamelabelrequiredrelevantappearanceconstraintconstraint_messagecalculationchoice_filterhintdefault
    begin groupinputsPatient./source = ‘user’field-list
    hiddensourceSourceuser
    hiddensource_idSource_ID
    hiddentask_idTask_ID
    begin groupcontactContact
    string_idPatient IDselect-contact type-personSelect a person from the list
    hiddenpatient_idMedic ID
    hiddennamePatient Name
    begin groupparentParent
    hidden_idFamily UUID
    begin groupparentGrandparent
    hidden_idCHW Area UUID
    hiddennameCHW Name
    hiddenphoneCHW Phone
    begin groupparentGreat Grandparent
    hidden_idCU UUID
    end group
    end group
    end group
    end group
    end group
    calculatepatient_id../inputs/contact/_id
    calculatepatient_name../inputs/contact/name

    Add the following rows that define the data collection fields below the existing rows (leave out the column names):

    typenamelabelrequiredrelevantappearanceconstraintconstraint_messagecalculationchoice_filterhintdefault
    begin groupgroup_assessmentAssessment
    select_one yes_nocoughDoes ${patient_name} have a cough?yes
    select_one symptom_durationcough_durationHow long has the cough lasted?yes${cough} = ‘yes’
    end group

    2. Define the Choices

    Add the following column names and rows to the choices sheet:

    list_namenamelabel
    yes_noyesYes
    yes_nonoNo
    symptom_duration33 days or less
    symptom_duration74 - 7 days
    symptom_duration138 - 13 days
    symptom_duration1414 days or more

    3. Define the XLS Settings

    Add the following column names and rows to the settings sheet:

    form_titleform_idversionstylepathinstance_namedefault_language
    Assess patientassessment1pagesdataen

    4. Convert the XLSForm and Upload the XForm

    Add the file to the forms/app subfolder in your project.

    project-name
    + Create project issue

    Building App Forms

    Building CHT app forms

    App forms allow users to submit reports from Android devices

    This tutorial will take you through how to build App forms for CHT applications, including:

    • Authoring forms in Excel, Google sheets or other spreadsheet applications.
    • Converting XLSForms to XForms
    • Uploading XForms to CHT

    You will be building assessment workflow that allows Community Health Workers to conduct a health assessment for children under the age of 5.

    Brief Overview of Key Concepts

    App forms serve as actions within the app.

    XLSForm is a form standard created to help simplify the authoring of forms in Excel.

    XForm is a CHT-enhanced version of the ODK XForm standard.

    Required Resources

    You should have a functioning CHT instance with cht-conf installed locally and a project folder set up already.

    Implementation Steps

    Create a new spread sheet in Google sheets or other preferred editor like Excel or Open Office. Name the spreadsheet assessment. The final file name should be assessment.xlsx.

    Create 2 additional sheets. Rename the sheets survey, choices and settings.

    1. Define XLS Survey/Form Fields

    Create the following columns in the survey sheet and then add the following rows that are populated automatically before the form is rendered to the user. These fields are usually hidden by default but can be accessed to display certain information about the person being assessed:

    typenamelabelrequiredrelevantappearanceconstraintconstraint_messagecalculationchoice_filterhintdefault
    begin groupinputsPatient./source = ‘user’field-list
    hiddensourceSourceuser
    hiddensource_idSource_ID
    hiddentask_idTask_ID
    begin groupcontactContact
    string_idPatient IDselect-contact type-personSelect a person from the list
    hiddenpatient_idMedic ID
    hiddennamePatient Name
    begin groupparentParent
    hidden_idFamily UUID
    begin groupparentGrandparent
    hidden_idCHW Area UUID
    hiddennameCHW Name
    hiddenphoneCHW Phone
    begin groupparentGreat Grandparent
    hidden_idCU UUID
    end group
    end group
    end group
    end group
    end group
    calculatepatient_id../inputs/contact/_id
    calculatepatient_name../inputs/contact/name

    Add the following rows that define the data collection fields below the existing rows (leave out the column names):

    typenamelabelrequiredrelevantappearanceconstraintconstraint_messagecalculationchoice_filterhintdefault
    begin groupgroup_assessmentAssessment
    select_one yes_nocoughDoes ${patient_name} have a cough?yes
    select_one symptom_durationcough_durationHow long has the cough lasted?yes${cough} = ‘yes’
    end group

    2. Define the Choices

    Add the following column names and rows to the choices sheet:

    list_namenamelabel
    yes_noyesYes
    yes_nonoNo
    symptom_duration33 days or less
    symptom_duration74 - 7 days
    symptom_duration138 - 13 days
    symptom_duration1414 days or more

    3. Define the XLS Settings

    Add the following column names and rows to the settings sheet:

    form_titleform_idversionstylepathinstance_namedefault_language
    Assess patientassessment1pagesdataen

    4. Convert the XLSForm and Upload the XForm

    Add the file to the forms/app subfolder in your project.

    project-name
       forms
         app
           assessment.xlsx
    @@ -318,7 +318,8 @@
     : Content and Layout

    This document covers the configuration best practices of forms, tasks, targets, and contact profiles when building your own community health app.

    Design System > Best Practices : Summary Page

    This document covers the configuration best practices of forms, tasks, targets, and contact profiles when building your own community health app.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/tutorials/application-graphics/index.html b/apps/tutorials/application-graphics/index.html index 89714258eb..0c08d3baf7 100644 --- a/apps/tutorials/application-graphics/index.html +++ b/apps/tutorials/application-graphics/index.html @@ -1,9 +1,9 @@ -Configuring CHT Application Graphics | Community Health Toolkit +Configuring CHT Application Graphics | Community Health Toolkit

    Configuring CHT Application Graphics

    Configuring CHT Application Graphics

    This tutorial will take you through customising some graphical elements of CHT core.

    You will cover site branding, partner logos, header tab icons, and app icons (used in tasks, targets, and contacts).

    Required Resources

    You should have a functioning CHT instance with cht-conf installed locally and completed a project folder setup.

    Implementation Steps

    1. Site branding

    You have the ability to modify the app title, logo, and favicon. For Progressive Web App installations you can also configure the desktop icon.

    VariableDescription
    logoShould be less than 100KB, have a transparent background, have high contrast, and be horizontal in shape with a ratio of about 3.5:1. We recommend SVG or PNG image formats.
    faviconIdeally 32x32 pixels, simple, and very small in size. We recommend SVG or PNG image formats.
    iconMust be at least 192x192 pixels, square. We recommend SVG or PNG image formats.

    Using cht-conf

    Create a branding.json file if it doesn’t exist. (This may have already been created by the initialise-project-layout command). + Create project issue

    Configuring CHT Application Graphics

    Configuring CHT Application Graphics

    This tutorial will take you through customising some graphical elements of CHT core.

    You will cover site branding, partner logos, header tab icons, and app icons (used in tasks, targets, and contacts).

    Required Resources

    You should have a functioning CHT instance with cht-conf installed locally and completed a project folder setup.

    Implementation Steps

    1. Site branding

    You have the ability to modify the app title, logo, and favicon. For Progressive Web App installations you can also configure the desktop icon.

    VariableDescription
    logoShould be less than 100KB, have a transparent background, have high contrast, and be horizontal in shape with a ratio of about 3.5:1. We recommend SVG or PNG image formats.
    faviconIdeally 32x32 pixels, simple, and very small in size. We recommend SVG or PNG image formats.
    iconMust be at least 192x192 pixels, square. We recommend SVG or PNG image formats.

    Using cht-conf

    Create a branding.json file if it doesn’t exist. (This may have already been created by the initialise-project-layout command). Edit the file with the following content:

     {
        "title": "My App Name",
        "resources": {
    @@ -387,7 +387,8 @@
     Features >
     App Management

    An interface for non-technical administrative users to manage users and settings

    Design System > Icon Library

    Icons for use in CHT application based on our human centered design principles

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/tutorials/application-settings/index.html b/apps/tutorials/application-settings/index.html index a79075cbb9..4004dea6f8 100644 --- a/apps/tutorials/application-settings/index.html +++ b/apps/tutorials/application-settings/index.html @@ -1,9 +1,9 @@ -CHT Application Settings | Community Health Toolkit +CHT Application Settings | Community Health Toolkit

    CHT Application Settings

    Managing CHT application settings

    This tutorial will take you through how to manage the CHT application settings, including;

    • Setting user roles and permissions
    • Enabling and disabling transitions
    • Configuring contact hierarchy and configuring replication.

    App settings allow you to both persist information that is critical to the application outside the code, and to create profiles that store the preferences for project deployments.

    Brief Overview of Key Concepts

    The settings which control CHT apps are defined in the app_settings.json file, and stored in the settings doc in the database.

    Permissions are settings that control access to specific app features and functionality.

    Roles define permissions for users to access a group of app features and functionality.

    Replication is when users download a copy of the data on to their device. Replication depth refers to the number of levels within a hierarchy a specific user role is able to replicate.

    Transitions are Javascript code that run when a document is changed. A transition can edit the changed doc or do anything server side code can do for that matter.

    Required Resources

    You should have a functioning CHT instance and have cht-conf installed locally.

    See Also: How to set up a CHT local configuration environment

    Implementation Steps

    In this section, you will define a new role, set permissions for the role, set transitions, configure a hierarchy, and upload your modified app settings file to your local environment.

    1. Set Roles and Permissions

    To add a new role, edit the object corresponding to "roles" key in app_settings.json. Add the new role as a key and within it, have an object with key/value pairs indicating the translation key of the role and whether it is an online or offline role.

    Configure a CHW role by adding the following snippet to the "roles" object:

    CHT Application Settings

    Managing CHT application settings

    This tutorial will take you through how to manage the CHT application settings, including;

    • Setting user roles and permissions
    • Enabling and disabling transitions
    • Configuring contact hierarchy and configuring replication.

    App settings allow you to both persist information that is critical to the application outside the code, and to create profiles that store the preferences for project deployments.

    Brief Overview of Key Concepts

    The settings which control CHT apps are defined in the app_settings.json file, and stored in the settings doc in the database.

    Permissions are settings that control access to specific app features and functionality.

    Roles define permissions for users to access a group of app features and functionality.

    Replication is when users download a copy of the data on to their device. Replication depth refers to the number of levels within a hierarchy a specific user role is able to replicate.

    Transitions are Javascript code that run when a document is changed. A transition can edit the changed doc or do anything server side code can do for that matter.

    Required Resources

    You should have a functioning CHT instance and have cht-conf installed locally.

    See Also: How to set up a CHT local configuration environment

    Implementation Steps

    In this section, you will define a new role, set permissions for the role, set transitions, configure a hierarchy, and upload your modified app settings file to your local environment.

    1. Set Roles and Permissions

    To add a new role, edit the object corresponding to "roles" key in app_settings.json. Add the new role as a key and within it, have an object with key/value pairs indicating the translation key of the role and whether it is an online or offline role.

    Configure a CHW role by adding the following snippet to the "roles" object:

      "chw": {
         "name": "usertype.chw",
         "offline": true
       }
    @@ -401,7 +401,8 @@
     Replication

    Settings for downloading copies of data onto a user’s device.

    CHT Core Framework > Overview > Sentinel Transitions

    Overview of Transitions, JavaScript code that runs server-side when database documents change

    Documentation

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/tutorials/application-tests/index.html b/apps/tutorials/application-tests/index.html index 807af3b9f5..4ea7e9fa3e 100644 --- a/apps/tutorials/application-tests/index.html +++ b/apps/tutorials/application-tests/index.html @@ -1,9 +1,9 @@ -Application Tests | Community Health Toolkit +Application Tests | Community Health Toolkit

    Application Tests

    Guides for writing automated tests for CHT applications

    This tutorial takes you through testing the various configurable components of CHT applications using cht-conf-test-harness.

    Prerequisites

    Complete the following tutorials:

    Importance of testing your application


    Testing your CHT application is important as it ensures you are consistently maintaining your application and defining implementation requirements. Testing also helps the user make better architectural decisions, optimizes the forms, tasks and other components of the application. Although it may seem tedious, testing your application ensures that you quickly discover issues that are introduced with changes or newer commits.

    CHT Application Testing


    CHT applications are greatly configurable. Depending on the number and complexity of app components, it can take a lot of time and effort to test the components manually. Some components, such as tasks, behave differently over time and are particularly challenging to test. As the project evolves, the configuration is often updated with new components and changes are made to the existing components. After each change, you need to test not only the new components, but also the old ones to make sure that the app works as expected. To facilitate the testing process, app builders are encouraged to write automated tests for their app using cht-conf-test-harness.

    Because it may be complicated to test with a real application, cht-conf-test-harness (also simply referred as harness), provides a platform that simulates the CHT application instance.

    Using cht-conf-test-harness, you can write tests and run them with Mocha testing framework to test the behavior of different components in a CHT application. Mocha works with a variety of assertion libraries including chai, should.js, expect.js, better-assert, unexpected among others.

    Preparation

    Writing tests for CHT apps requires a good understanding of the project workflows and requirements. To test using the harness, there are a few things you need to set up:

    1. From the previous tutorials, you should have a functioning CHT instance with cht-conf installed locally and a project folder set up. + Create project issue

    Application Tests

    Guides for writing automated tests for CHT applications

    This tutorial takes you through testing the various configurable components of CHT applications using cht-conf-test-harness.

    Prerequisites

    Complete the following tutorials:

    Importance of testing your application


    Testing your CHT application is important as it ensures you are consistently maintaining your application and defining implementation requirements. Testing also helps the user make better architectural decisions, optimizes the forms, tasks and other components of the application. Although it may seem tedious, testing your application ensures that you quickly discover issues that are introduced with changes or newer commits.

    CHT Application Testing


    CHT applications are greatly configurable. Depending on the number and complexity of app components, it can take a lot of time and effort to test the components manually. Some components, such as tasks, behave differently over time and are particularly challenging to test. As the project evolves, the configuration is often updated with new components and changes are made to the existing components. After each change, you need to test not only the new components, but also the old ones to make sure that the app works as expected. To facilitate the testing process, app builders are encouraged to write automated tests for their app using cht-conf-test-harness.

    Because it may be complicated to test with a real application, cht-conf-test-harness (also simply referred as harness), provides a platform that simulates the CHT application instance.

    Using cht-conf-test-harness, you can write tests and run them with Mocha testing framework to test the behavior of different components in a CHT application. Mocha works with a variety of assertion libraries including chai, should.js, expect.js, better-assert, unexpected among others.

    Preparation

    Writing tests for CHT apps requires a good understanding of the project workflows and requirements. To test using the harness, there are a few things you need to set up:

    1. From the previous tutorials, you should have a functioning CHT instance with cht-conf installed locally and a project folder set up. cht-conf which is short for CHT app configurer, is a command-line interface tool used to manage and configure your CHT apps. It is used for backup, conversion, validation, uploading and other actions which can be found by running cht --help.

    2. Ensure your package.json file has the required libraries. A package.json file is used to record important metadata about a project and defines functional attributes that npm uses to install dependencies and run scripts. This file should be at the root of your project folder. If your package.json file does not already have them, add cht-conf-test-harness, chai, and mocha by running this in your command-line:

      npm install --save-dev cht-conf-test-harness chai mocha
       
    3. Also, add the following scripts to package.json, if not already present:

      "scripts": {
      @@ -617,7 +617,8 @@
       Tasks >
       Querying Task Documents
       : Testing Task Document Data

      Querying the data which results from an example task. Notes on the performance implications of tasks.

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/tutorials/condition-cards/index.html b/apps/tutorials/condition-cards/index.html index 5d92fe39aa..221af04f2c 100644 --- a/apps/tutorials/condition-cards/index.html +++ b/apps/tutorials/condition-cards/index.html @@ -1,9 +1,9 @@ -Building Condition Cards | Community Health Toolkit +Building Condition Cards | Community Health Toolkit

    Building Condition Cards

    Building CHT application condition cards

    This tutorial will take you through building a condition card for CHT applications.

    Condition cards, like contact summaries display information about the contact. The data displayed in condition cards can be pulled from submitted reports.

    In this tutorial,you will be adding a condition card that displays information about a person’s most recent assessment, including: the date of the most recent assessment, and whether or not they had a cough.

    Brief Overview of Key Concepts

    Condition cards can be permanent or conditional. They can be set to appear only when a specific type of report is submitted. They can also be set to disappear when a condition is resolved or a certain amount of time has passed.

    Condition cards have several configurable elements including:

    • Title
    • Label for each data point displayed
    • Data point for the field
    • Icon for the field, if desired
    • Conditions under which to display

    Required Resources

    You should have a functioning CHT instance with cht-conf installed locally, completed a project folder setup, and an assessment form.

    Implementation Steps

    Create a contact-summary.templated.js file if it doesn’t exist. (This may have already been created by the initialise-project-layout command.)

    1. Add Dependencies and Variable Definitions

    Add the following dependencies and variable definitions at the top of the file (some of them may have been added from the contact summary tutorial):

    const thisContact = contact;
    + Create project issue

    Building Condition Cards

    Building CHT application condition cards

    This tutorial will take you through building a condition card for CHT applications.

    Condition cards, like contact summaries display information about the contact. The data displayed in condition cards can be pulled from submitted reports.

    In this tutorial,you will be adding a condition card that displays information about a person’s most recent assessment, including: the date of the most recent assessment, and whether or not they had a cough.

    Brief Overview of Key Concepts

    Condition cards can be permanent or conditional. They can be set to appear only when a specific type of report is submitted. They can also be set to disappear when a condition is resolved or a certain amount of time has passed.

    Condition cards have several configurable elements including:

    • Title
    • Label for each data point displayed
    • Data point for the field
    • Icon for the field, if desired
    • Conditions under which to display

    Required Resources

    You should have a functioning CHT instance with cht-conf installed locally, completed a project folder setup, and an assessment form.

    Implementation Steps

    Create a contact-summary.templated.js file if it doesn’t exist. (This may have already been created by the initialise-project-layout command.)

    1. Add Dependencies and Variable Definitions

    Add the following dependencies and variable definitions at the top of the file (some of them may have been added from the contact summary tutorial):

    const thisContact = contact;
     const thisLineage = lineage;
     const allReports = reports;
     


    2. Define cards and Add a Condition Card Object

    const thisContact = contact;
    @@ -377,7 +377,8 @@
     Reference >
     contact-summary.templated.js
     : Condition Cards

    Contact Pages: Customizing the fields, cards, and actions on profile pages

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/tutorials/contact-and-users-1/index.html b/apps/tutorials/contact-and-users-1/index.html index 75267bbe96..61c7d80f9f 100644 --- a/apps/tutorials/contact-and-users-1/index.html +++ b/apps/tutorials/contact-and-users-1/index.html @@ -1,9 +1,9 @@ -Contact and User Management - Part 1 | Community Health Toolkit +Contact and User Management - Part 1 | Community Health Toolkit

    Contact and User Management - Part 1

    Creating and editing contacts and users in the CHT UI

    In this tutorial you will learn how to create and edit contacts and their associated users in and application built with the CHT using the default contact creation forms. This will help you get familiar with the UI of the webapp as well as some features and functionality. If you are already comfortable with this, you can skip to part 2, which covers manipulating contacts and their associated documents using cht-conf.

    Brief Overview of Key Concepts

    Contacts are people or places that are created in the CHT application.

    People are both patients in the system and users of the system, such as CHWs or Nurses.

    Places represent either an actual physical location such as a health facility, clinic, or a grouping such as a household or CHW Area.

    Contact forms are forms in the CHT app that are used to create people or places.

    CHT App Hierarchy is often modeled after the health system, health program or community structure. All people who are registered in the app must be associated with a Place. These Places are located in a hierarchy with other Places. For instance, a Family Member is part of a Household. A Household and CHWs are part of a CHW Area. A CHW Area and nurses are part of a Health Facility. Additional levels may be added as needed. The Admin level operates outside of the hierarchy and gives access to all levels and people.

    app hierarchy

    Users represent credentials and roles / permissions for accessing the application. This can either be:

    • People who can log into the application, such as CHWs or Nurses or
    • Credentials granting external software restricted permissions to perform certain tasks, such as allowing an external service permission to write reports via the api.

    Required Resources

    You should have a functioning CHT instance with contact forms configured. Read How to set up a CHT local configuration environment

    Implementation Steps

    In this tutorial, you will work with the default contact forms and the default hierarchy, which is illustrated above in the overview of key concepts.

    While logged in as an admin user, you will first create the Health Facility, CHW Supervisor, CHW Area, and CHW. You will then create the users for the CHW so that they can log in and create households and household members.

    1. Create New Health Facility


    While logged into the CHT application, go to the People tab and select New Health Facility


    For now we will skip creating or assigning a primary contact so that we can focus on creating the new Health Facility.


    Enter the details of the Health Facility and submit the form.


    You should see the newly created Health Facility appear on the left-hand side and when you select it, you will see details of the Health Facility appear on the right-hand side.



    2. Create CHW Area and CHW

    We will now create a Place and the primary contact for it within one form. We want to create a CHW Area within the Health Facility that we previously created.


    Select the Health Facility on the left-hand side. You will then select New Area on the right-hand side.


    Select the option that lets you create a new person within the form. This person will automatically become the primary contact for the created place.


    Fill in the required fields and go to the next section.


    You will get an option to name the Place after the created contact person or name it yourself. If you select Yes, the new place will be named <contact-name>'s Area. For example Jane Doe's Area.


    Once you submit, a new CHW Area will be created. On the right-hand you should see the CHW Area name, the primary contact of the CHW Area, and the Health Facility that the CHW Area belongs to.



    3. Create CHW Supervisor


    To create a primary contact for an existing Place (in this case, for the Health Facility that we created without a primary contact); select the Place and then select the New Person action.


    A new person form will appear with an option to change the Place the new person will belong to. A new contact will be created in the Health Facility when you submit this form.


    Finally, we will set the newly created person as a primary contact for the Health Facility they belong to. To do this, select the Health Facility and then select the Edit action.


    You should see an edit form from which you can set the primary contact of the Health Facility. Click Submit to apply the changes.



    4. Create the CHW User

    You may want to log in as a CHW and perform some actions now that the CHW and CHW Supervisor contacts are created; let’s create a CHW user who’s linked to the CHW contact we created earlier.


    Go to the hamburger menu and select App Settings.


    When you are on the App Settings page, select Users on the left-hand side and then select Add User on the right-hand side.


    You should now see an Add User Form. Fill in the user name, then select the role as CHW or Regional Admin. In the Place field, select the name of the CHW Area whose CHW you want to create a user for (you can search by typing the first few letters of the CHW Area name). Once that is done, under the Associate Contact field select the name of the CHW whose user you are creating. Finally, input a password and hit Submit.


    Once this is done, you can logout and log into the app using the username and password that you just created.

    Frequently Asked Questions


    CHT Applications > + Create project issue

    Contact and User Management - Part 1

    Creating and editing contacts and users in the CHT UI

    In this tutorial you will learn how to create and edit contacts and their associated users in and application built with the CHT using the default contact creation forms. This will help you get familiar with the UI of the webapp as well as some features and functionality. If you are already comfortable with this, you can skip to part 2, which covers manipulating contacts and their associated documents using cht-conf.

    Brief Overview of Key Concepts

    Contacts are people or places that are created in the CHT application.

    People are both patients in the system and users of the system, such as CHWs or Nurses.

    Places represent either an actual physical location such as a health facility, clinic, or a grouping such as a household or CHW Area.

    Contact forms are forms in the CHT app that are used to create people or places.

    CHT App Hierarchy is often modeled after the health system, health program or community structure. All people who are registered in the app must be associated with a Place. These Places are located in a hierarchy with other Places. For instance, a Family Member is part of a Household. A Household and CHWs are part of a CHW Area. A CHW Area and nurses are part of a Health Facility. Additional levels may be added as needed. The Admin level operates outside of the hierarchy and gives access to all levels and people.

    app hierarchy

    Users represent credentials and roles / permissions for accessing the application. This can either be:

    • People who can log into the application, such as CHWs or Nurses or
    • Credentials granting external software restricted permissions to perform certain tasks, such as allowing an external service permission to write reports via the api.

    Required Resources

    You should have a functioning CHT instance with contact forms configured. Read How to set up a CHT local configuration environment

    Implementation Steps

    In this tutorial, you will work with the default contact forms and the default hierarchy, which is illustrated above in the overview of key concepts.

    While logged in as an admin user, you will first create the Health Facility, CHW Supervisor, CHW Area, and CHW. You will then create the users for the CHW so that they can log in and create households and household members.

    1. Create New Health Facility


    While logged into the CHT application, go to the People tab and select New Health Facility


    For now we will skip creating or assigning a primary contact so that we can focus on creating the new Health Facility.


    Enter the details of the Health Facility and submit the form.


    You should see the newly created Health Facility appear on the left-hand side and when you select it, you will see details of the Health Facility appear on the right-hand side.



    2. Create CHW Area and CHW

    We will now create a Place and the primary contact for it within one form. We want to create a CHW Area within the Health Facility that we previously created.


    Select the Health Facility on the left-hand side. You will then select New Area on the right-hand side.


    Select the option that lets you create a new person within the form. This person will automatically become the primary contact for the created place.


    Fill in the required fields and go to the next section.


    You will get an option to name the Place after the created contact person or name it yourself. If you select Yes, the new place will be named <contact-name>'s Area. For example Jane Doe's Area.


    Once you submit, a new CHW Area will be created. On the right-hand you should see the CHW Area name, the primary contact of the CHW Area, and the Health Facility that the CHW Area belongs to.



    3. Create CHW Supervisor


    To create a primary contact for an existing Place (in this case, for the Health Facility that we created without a primary contact); select the Place and then select the New Person action.


    A new person form will appear with an option to change the Place the new person will belong to. A new contact will be created in the Health Facility when you submit this form.


    Finally, we will set the newly created person as a primary contact for the Health Facility they belong to. To do this, select the Health Facility and then select the Edit action.


    You should see an edit form from which you can set the primary contact of the Health Facility. Click Submit to apply the changes.



    4. Create the CHW User

    You may want to log in as a CHW and perform some actions now that the CHW and CHW Supervisor contacts are created; let’s create a CHW user who’s linked to the CHW contact we created earlier.


    Go to the hamburger menu and select App Settings.


    When you are on the App Settings page, select Users on the left-hand side and then select Add User on the right-hand side.


    You should now see an Add User Form. Fill in the user name, then select the role as CHW or Regional Admin. In the Place field, select the name of the CHW Area whose CHW you want to create a user for (you can search by typing the first few letters of the CHW Area name). Once that is done, under the Associate Contact field select the name of the CHW whose user you are creating. Finally, input a password and hit Submit.


    Once this is done, you can logout and log into the app using the username and password that you just created.

    Frequently Asked Questions

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/tutorials/contact-and-users-2/index.html b/apps/tutorials/contact-and-users-2/index.html index 710e7f0ed3..b40bf3d370 100644 --- a/apps/tutorials/contact-and-users-2/index.html +++ b/apps/tutorials/contact-and-users-2/index.html @@ -1,9 +1,9 @@ -Contact and User Management - Part 2 | Community Health Toolkit +Contact and User Management - Part 2 | Community Health Toolkit

    Contact and User Management - Part 2

    Creating and editing contacts and users with cht-conf

    In this tutorial you will learn how to create and edit contacts and their associated users in the CHT application using cht-conf. If you haven’t already, have a look at part 1 of this tutorial for a useful overview of key concepts.

    Brief Overview of Key Concepts

    cht-conf is a command-line interface tool to manage and configure your apps built using the Core Framework of the Community Health Toolkit.

    See more key concepts in part 1 of this tutorial.

    Required Resources

    You should have a functioning CHT instance and have cht-conf installed locally. Read How to set up a CHT local configuration environment

    Implementation Steps

    In these steps you are going to create a Health Facility, CHW Areas, primary contacts for the CHW Areas, and their associated users.

    1. Create Health Facilities (using cht-conf’s csv-to-docs and upload-docs features)

    To create contacts and their associated users with cht-conf, you will need to create a CSV file with the information of the contacts and the users that you would like to create. The name of the file determines the type of doc created for rows contained in the file.

    For example, file named place.district_hospital.csv adds the property "type":"district_hospital" and a file named person.clinic.csv add the property "type":"person"

    Create a CSV file named place.district_hospital.csv and add the details of the Health Facilities you would like to create.

    name
    Nairobi South Facility
    Nairobi West Facility
    Nairobi East Facility

    Save this file to a folder name csv in your project’s base directory.

    Open terminal or command line. cd to your project’s base directory and then run the command

    cht csv-to-docs
    + Create project issue

    Contact and User Management - Part 2

    Creating and editing contacts and users with cht-conf

    In this tutorial you will learn how to create and edit contacts and their associated users in the CHT application using cht-conf. If you haven’t already, have a look at part 1 of this tutorial for a useful overview of key concepts.

    Brief Overview of Key Concepts

    cht-conf is a command-line interface tool to manage and configure your apps built using the Core Framework of the Community Health Toolkit.

    See more key concepts in part 1 of this tutorial.

    Required Resources

    You should have a functioning CHT instance and have cht-conf installed locally. Read How to set up a CHT local configuration environment

    Implementation Steps

    In these steps you are going to create a Health Facility, CHW Areas, primary contacts for the CHW Areas, and their associated users.

    1. Create Health Facilities (using cht-conf’s csv-to-docs and upload-docs features)

    To create contacts and their associated users with cht-conf, you will need to create a CSV file with the information of the contacts and the users that you would like to create. The name of the file determines the type of doc created for rows contained in the file.

    For example, file named place.district_hospital.csv adds the property "type":"district_hospital" and a file named person.clinic.csv add the property "type":"person"

    Create a CSV file named place.district_hospital.csv and add the details of the Health Facilities you would like to create.

    name
    Nairobi South Facility
    Nairobi West Facility
    Nairobi East Facility

    Save this file to a folder name csv in your project’s base directory.

    Open terminal or command line. cd to your project’s base directory and then run the command

    cht csv-to-docs
     

    This will convert rows of the CSV files from the csv folder to JSON docs that are stored in a json-docs folder.

    To upload the JSON docs to your local test instance, run the command

    cht --url=https://<username>:<password>@localhost --accept-self-signed-certs upload-docs
     

    Be sure to replace the values <username> and <password> with the actual username and password of your test instance.



    2. Create CHW Areas, CHW Contacts and Users (using cht-conf’s create-users feature)

    Next you are going to create CHW Areas for the Health Facilities you created in the step above along with the CHW contacts and users for these CHW Areas.

    Create a CSV file named users.csv and add the details of the Users, CHW contacts, and CHW Areas you would like to create. Save this file in the base project directory.

    usernamepasswordrolesfullnamephonecontact.namecontact.phonecontact.sexcontact.ageplace.typeplace.nameplace.parent
    mmutisoq3Z5-vH5district_adminMary Mutiso0712345678Mary Mutiso0712345678Female36health_centerMary Mutiso’s Area<facility uuid>


    The value place.parent is the uuid of the Facility to which the CHW Area belongs to. You can get this value by selecting the Health Facility in the webapp and copying the last portion of the url.


    Run the command

    cht --url=https://<username>:<password>@localhost --accept-self-signed-certs create-users
     

    This will create the CHW Area, the CHW contact, and the user that the CHW will use to log into the application.

    Frequently Asked Questions


    CHT Applications > @@ -313,7 +313,8 @@ Bulk Load Users

    How to create users in bulk

    CHT Applications > Concepts > Users

    Defining the user roles and their permissions

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/tutorials/contact-summary/index.html b/apps/tutorials/contact-summary/index.html index d59028e72d..bcae2afd56 100644 --- a/apps/tutorials/contact-summary/index.html +++ b/apps/tutorials/contact-summary/index.html @@ -1,9 +1,9 @@ -Building Contact Summary | Community Health Toolkit +Building Contact Summary | Community Health Toolkit

    Building Contact Summary

    Building CHT application contact summary

    This tutorial will take you through building a contact summary for CHT applications.

    Contact summaries display basic information about the contact.

    You will be adding a contact summary that displays information about a person’s patient id, age, sex, phone number, and information about the place they belong to ie. parent.

    Brief Overview of Key Concepts

    Each field that can be shown on a contact’s profile is defined as an object in the fields array of contact-summary.templated.js.

    The properties for each object determine how and when the field is shown.

    Required Resources

    You should have a functioning CHT instance with cht-conf installed locally, and completed a project folder setup.

    Implementation Steps

    Create a contact-summary.templated.js file. (This may have already been created by the initialise-project-layout command.)

    1. Add Dependencies and Variable Definitions

    Add the following dependencies and variable definitions at the top of the file:

    const thisContact = contact;
    + Create project issue

    Building Contact Summary

    Building CHT application contact summary

    This tutorial will take you through building a contact summary for CHT applications.

    Contact summaries display basic information about the contact.

    You will be adding a contact summary that displays information about a person’s patient id, age, sex, phone number, and information about the place they belong to ie. parent.

    Brief Overview of Key Concepts

    Each field that can be shown on a contact’s profile is defined as an object in the fields array of contact-summary.templated.js.

    The properties for each object determine how and when the field is shown.

    Required Resources

    You should have a functioning CHT instance with cht-conf installed locally, and completed a project folder setup.

    Implementation Steps

    Create a contact-summary.templated.js file. (This may have already been created by the initialise-project-layout command.)

    1. Add Dependencies and Variable Definitions

    Add the following dependencies and variable definitions at the top of the file:

    const thisContact = contact;
     const thisLineage = lineage;
     


    2. Define Contact Summary Fields

    Define the patient_id, age, sex, phone, and parent contact fields as shown below:

    const thisContact = contact;
     const thisLineage = lineage;
    @@ -334,7 +334,8 @@
     Reference >
     contact-summary.templated.js
     : Contact Summary

    Contact Pages: Customizing the fields, cards, and actions on profile pages

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/tutorials/couch2pg-setup/index.html b/apps/tutorials/couch2pg-setup/index.html index 6d07f4f11e..548415307f 100644 --- a/apps/tutorials/couch2pg-setup/index.html +++ b/apps/tutorials/couch2pg-setup/index.html @@ -1,9 +1,9 @@ -Local Couch2pg Setup | Community Health Toolkit +Local Couch2pg Setup | Community Health Toolkit

    Local Couch2pg Setup

    Setting up a Couch2pg service to download data from CouchDB to Postgres Database

    This tutorial will take you through setting up a Couch2pg service.

    By the end of the tutorial you should be able to:

    • Set up a Couch2pg service
    • Run the Couch2pg service

    CHT Couch2pg is a background process that moves data from Couchdb to Postgres through one way replication. It therefore, needs to have full read and write access to both the Postgres Database and Couchdb upstream. It is built in nodejs and can be set up as a background process using systemd. Review this architecture diagram to get a conceptual understanding of how couch2pg works.

    Brief Overview of key environmental variables

    COUCHDB_URL is the CouchDB instance URL with no trailing slash after /medic, format: https://[user]:[password]@localhost:[port]/medic

    POSTGRESQL_URL is the PostgreSQL instance URL, format: postgres://[user]:[password]@localhost:[port]/[database name]

    COUCH2PG_SLEEP_MINS is the interval size in minutes Couch2pg will use to poll Couchdb.

    COUCH2PG_DOC_LIMIT is the number of documents Couch2pg will fetch in each query.

    COUCH2PG_RETRY_COUNT is the amount of times couch2pg should retry a failed connection before it fails.

    COUCH2PG_CHANGES_LIMIT is the number of changes to query since the last sync operation.

    To read more about environmental variables, see the CHT Couch2pg readme.

    Required Resources

    Before you begin, you need to have some useful software and tools that are required for things to work:

    Prerequisites

    You should have a functioning CHT instance installed locally

    You should have a working database with a user that has full creation rights on the database. A database POSTGRES_DB_NAME and couch2pg user can be created and access granted using the following query:

    CREATE DATABASE POSTGRES_DB_NAME;
    + Create project issue

    Local Couch2pg Setup

    Setting up a Couch2pg service to download data from CouchDB to Postgres Database

    This tutorial will take you through setting up a Couch2pg service.

    By the end of the tutorial you should be able to:

    • Set up a Couch2pg service
    • Run the Couch2pg service

    CHT Couch2pg is a background process that moves data from Couchdb to Postgres through one way replication. It therefore, needs to have full read and write access to both the Postgres Database and Couchdb upstream. It is built in nodejs and can be set up as a background process using systemd. Review this architecture diagram to get a conceptual understanding of how couch2pg works.

    Brief Overview of key environmental variables

    COUCHDB_URL is the CouchDB instance URL with no trailing slash after /medic, format: https://[user]:[password]@localhost:[port]/medic

    POSTGRESQL_URL is the PostgreSQL instance URL, format: postgres://[user]:[password]@localhost:[port]/[database name]

    COUCH2PG_SLEEP_MINS is the interval size in minutes Couch2pg will use to poll Couchdb.

    COUCH2PG_DOC_LIMIT is the number of documents Couch2pg will fetch in each query.

    COUCH2PG_RETRY_COUNT is the amount of times couch2pg should retry a failed connection before it fails.

    COUCH2PG_CHANGES_LIMIT is the number of changes to query since the last sync operation.

    To read more about environmental variables, see the CHT Couch2pg readme.

    Required Resources

    Before you begin, you need to have some useful software and tools that are required for things to work:

    Prerequisites

    You should have a functioning CHT instance installed locally

    You should have a working database with a user that has full creation rights on the database. A database POSTGRES_DB_NAME and couch2pg user can be created and access granted using the following query:

    CREATE DATABASE POSTGRES_DB_NAME;
     CREATE USER couch2pg WITH ENCRYPTED PASSWORD 'mypassword';
     GRANT ALL PRIVILEGES ON DATABASE POSTGRES_DB_NAME TO couch2pg;
     

    All steps below require you to have a local clone of the repo.

    git clone https://github.com/medic/cht-couch2pg.git
    @@ -314,7 +314,8 @@
     
    1. Run: docker-compose up

    2. Connect to the PostgreSQL instance with login cht_couch2pg, password cht_couch2pg_password and database cht.



    Known issues

    1. Node version compatibility
    • Version 14 and 16 have been known to fail silently, and you can conveniently switch between node versions using nvm.
    1. Postgres authentication
    • If the error Error: Unknown authenticationOk message typeMessage { name: 'authenticationOk', length: 23 } is observed, it is because Postgres is setup to use a different password encryption algorithm compared to what Couch2pg uses. Couch2pg was made to work with md5 which is the default method in Postgres v10-13. However, on postgres v14 the default method is scram-sha-256 detailed in the notes.

    • The setting can be updated in the Postgres configuration file which is in /etc/postgresql/14/main/postgres.conf in Ubuntu 20.04. The key password_encryption should be set to md5. After updating the setting, restart the Postgres service.

    • To confirm that the role used with couch2pg has an md5 encrypted password use the query SELECT rolname, rolpassword FROM pg_authid. The role password should start with md5.




    CHT Applications > Tutorials > Getting started

    Setting up a local environment to build and test CHT 4.x applications

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/tutorials/death-reporting/index.html b/apps/tutorials/death-reporting/index.html index 6601702209..6f8884acea 100644 --- a/apps/tutorials/death-reporting/index.html +++ b/apps/tutorials/death-reporting/index.html @@ -1,9 +1,9 @@ -Building Death Report Workflows | Community Health Toolkit +Building Death Report Workflows | Community Health Toolkit

    Building Death Report Workflows

    Building Death Report Workflows

    Death Reporting

    Guide for setting up a comprehensive death report workflow

    In this tutorial you will learn how to set up a death report workflow. This includes laying out a death report form as well as handling all the configurations needed for wiring it up in the CHT. + Create project issue

    Building Death Report Workflows

    Building Death Report Workflows

    Death Reporting

    Guide for setting up a comprehensive death report workflow

    In this tutorial you will learn how to set up a death report workflow. This includes laying out a death report form as well as handling all the configurations needed for wiring it up in the CHT. By the end of the tutorial you should be able to:

    • Mark select contacts as deceased
    • Make relevant app updates for dead contacts

    Brief Overview of Key Concepts

    When a contact is marked as deceased within the CHT, the contact will be hidden by default on the contacts tab.

    Required Resources

    You will need to:

    1. Configure your application hierarchy
    2. Create some contacts
    3. Know how to create an app form
    4. Know how to set form properties

    Implementation Steps

    1. Create a new app form with a name like “Death Report”. This will be used to flag a contact as deceased.
    2. Set the form properties to show for contacts that can die.
    3. Enable the death_reporting transition.
    4. Make some recommended updates to tasks, targets, and contact-summary.

    1. Create the Death Form

    Create a new app form with your desired experience for reporting a death. Or you can use the death_report.xlsx and death_report.properties.json files from this reference application.

    It is common to want to know the date of death, place of death, or cause of death when reporting a death. If you want to ask date of the contact’s death, use a field of type date. This information will be used again in step 3.

    2. Edit the Form Properties.json File

    It doesn’t make sense to have “places” in your hierarchy that can be deceased. It also doesn’t make sense for somebody who is dead to die again. But can the administration of a health facility die? That is for you to decide.

    This snippet is an example form properties file which constrains the death form to show only for contacts which:

    1. Are currently alive
    2. Are within a family
    3. Have a contact_type with “person: true”
    {
     	"context":  {
     		"expression":  "!contact.date_of_death && user.parent.contact_type === 'family'",
    @@ -356,7 +356,8 @@
     app_settings.json >
     .transitions
     : Death Reporting

    Sentinel Transitions: functions executed when database documents change

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/tutorials/form-properties/index.html b/apps/tutorials/form-properties/index.html index 97d3bb7aa3..49a83b634f 100644 --- a/apps/tutorials/form-properties/index.html +++ b/apps/tutorials/form-properties/index.html @@ -1,9 +1,9 @@ -Setting Form Properties | Community Health Toolkit +Setting Form Properties | Community Health Toolkit

    Setting Form Properties

    How to set form properties that contain meta information related to App forms

    This tutorial will take you through how to write the <form_name>.properties.json file.

    The <form_name>.properties.json file allows you to add logic that ensures that the right action appears for the right contacts (people and places). For instance, an assessment form for children under-5 will only appear for person contacts on the CHT whose age is less than 5.

    You will be adding meta-data and context to an assessment workflow that allows Community Health Workers to conduct a health assessment for children under the age of 5.

    Brief Overview of Key Concepts

    Form context defines when and where the form should be available in the app.

    Required Resources

    You should have a functioning CHT instance with cht-conf installed locally, completed a project folder setup, and an assessment form.

    Implementation Steps

    Create a new file in the same folder as your assessment.xlsx file and name it assessment.properties.json.

    1. Define the Form’s Title

    Edit the assessment.properties.json file and add a title key with the value corresponding to the desired file title.

    Setting Form Properties

    How to set form properties that contain meta information related to App forms

    This tutorial will take you through how to write the <form_name>.properties.json file.

    The <form_name>.properties.json file allows you to add logic that ensures that the right action appears for the right contacts (people and places). For instance, an assessment form for children under-5 will only appear for person contacts on the CHT whose age is less than 5.

    You will be adding meta-data and context to an assessment workflow that allows Community Health Workers to conduct a health assessment for children under the age of 5.

    Brief Overview of Key Concepts

    Form context defines when and where the form should be available in the app.

    Required Resources

    You should have a functioning CHT instance with cht-conf installed locally, completed a project folder setup, and an assessment form.

    Implementation Steps

    Create a new file in the same folder as your assessment.xlsx file and name it assessment.properties.json.

    1. Define the Form’s Title

    Edit the assessment.properties.json file and add a title key with the value corresponding to the desired file title.

    {
       "title": "Assessment"
     }
     

    2. Define the Form’s Icon

    Add a resources folder in your project folder and put your preferred icon for assessment in it. Name the icon file icon-healthcare-assessment.png if it is a png file or icon-healthcare-assessment.svg if it is an svg file.

    Create a resources.json file in your project folder and add key/value pairs for your icon resources.

    {
    @@ -327,7 +327,8 @@
     app
     : Formsappform Namepropertiesjson

    App Forms: Used to complete reports, tasks, and actions in the app

    Design System > Best Practices

    This document covers the configuration best practices of forms, tasks, targets, and contact profiles when building your own community health app.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/tutorials/index.html b/apps/tutorials/index.html index 361fbbbad4..2f33480171 100644 --- a/apps/tutorials/index.html +++ b/apps/tutorials/index.html @@ -1,9 +1,9 @@ -Step-by-Step Tutorials | Community Health Toolkit +Step-by-Step Tutorials | Community Health Toolkit

    Step-by-Step Tutorials

    A growing collection of thorough step-by-step tutorials for developing and deploying digital health apps with the Community Health Toolkit.

    Getting started building a CHT app

    Setting up a local environment to build and test CHT 4.x applications

    Local Couch2pg Setup

    Setting up a Couch2pg service to download data from CouchDB to Postgres Database

    Contact and User Management - Part 1

    Creating and editing contacts and users in the CHT UI

    Contact and User Management - Part 2

    Creating and editing contacts and users with cht-conf

    Building SMS Forms

    Building CHT application SMS forms

    CHT Application Settings

    Managing CHT application settings

    Setting up Multi-facility users

    Creating and assigning users to multiple places in the CHT UI

    Building SMS Schedules

    Building CHT application SMS schedules

    Building App Forms

    Building CHT app forms

    Setting Form Properties

    How to set form properties that contain meta information related to App forms

    Building A Simple Task

    Writing and testing a simple task

    Building Target Widgets

    How to build CHT monthly and all time target widgets

    Building Contact Summary

    Building CHT application contact summary

    Building Death Report Workflows

    Building Death Report Workflows

    Building Condition Cards

    Building CHT application condition cards

    Localization

    Localizing language in the CHT

    Configuring CHT Application Graphics

    Configuring CHT Application Graphics

    Application Tests

    Guides for writing automated tests for CHT applications

    CHT User Management tool

    How to use and configure a user management tool for your CHT project.

    Building A Complex Task (Optional)

    Building a more complex task

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/tutorials/index.xml b/apps/tutorials/index.xml index d9bab935bb..83316bf7c4 100644 --- a/apps/tutorials/index.xml +++ b/apps/tutorials/index.xml @@ -1,4233 +1,41 @@ -Community Health Toolkit – Step-by-Step Tutorialshttps://docs.communityhealthtoolkit.org/apps/tutorials/Recent content in Step-by-Step Tutorials on Community Health ToolkitHugo -- gohugo.ioenApps: Getting started building a CHT apphttps://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/ -<div class="pageinfo pageinfo-primary"> -<p>This tutorial will take you through setting up a local environment to build and test CHT applications on CHT version 4.x. This includes setting up the necessary tools to download and run the CHT public docker image as well as a command line interface tool to manage and build CHT apps.</p> -<p>By the end of the tutorial you should be able to:</p> -<ul> -<li>View the login page to CHT webapp on localhost</li> -<li>Upload default settings to localhost</li> -</ul> -</div> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -This guide will only work with CHT 4.x instances. See the -<a href="https://docs.communityhealthtoolkit.org/hosting/3.x/app-developer/">3.x App Developer Hosting</a> for setting up comparable 3.x instances. -</div> -<h2 id="brief-overview-of-key-concepts">Brief Overview of Key Concepts</h2> -<p>The <em>CHT Core Framework</em> makes it faster to build full-featured, scalable digital health apps by providing a foundation developers can build on. These apps can support most languages, are <a href="https://docs.communityhealthtoolkit.org/core/overview/offline-first/">Offline-First</a>, and work on basic phones (via SMS), smartphones, tablets, and computers.</p> -<p><a href="https://github.com/medic/cht-conf"><em>CHT Project Configurer</em></a> also known as <em><strong>cht-conf</strong></em> is a command-line interface tool to manage and configure CHT apps.</p> -<p><em>Docker</em> is a tool designed to make it easier to create, deploy, and run applications by using containers.</p> -<p><em>Containers</em> allow a developer to package up an application with all of the parts it needs, such as libraries and other dependencies, and deploy it as one package.</p> -<h2 id="setup-environment">Setup environment</h2> -<p>CHT app development can be done on Linux, macOS, or Windows (using the <a href="https://learn.microsoft.com/en-us/windows/wsl/install">Windows Subsystem for Linux (WSL2)</a>).</p> -<p>CHT apps can be built on your local system (with the necessary libraries installed and configured) or they can be built from within VS Code Dev Containers.</p> -<p>Before you begin, ensure you have the following tools:</p> -<ul> -<li><a href="https://git-scm.com/downloads">git</a> or the <a href="https://desktop.github.com/">Github Desktop</a></li> -<li><a href="https://docs.communityhealthtoolkit.org/hosting/requirements/#docker">docker and docker-compose</a>.</li> -</ul> -<h3 id="installing-docker">Installing Docker</h3> -<ul class="nav nav-tabs" id="tabs-0" role="tablist"> -<li class="nav-item"> -<button class="nav-link active" -id="tabs-00-00-tab" data-bs-toggle="tab" data-bs-target="#tabs-00-00" role="tab" -aria-controls="tabs-00-00" aria-selected="true"> -Linux (Ubuntu) -</button> -</li><li class="nav-item"> -<button class="nav-link" -id="tabs-00-01-tab" data-bs-toggle="tab" data-bs-target="#tabs-00-01" role="tab" -aria-controls="tabs-00-01" aria-selected="false"> -macOS -</button> -</li><li class="nav-item"> -<button class="nav-link" -id="tabs-00-02-tab" data-bs-toggle="tab" data-bs-target="#tabs-00-02" role="tab" -aria-controls="tabs-00-02" aria-selected="false"> -Windows (WSL2) -</button> -</li> -</ul> -<div class="tab-content" id="tabs-0-content"> -<div class="tab-body tab-pane fade show active" -id="tabs-00-00" role="tabpanel" aria-labelled-by="tabs-00-00-tab" tabindex="0"> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>curl -fsSL get.docker.com -o get-docker.sh <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> sh get-docker.sh -</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"># OPTIONAL: Allow user to run Docker without sudo</span> -</span></span><span style="display:flex;"><span>dockerd-rootless-setuptool.sh install -</span></span><span style="display:flex;"><span><span style="color:#204a87">echo</span> <span style="color:#4e9a06">&#34;export PATH=/usr/bin:</span><span style="color:#000">$PATH</span><span style="color:#4e9a06">&#34;</span> &gt;&gt; ~/.<span style="color:#204a87;font-weight:bold">$(</span>basename <span style="color:#000">$SHELL</span><span style="color:#204a87;font-weight:bold">)</span>rc -</span></span><span style="display:flex;"><span><span style="color:#204a87">echo</span> <span style="color:#4e9a06">&#34;export DOCKER_HOST=unix:///run/user/1000/docker.sock&#34;</span> &gt;&gt; ~/.<span style="color:#204a87;font-weight:bold">$(</span>basename <span style="color:#000">$SHELL</span><span style="color:#204a87;font-weight:bold">)</span>rc -</span></span><span style="display:flex;"><span>. ~/.<span style="color:#204a87;font-weight:bold">$(</span>basename <span style="color:#000">$SHELL</span><span style="color:#204a87;font-weight:bold">)</span>rc -</span></span></code></pre></div> -</div> -<div class="tab-body tab-pane fade" -id="tabs-00-01" role="tabpanel" aria-labelled-by="tabs-00-01-tab" tabindex="0"> -<p>Download and install <a href="https://www.docker.com/products/docker-desktop">Docker Desktop</a> or <a href="https://github.com/abiosoft/colima#readme">Colima</a>.</p> -</div> -<div class="tab-body tab-pane fade" -id="tabs-00-02" role="tabpanel" aria-labelled-by="tabs-00-02-tab" tabindex="0"> -<p>Download and install <a href="https://www.docker.com/products/docker-desktop">Docker Desktop</a>.</p> -</div> -</div> -<p>Restart your entire machine to finish initializing Docker.</p> -<p>After restarting, verify Docker is running as expected. Run the simple <code>hello-world</code> Docker container. This should output &ldquo;Hello from Docker!&rdquo; as well as some other intro text:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>docker run hello-world -</span></span></code></pre></div> -<h3 id="initialize-project-directory">Initialize project directory</h3> -<p>Using the terminal (or the WSL shell on Windows: <em>Start &gt; wsl</em>), run the following commands to create a new project directory for your CHT app:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>mkdir -p ~/cht-project -</span></span><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/cht-project -</span></span></code></pre></div><hr> -<h3 id="developing-locally">Developing locally</h3> -<p>To build CHT apps on your local system, you need to have some additional tools:</p> -<ul class="nav nav-tabs" id="tabs-5" role="tablist"> -<li class="nav-item"> -<button class="nav-link active" -id="tabs-05-00-tab" data-bs-toggle="tab" data-bs-target="#tabs-05-00" role="tab" -aria-controls="tabs-05-00" aria-selected="true"> -Linux (Ubuntu) -</button> -</li><li class="nav-item"> -<button class="nav-link" -id="tabs-05-01-tab" data-bs-toggle="tab" data-bs-target="#tabs-05-01" role="tab" -aria-controls="tabs-05-01" aria-selected="false"> -macOS -</button> -</li><li class="nav-item"> -<button class="nav-link" -id="tabs-05-02-tab" data-bs-toggle="tab" data-bs-target="#tabs-05-02" role="tab" -aria-controls="tabs-05-02" aria-selected="false"> -Windows (WSL2) -</button> -</li> -</ul> -<div class="tab-content" id="tabs-5-content"> -<div class="tab-pane fade show active" -id="tabs-05-00" role="tabpanel" aria-labelled-by="tabs-05-00-tab" tabindex="5"> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>sudo apt update <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> sudo apt -y dist-upgrade -</span></span><span style="display:flex;"><span>sudo apt -y install python3-pip python3-setuptools python3-wheel xsltproc -</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"># Use NVM to install NodeJS:</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87">export</span> <span style="color:#000">nvm_version</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">`</span>curl -s https://api.github.com/repos/nvm-sh/nvm/releases/latest <span style="color:#000;font-weight:bold">|</span> jq -r .name<span style="color:#4e9a06">`</span> -</span></span><span style="display:flex;"><span>curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/<span style="color:#000">$nvm_version</span>/install.sh <span style="color:#000;font-weight:bold">|</span> <span style="color:#000">$SHELL</span> -</span></span><span style="display:flex;"><span>. ~/.<span style="color:#204a87;font-weight:bold">$(</span>basename <span style="color:#000">$SHELL</span><span style="color:#204a87;font-weight:bold">)</span>rc -</span></span><span style="display:flex;"><span>nvm install <span style="color:#0000cf;font-weight:bold">20</span></span></span></code></pre></div> -</div> -<div class="tab-pane fade" -id="tabs-05-01" role="tabpanel" aria-labelled-by="tabs-05-01-tab" tabindex="5"> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"># Uses Homebrew: https://brew.sh/</span> -</span></span><span style="display:flex;"><span>brew update -</span></span><span style="display:flex;"><span>brew install curl jq pyenv git make node@20 gcc -</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"># Python no longer included by default in macOS &gt;12.3</span> -</span></span><span style="display:flex;"><span>pyenv install 2.7.18 -</span></span><span style="display:flex;"><span>pyenv global 2.7.18 -</span></span><span style="display:flex;"><span><span style="color:#204a87">echo</span> <span style="color:#4e9a06">&#34;eval \&#34;\$(pyenv init --path)\&#34;&#34;</span> &gt;&gt; ~/.<span style="color:#204a87;font-weight:bold">$(</span>basename <span style="color:#000">$SHELL</span><span style="color:#204a87;font-weight:bold">)</span>rc -</span></span><span style="display:flex;"><span>. ~/.<span style="color:#204a87;font-weight:bold">$(</span>basename <span style="color:#000">$SHELL</span><span style="color:#204a87;font-weight:bold">)</span>rc</span></span></code></pre></div> -</div> -<div class="tab-pane fade" -id="tabs-05-02" role="tabpanel" aria-labelled-by="tabs-05-02-tab" tabindex="5"> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>sudo apt update <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> sudo apt -y dist-upgrade -</span></span><span style="display:flex;"><span>sudo apt -y install python3-pip python3-setuptools python3-wheel xsltproc -</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"># Use NVM to install NodeJS:</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87">export</span> <span style="color:#000">nvm_version</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">`</span>curl -s https://api.github.com/repos/nvm-sh/nvm/releases/latest <span style="color:#000;font-weight:bold">|</span> jq -r .name<span style="color:#4e9a06">`</span> -</span></span><span style="display:flex;"><span>curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/<span style="color:#000">$nvm_version</span>/install.sh <span style="color:#000;font-weight:bold">|</span> <span style="color:#000">$SHELL</span> -</span></span><span style="display:flex;"><span>. ~/.<span style="color:#204a87;font-weight:bold">$(</span>basename <span style="color:#000">$SHELL</span><span style="color:#204a87;font-weight:bold">)</span>rc -</span></span><span style="display:flex;"><span>nvm install <span style="color:#0000cf;font-weight:bold">20</span></span></span></code></pre></div> -</div> -</div> -<h4 id="pyxform"><code>pyxform</code></h4> -<p>Using python on your terminal, install <code>pyxform</code> globally using the command below.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>sudo python -m pip install git+https://github.com/medic/pyxform.git@medic-conf-1.17#egg<span style="color:#ce5c00;font-weight:bold">=</span>pyxform-medic -</span></span></code></pre></div><p>If you encounter the error <code>npm ERR! gyp ERR verb find Python Python is not set</code> while installing pyxform and are running macOS, see <a href="https://docs.communityhealthtoolkit.org/contribute/code/core/dev-environment/#macos--123">this troubleshooting section</a>.</p> -<h4 id="cht-conf"><code>cht-conf</code></h4> -<p>Using npm on your terminal, install <code>cht-conf</code> globally using the command below.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>npm install -g cht-conf -</span></span></code></pre></div> -<figure class="right col-6 col-lg-8"><a href="confirm-cht-conf.png"> -<img src="confirm-cht-conf.png"/> </a> -</figure> -<p>You can confirm that the installation was successful by typing <code>cht</code> in your terminal.</p> -<p>If you have trouble installing <code>cht-conf</code>, see the application&rsquo;s <a href="https://github.com/medic/cht-conf">GitHub repository</a> for more information.</p> -<p>Using the terminal (or the WSL shell on Windows: <em>Start &gt; wsl</em>), run the following commands from within your project directory (created above) to bootstrap your new CHT project:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/cht-project -</span></span><span style="display:flex;"><span>cht initialise-project-layout -</span></span></code></pre></div><br clear="all"> -<hr> -<h3 id="developing-with-vs-code-dev-container">Developing with VS Code Dev Container</h3> -<p>If you want to develop CHT apps with VS Code, you can use the <code>cht-app-ide</code> Docker image as a <a href="https://code.visualstudio.com/docs/devcontainers/containers">Development Container</a>. This will allow you to use the <code>cht-conf</code> utility and its associated tech stack from within VS Code (without needing to install dependencies like NodeJS on your host system).</p> -<p><a href="https://code.visualstudio.com/">Install VS Code</a> if you do not have it already.</p> -<p>Using the terminal (or the WSL shell on Windows: <em>Start &gt; wsl</em>, or <a href="https://gitforwindows.org/">Git Bash for Windows</a> without WSL), run the following commands from within your project directory (created above) to download the <code>.devcontainer.json</code> config file, install the <a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers">Dev Containers extension</a>, and open the project directory in VSCode:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/cht-project -</span></span><span style="display:flex;"><span>curl -s https://raw.githubusercontent.com/medic/cht-conf/main/devcontainer.cht-app-ide/.devcontainer.json &gt; .devcontainer.json -</span></span><span style="display:flex;"><span>code --install-extension ms-vscode-remote.remote-containers -</span></span><span style="display:flex;"><span>code -n . -</span></span></code></pre></div><p>When opening VS Code, you may be prompted with the question:</p> -<blockquote> -<p><strong>Do you trust the authors of the files in this folder?</strong></p> -</blockquote> -<p>Choose, &ldquo;Yes, I trust the authors&rdquo;.</p> -<p>Open the Command Palette in VS Code (<em>Ctrl+Shift+P</em> or <em>Cmd+Shift+P</em>) and select <code>Reopen in Container</code>. This will open your workspace inside a container based on the <code>cht-app-ide</code> image. You can use the <code>cht</code> commands by opening a terminal in VS Code (<em>Ctrl+Shift+`</em> or <em>Cmd+Shift+`</em>). If prompted &ldquo;Do you trust the authors&hellip;&rdquo; choose &ldquo;Trust Folder &amp; Continue&rdquo;.</p> -<p>Run the following command in the VS Code terminal to bootstrap your new CHT project:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>cht initialise-project-layout -</span></span></code></pre></div><h4 id="terminal-environment">Terminal environment</h4> -<p>When opening a terminal in VS Code in a development container, the terminal will be running on the <em>container environment</em> by default. This is what gives you access to the various <code>cht</code> commands. However, this also means you do NOT have access, within the default VS Code terminal, to commands from your <em>host environment</em>. So, for example, you cannot run <code>docker</code> commands since Docker is not installed inside the container.</p> -<p>To open a terminal running on you <em>host environment</em> in VS Code, open the Command Palette (<em>Ctrl+Shift+P</em> or <em>Cmd+Shift+P</em>) and select <code>Create New Integrated Terminal (Local)</code>. Just remember that you will NOT be able to run <code>cht</code> commands from this terminal since cht-conf is not installed on your host machine.</p> -<h4 id="note-on-connecting-to-a-local-cht-instance">Note on connecting to a local CHT instance</h4> -<p>When using <code>cht-conf</code> within a Docker container to connect to a CHT instance that is running on your local machine (e.g. a development instance), you cannot use the <code>--local</code> flag or <code>localhost</code> in your <code>--url</code> parameter (since these will be interpreted as &ldquo;local to the container&rdquo;).</p> -<p>It is recommended to run a local CHT instance using the <a href="https://docs.communityhealthtoolkit.org/hosting/4.x/app-developer/#cht-docker-helper-for-4x">CHT Docker Helper script</a>. You can connect to the resulting <code>...local-ip.medicmobile.org</code> URL from the Docker container (or the VS Code terminal). (Just make sure the port your CHT instance is hosted on is not blocked by your firewall).</p> -<hr> -<h2 id="deploy-local-cht-instance">Deploy local CHT instance</h2> -<p>Now that you have the dependent tools and software installed, you are ready to set up your local CHT environment.</p> -<p>Refer to the <a href="https://docs.communityhealthtoolkit.org/hosting/4.x/app-developer/">App Developer Hosting Guide</a> for instructions on how to deploy a local CHT instance.</p> -<p>Note that the first time you run your CHT instance it may take a while. In case you run into issues running your docker file, ensure that the following setting in Docker is checked.</p> -<blockquote> -<p>Settings &raquo; General &raquo; Use Docker Compose V2</p> -</blockquote> -<figure class="right col-6 col-lg-8"><a href="medic-login.png"> -<img src="medic-login.png"/> </a> -</figure> -<p>Once your instance has started, navigate to <a href="https://localhost">https://localhost</a> with the Google Chrome browser and login with the default username <code>medic</code> and default password <code>password</code>.</p> -<p>You might get an error &ldquo;Your connection is not private&rdquo; (see <a href="./privacy.error.png">screenshot</a>). Click &ldquo;Advanced&rdquo; and then click &ldquo;Proceed to localhost&rdquo;.</p> -<p>If you are using macOS you will not be able to find the &ldquo;Proceed to localhost&rdquo; link in Chrome, to bypass that error just click anywhere on the denial page and type &ldquo;thisisunsafe&rdquo;.</p> -<p>This error can be fixed by <a href="#optional-install-valid-tls-certificate">installing a TLS certificate</a> as described below.</p> -<p>If you encounter an error <code>bind: address already in use</code>, see the <a href="https://docs.communityhealthtoolkit.org/hosting/3.x/self-hosting/#port-conflicts">Port Conflicts section</a> in the Docker Setup guide.</p> -<p>This CHT instance is empty and has no data in it. While you&rsquo;re free to explore and add your own data, in step 3 below we will upload sample data. Proceed to step 2 to install <code>cht-conf</code> which is needed to upload the test data.</p> -<br clear="all"> -<hr> -<h3 id="upload-test-data">Upload Test Data</h3> -<p>By default, the CHT will have the <a href="https://docs.communityhealthtoolkit.org/apps/examples/anc/">Maternal &amp; Newborn Health Reference Application</a> installed. To upload demo data you can use <code>cht-conf</code>:</p> -<figure class="right col-3 col-lg-6"><a href="test.data.png"> -<img src="test.data.png"/> </a> -</figure> -<ul> -<li>Clone <code>cht-core</code> on your computer using the following command:</li> -</ul> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>git clone https://github.com/medic/cht-core.git -</span></span></code></pre></div><ul> -<li>Navigate your terminal to the <code>cht-core/config/default</code> directory. This is where the reference application is stored.</li> -<li>Run the following <code>cht-conf</code> command to compile and upload default test data to your local instance:</li> -</ul> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>cht --url<span style="color:#ce5c00;font-weight:bold">=</span>https://medic:password@localhost --accept-self-signed-certs csv-to-docs upload-docs -</span></span></code></pre></div><p>With the test data uploaded, log back into your CHT instance and note the &ldquo;Test Health Facility&rdquo; and related data.</p> -<br clear="all"> -<hr> -<h3 id="upload-a-blank-project">Upload a Blank Project</h3> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -This step will erase the default Maternal &amp; Newborn Health Reference Application. -</div> -<p>You can also upload the blank project you created above (via the <code>cht initialise-project-layout</code> command).</p> -<p>Deploy the blank project onto your local test environment with the following command:</p> -<ul class="nav nav-tabs" id="tabs-15" role="tablist"> -<li class="nav-item"> -<button class="nav-link active" -id="tabs-15-00-tab" data-bs-toggle="tab" data-bs-target="#tabs-15-00" role="tab" -aria-controls="tabs-15-00" aria-selected="true"> -Local -</button> -</li><li class="nav-item"> -<button class="nav-link" -id="tabs-15-01-tab" data-bs-toggle="tab" data-bs-target="#tabs-15-01" role="tab" -aria-controls="tabs-15-01" aria-selected="false"> -Dev Container -</button> -</li> -</ul> -<div class="tab-content" id="tabs-15-content"> -<div class="tab-pane fade show active" -id="tabs-15-00" role="tabpanel" aria-labelled-by="tabs-15-00-tab" tabindex="15"> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"># accept-self-signed-certs bypasses normal SSL certificate verification. This is necessary when connecting to a local CHT instance.</span> -</span></span><span style="display:flex;"><span>cht --url<span style="color:#ce5c00;font-weight:bold">=</span>https://medic:password@localhost --accept-self-signed-certs</span></span></code></pre></div> -</div> -<div class="tab-pane fade" -id="tabs-15-01" role="tabpanel" aria-labelled-by="tabs-15-01-tab" tabindex="15"> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"># Requires instance started with CHT Docker Helper (accessible via a local-ip.medicmobile.org URL)</span> -</span></span><span style="display:flex;"><span>cht --url<span style="color:#ce5c00;font-weight:bold">=</span>https://medic:password@&lt;your-local-ip.medicmobile.org-url&gt;</span></span></code></pre></div> -</div> -</div> -<figure class="right col-6 col-lg-8"><a href="all-actions-completed.png"> -<img src="all-actions-completed.png"/> </a> -</figure> -<p>If the above command shows an error similar to this one <code>ERROR Error: Webpack warnings when building contact-summary</code> you will need to install all the dependencies and libraries it needs (by running <code>npm ci</code>) before trying to upload the configuration again with the <code>cht ...</code> command.</p> -<p>Once you have run the above command it should complete with the message: <code>INFO All actions completed.</code>.</p> -<br clear="all"> -<hr> -<h3 id="optional-install-valid-tls-certificate">Optional: Install Valid TLS Certificate</h3> -<figure class="right col-6 col-lg-8"><a href="local-ip.TLS.png"> -<img src="local-ip.TLS.png"/> </a> -</figure> -<p>With the blank project deployed to your CHT instance, you&rsquo;re ready to start writing your first app. A big part of authoring an app is testing it on a mobile device, likely using the unbranded version of <a href="https://github.com/medic/cht-android">CHT Android</a>. In order to test in the APK, your CHT instance needs a valid TLS certificate which the default docker version does not have.</p> -<p>To install a valid certificate, open a terminal in the <code>cht-core</code> directory. Ensure the CHT instance is running and make this call:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>./scripts/add-local-ip-certs-to-docker-4.x.sh cht_nginx_1 -</span></span></code></pre></div><p>If <code>add-local-ip-certs-to-docker-4.x.sh</code> is not in your scripts directory, be sure to use <code>git</code> or GitHub Desktop to update your local repository with the latest changes. If you can&rsquo;t update for some reason, you can <a href="https://raw.githubusercontent.com/medic/cht-core/master/scripts/add-local-ip-certs-to-docker-4.x.sh">download it directly</a>.</p> -<p>To see what a before and after looks like, note the screenshot to the left which uses <code>curl</code> to test the certificate validity.</p> -<p>The output of <code>add-local-ip-certs-to-docker.sh</code> looks like this:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>cht_nginx_1 -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span>If just the container name is shown above, a fresh local-ip.medicmobile.org certificate was downloaded. -</span></span></code></pre></div><p>The IP of your computer is used in the URL of the CHT instance now. For example, if your IP is <code>192.168.68.40</code> then the CHT URL with a valid TLS certificate is <code>192-168-68-40.local-ip.medicmobile.org</code>. See the <a href="https://local-ip.medicmobile.org/">local-ip.medicmobile.org</a> site to read more about these free-to-use certificates.</p> -<p>When using <code>cht-conf</code> you can now drop the use of <code>--accept-self-signed-certs</code>. Further, update the URL to be based on your IP. Using the example IP above, this would be <code>--url=https://medic:password@192-168-68-40.local-ip.medicmobile.org</code>. As well, you can now use this URL to test with the CHT Android app.</p> -<h2 id="frequently-asked-questions">Frequently Asked Questions</h2> -<ul> -<li><a href="https://forum.communityhealthtoolkit.org/t/cant-upgrade-to-3-8-version/608">How do I upgrade to a higher version of the webapp?</a></li> -<li><a href="https://forum.communityhealthtoolkit.org/t/unable-to-install-core-framework-in-cloud-instance/533">How do I access the instance remotely?</a></li> -<li><a href="https://forum.communityhealthtoolkit.org/t/installing-cht-conf/1593">Error &lsquo;No module named pip&rsquo; when installing cht-conf</a></li> -<li><a href="https://forum.communityhealthtoolkit.org/t/couchdb-failed-to-start-properly/1683">CouchDB failed to start properly</a></li> -<li><a href="https://forum.communityhealthtoolkit.org/t/enable-adding-contacts-in-a-blank-project-deployed-onlocal-test-environment-for-cht-instance/1652">Enable adding contacts in a blank project</a></li> -</ul>Apps: Local Couch2pg Setuphttps://docs.communityhealthtoolkit.org/apps/tutorials/couch2pg-setup/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/couch2pg-setup/ -<div class="pageinfo pageinfo-primary"> -<p>This tutorial will take you through setting up a Couch2pg service.</p> -<p>By the end of the tutorial you should be able to:</p> -<ul> -<li>Set up a Couch2pg service</li> -<li>Run the Couch2pg service</li> -</ul> -</div> -<p><a href="https://github.com/medic/cht-couch2pg">CHT Couch2pg</a> is a background process that moves data from Couchdb to Postgres through one way replication. It therefore, needs to have full read and write access to both the Postgres Database and Couchdb upstream. It is built in nodejs and can be set up as a background process using systemd. Review this <a href="https://docs.communityhealthtoolkit.org/core/overview/architecture/#overview">architecture diagram</a> to get a conceptual understanding of how couch2pg works.</p> -<h2 id="brief-overview-of-key-environmental-variables">Brief Overview of key environmental variables</h2> -<p><em>COUCHDB_URL</em> is the CouchDB instance URL with no trailing slash after <code>/medic</code>, format: <code>https://[user]:[password]@localhost:[port]/medic</code></p> -<p><em>POSTGRESQL_URL</em> is the PostgreSQL instance URL, format: <code>postgres://[user]:[password]@localhost:[port]/[database name]</code></p> -<p><em>COUCH2PG_SLEEP_MINS</em> is the interval size in minutes Couch2pg will use to poll Couchdb.</p> -<p><em>COUCH2PG_DOC_LIMIT</em> is the number of documents Couch2pg will fetch in each query.</p> -<p><em>COUCH2PG_RETRY_COUNT</em> is the amount of times couch2pg should retry a failed connection before it fails.</p> -<p><em>COUCH2PG_CHANGES_LIMIT</em> is the number of changes to query since the last sync operation.</p> -<p>To read more about environmental variables, see the <a href="https://github.com/medic/cht-couch2pg#readme">CHT Couch2pg readme</a>.</p> -<h2 id="required-resources">Required Resources</h2> -<p>Before you begin, you need to have some useful software and tools that are required for things to work:</p> -<ul> -<li><a href="https://nodejs.org/en/">nodejs</a> 8 up to 12.</li> -<li><a href="https://www.npmjs.com/get-npm">npm</a></li> -<li><a href="https://www.postgresql.org/">PostgreSQL</a> 9.4 or later</li> -</ul> -<h2 id="prerequisites">Prerequisites</h2> -<p>You should have a functioning <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/">CHT instance installed locally</a></p> -<p>You should have a working database with a user that has full creation rights on the database. A database <code>POSTGRES_DB_NAME</code> and <code>couch2pg</code> user can be created and access granted using the following query:</p> -<pre tabindex="0"><code>CREATE DATABASE POSTGRES_DB_NAME; -CREATE USER couch2pg WITH ENCRYPTED PASSWORD &#39;mypassword&#39;; -GRANT ALL PRIVILEGES ON DATABASE POSTGRES_DB_NAME TO couch2pg; -</code></pre><p>All steps below require you to have a local clone of the repo.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>git clone https://github.com/medic/cht-couch2pg.git -</span></span></code></pre></div><h2 id="setting-up-with-environment-variables">Setting up with environment variables</h2> -<ol> -<li> -<p>Change directory into the repo&rsquo;s directory where you cloned it: <code>cd /path/to/cht-couch2pg</code></p> -</li> -<li> -<p>Install dependencies: <code>npm ci</code></p> -</li> -<li> -<p>Export the four variables with the correct values:</p> -</li> -</ol> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">export</span> <span style="color:#000">POSTGRESQL_URL</span><span style="color:#ce5c00;font-weight:bold">=</span>postgres://<span style="color:#ce5c00;font-weight:bold">[</span>user<span style="color:#ce5c00;font-weight:bold">]</span>:<span style="color:#ce5c00;font-weight:bold">[</span>password<span style="color:#ce5c00;font-weight:bold">]</span>@localhost:<span style="color:#ce5c00;font-weight:bold">[</span>port<span style="color:#ce5c00;font-weight:bold">]</span>/<span style="color:#ce5c00;font-weight:bold">[</span>database name<span style="color:#ce5c00;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87">export</span> <span style="color:#000">COUCHDB_URL</span><span style="color:#ce5c00;font-weight:bold">=</span>https://<span style="color:#ce5c00;font-weight:bold">[</span>user<span style="color:#ce5c00;font-weight:bold">]</span>:<span style="color:#ce5c00;font-weight:bold">[</span>password<span style="color:#ce5c00;font-weight:bold">]</span>@localhost:<span style="color:#ce5c00;font-weight:bold">[</span>port<span style="color:#ce5c00;font-weight:bold">]</span>/medic -</span></span><span style="display:flex;"><span><span style="color:#204a87">export</span> <span style="color:#000">COUCH2PG_DOC_LIMIT</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#0000cf;font-weight:bold">1000</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87">export</span> <span style="color:#000">COUCH2PG_RETRY_COUNT</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#0000cf;font-weight:bold">5</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87">export</span> <span style="color:#000">COUCH2PG_SLEEP_MINS</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#0000cf;font-weight:bold">120</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87">export</span> <span style="color:#000">COUCH2PG_CHANGES_LIMIT</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#0000cf;font-weight:bold">1000</span> -</span></span></code></pre></div><ol start="4"> -<li>Run: <code>node .</code></li> -</ol> -<p>If you want to set and save all possible variables:</p> -<ol start="5"> -<li> -<p>Copy <code>sample.env</code> to <code>couch2pg.env</code>: <code>cp sample.env couch2pg.env</code></p> -</li> -<li> -<p>Edit <code>couch2pg.env</code> to have all the variables you need.</p> -</li> -</ol> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -<code>POSTGRESQL_URL</code> shouldn’t be edited as it is defined by the variables above it. -</div> -<ol start="7"> -<li>Run: <code>. ./couch2pg.env &amp;&amp; node .</code></li> -</ol> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -To run cht-couch2pg in interactive mode, use <code>node . -i</code>. You will be prompted to answer questions to capture the same the environmental variables. For each question, you will be given suggestions for an answer. -</div> -<br clear="all"> -<hr> -<h2 id="using-docker-compose">Using docker-compose</h2> -<p>The simplest way to run couch2pg is with <code>docker-compose</code> which only needs configuration of the CouchDB instance URL. The compose file will then create a PostgreSQL container, connect to the CouchDB server and proceed to download couchDB documents to the PostgreSQL container:</p> -<ol> -<li> -<p>Change directory into the repo&rsquo;s directory where you cloned it: <code>cd /path/to/cht-couch2pg</code></p> -</li> -<li> -<p>Set the URL for CouchDB in the <code>COUCHDB_URL</code> env variable. e.g.</p> -</li> -</ol> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">export</span> <span style="color:#000">COUCHDB_URL</span><span style="color:#ce5c00;font-weight:bold">=</span>https://medic:password@192-168-68-26.local-ip.medicmobile.org:8442/medic -</span></span></code></pre></div> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -The CouchDB URL needs to be reachable from the docker container (i.e. not localhost). -</div> -<ol start="3"> -<li> -<p>Run: <code>docker-compose up</code></p> -</li> -<li> -<p>Connect to the PostgreSQL instance with login <code>cht_couch2pg</code>, password <code>cht_couch2pg_password</code> and database <code>cht</code>.</p> -</li> -</ol> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -To set all possible variables or store the variables in configuration file, follow steps 5 and 6 above. To connect to the PostgreSQL instance, use the server from <code>POSTGRES_SERVER_NAME</code>, use login from <code>COUCH2PG_USER</code>, password from <code>COUCH2PG_USER_PASSWORD</code> and the database from <code>POSTGRES_DB_NAME</code>. -</div> -<br clear="all"> -<hr> -<h2 id="known-issues">Known issues</h2> -<ol> -<li>Node version compatibility</li> -</ol> -<ul> -<li>Version 14 and 16 have been known to fail silently, and you can conveniently switch between node versions using <a href="https://github.com/nvm-sh/nvm">nvm</a>.</li> -</ul> -<ol start="2"> -<li>Postgres authentication</li> -</ol> -<ul> -<li> -<p>If the error <code>Error: Unknown authenticationOk message typeMessage { name: 'authenticationOk', length: 23 }</code> is observed, it is because Postgres is setup to use a different password encryption algorithm compared to what Couch2pg uses. Couch2pg was made to work with <code>md5</code> which is the default method in Postgres v10-13. However, on postgres v14 the default method is <code>scram-sha-256</code> detailed in the <a href="https://postgresqlco.nf/doc/en/param/password_encryption/14/">notes</a>.</p> -</li> -<li> -<p>The setting can be updated in the Postgres configuration file which is in <code>/etc/postgresql/14/main/postgres.conf</code> in Ubuntu 20.04. The key <code>password_encryption</code> should be set to md5. After updating the setting, restart the Postgres service.</p> -</li> -<li> -<p>To confirm that the role used with couch2pg has an md5 encrypted password use the query <code>SELECT rolname, rolpassword FROM pg_authid</code>. The role password should start with md5.</p> -</li> -</ul> -<br clear="all"> -<hr>Apps: Contact and User Management - Part 1https://docs.communityhealthtoolkit.org/apps/tutorials/contact-and-users-1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/contact-and-users-1/ -<div class="pageinfo pageinfo-primary"> -<p>In this tutorial you will learn how to create and edit contacts and their associated users in and application built with the CHT using the default contact creation forms. This will help you get familiar with the UI of the webapp as well as some features and functionality. If you are already comfortable with this, you can skip to <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/contact-and-users-2/">part 2, which covers manipulating contacts and their associated documents using cht-conf</a>.</p> -</div> -<h2 id="brief-overview-of-key-concepts">Brief Overview of Key Concepts</h2> -<p><em>Contacts</em> are people or places that are created in the CHT application.</p> -<p><em>People</em> are both patients in the system and users of the system, such as CHWs or Nurses.</p> -<p><em>Places</em> represent either an actual physical location such as a health facility, clinic, or a grouping such as a household or CHW Area.</p> -<p><em>Contact forms</em> are forms in the CHT app that are used to create people or places.</p> -<p><em>CHT App Hierarchy</em> is often modeled after the health system, health program or community structure. All people who are registered in the app must be associated with a Place. These Places are located in a hierarchy with other Places. For instance, a Family Member is part of a Household. A Household and CHWs are part of a CHW Area. A CHW Area and nurses are part of a Health Facility. Additional levels may be added as needed. The Admin level operates outside of the hierarchy and gives access to all levels and people.</p> -<p><img src="app-hierarchy.jpg" alt="app hierarchy" title="Default app hierarchy"></p> -<p><em>Users</em> represent credentials and roles / permissions for accessing the application. This can either be:</p> -<ul> -<li>People who can log into the application, such as CHWs or Nurses or</li> -<li>Credentials granting external software restricted permissions to perform certain tasks, such as allowing an external service permission to write reports via the api.</li> -</ul> -<h2 id="required-resources">Required Resources</h2> -<p>You should have a functioning CHT instance with contact forms configured. Read <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/">How to set up a CHT local configuration environment</a></p> -<h2 id="implementation-steps">Implementation Steps</h2> -<p>In this tutorial, you will work with the default contact forms and the default hierarchy, which is illustrated above in the overview of key concepts.</p> -<p>While logged in as an admin user, you will first create the Health Facility, CHW Supervisor, CHW Area, and CHW. You will then create the users for the CHW so that they can log in and create households and household members.</p> -<h3 id="1-create-new-health-facility">1. Create New Health Facility</h3> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-facility/select-new-facility.png"> -<img src="new-facility/select-new-facility.png"/> </a> -</figure> -<p>While logged into the CHT application, go to the <strong>People tab</strong> and select <strong>New Health Facility</strong></p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-facility/skip-primary-contact.png"> -<img src="new-facility/skip-primary-contact.png"/> </a> -</figure> -<p>For now we will skip creating or assigning a primary contact so that we can focus on creating the new Health Facility.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-facility/enter-facility-name.png"> -<img src="new-facility/enter-facility-name.png"/> </a> -</figure> -<p>Enter the details of the Health Facility and submit the form.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-facility/created-facility.png"> -<img src="new-facility/created-facility.png"/> </a> -</figure> -<p>You should see the newly created Health Facility appear on the left-hand side and when you select it, you will see details of the Health Facility appear on the right-hand side.</p> -<br clear="all"> -<hr> -<h3 id="2-create-chw-area-and-chw">2. Create CHW Area and CHW</h3> -<p>We will now create a Place and the primary contact for it within one form. We want to create a CHW Area within the Health Facility that we previously created.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-chw-area/new-chw-area.png"> -<img src="new-chw-area/new-chw-area.png"/> </a> -</figure> -<p>Select the <strong>Health Facility</strong> on the left-hand side. You will then select <strong>New Area</strong> on the right-hand side.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-chw-area/create-new-person.png"> -<img src="new-chw-area/create-new-person.png"/> </a> -</figure> -<p>Select the option that lets you create a new person within the form. This person will automatically become the primary contact for the created place.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-chw-area/fill-required-fields.png"> -<img src="new-chw-area/fill-required-fields.png"/> </a> -</figure> -<p>Fill in the required fields and go to the next section.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-chw-area/name-after-primary-contact.png"> -<img src="new-chw-area/name-after-primary-contact.png"/> </a> -</figure> -<p>You will get an option to name the Place after the created contact person or name it yourself. If you select <strong>Yes</strong>, the new place will be named <code>&lt;contact-name&gt;'s Area</code>. For example <code>Jane Doe's Area</code>.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-chw-area/created-chw-area.png"> -<img src="new-chw-area/created-chw-area.png"/> </a> -</figure> -<p>Once you submit, a new CHW Area will be created. On the right-hand you should see the CHW Area name, the primary contact of the CHW Area, and the Health Facility that the CHW Area belongs to.</p> -<br clear="all"> -<hr> -<h3 id="3-create-chw-supervisor">3. Create CHW Supervisor</h3> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-chw-supervisor/new-person.png"> -<img src="new-chw-supervisor/new-person.png"/> </a> -</figure> -<p>To create a primary contact for an existing Place (in this case, for the Health Facility that we created without a primary contact); select the Place and then select the <strong>New Person</strong> action.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-chw-supervisor/belongs-to.png"> -<img src="new-chw-supervisor/belongs-to.png"/> </a> -</figure> -<p>A <em>new person form</em> will appear with an option to change the Place the new person will belong to. A new contact will be created in the Health Facility when you submit this form.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-chw-supervisor/edit-facility.png"> -<img src="new-chw-supervisor/edit-facility.png"/> </a> -</figure> -<p>Finally, we will set the newly created person as a primary contact for the Health Facility they belong to. To do this, select the Health Facility and then select the <strong>Edit</strong> action.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-chw-supervisor/set-primary-contact.png"> -<img src="new-chw-supervisor/set-primary-contact.png"/> </a> -</figure> -<p>You should see an edit form from which you can set the primary contact of the Health Facility. Click <strong>Submit</strong> to apply the changes.</p> -<br clear="all"> -<hr> -<h3 id="4-create-the-chw-user">4. Create the CHW User</h3> -<p>You may want to log in as a CHW and perform some actions now that the CHW and CHW Supervisor contacts are created; let&rsquo;s create a CHW user who&rsquo;s linked to the CHW contact we created earlier.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-chw-user/app-settings.png"> -<img src="new-chw-user/app-settings.png"/> </a> -</figure> -<p>Go to the <strong>hamburger menu</strong> and select <strong>App Settings</strong>.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-chw-user/add-user.png"> -<img src="new-chw-user/add-user.png"/> </a> -</figure> -<p>When you are on the <strong>App Settings</strong> page, select <strong>Users</strong> on the left-hand side and then select <strong>Add User</strong> on the right-hand side.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-chw-user/fill-user-details.png"> -<img src="new-chw-user/fill-user-details.png"/> </a> -</figure> -<p>You should now see an <strong>Add User Form</strong>. Fill in the user name, then select the role as <strong>CHW</strong> or <strong>Regional Admin</strong>. In the <strong>Place</strong> field, select the name of the CHW Area whose CHW you want to create a user for (you can search by typing the first few letters of the CHW Area name). Once that is done, under the <strong>Associate Contact</strong> field select the name of the CHW whose user you are creating. Finally, input a password and hit <strong>Submit</strong>.</p> -<br clear="all"> -<p>Once this is done, you can logout and log into the app using the username and password that you just created.</p> -<h2 id="frequently-asked-questions">Frequently Asked Questions</h2> -<ul> -<li><a href="https://forum.communityhealthtoolkit.org/t/is-there-any-downside-of-creating-too-many-users/531">Is there any downside of creating too many users?</a></li> -<li><a href="https://forum.communityhealthtoolkit.org/t/for-offline-users-how-often-does-the-app-try-to-refresh-if-there-is-an-available-internet-connection/503">For offline users, how often does the app try to refresh if there is an available internet connection?</a></li> -<li><a href="https://forum.communityhealthtoolkit.org/t/can-one-person-belong-to-multiple-places-in-the-same-hierarchy/101">Can one person belong to multiple places in the same hierarchy?</a></li> -</ul>Apps: Contact and User Management - Part 2https://docs.communityhealthtoolkit.org/apps/tutorials/contact-and-users-2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/contact-and-users-2/ -<div class="pageinfo pageinfo-primary"> -<p>In this tutorial you will learn how to create and edit contacts and their associated users in the CHT application using cht-conf. If you haven&rsquo;t already, have a look at <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/contact-and-users-1/">part 1</a> of this tutorial for a useful overview of key concepts.</p> -</div> -<h2 id="brief-overview-of-key-concepts">Brief Overview of Key Concepts</h2> -<p><a href="https://github.com/medic/cht-conf"><em>cht-conf</em></a> is a command-line interface tool to manage and configure your apps built using the Core Framework of the Community Health Toolkit.</p> -<p>See more <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/contact-and-users-1/#brief-overview-of-key-concepts">key concepts</a> in part 1 of this tutorial.</p> -<h2 id="required-resources">Required Resources</h2> -<p>You should have a functioning CHT instance and have cht-conf installed locally. Read <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/">How to set up a CHT local configuration environment</a></p> -<h2 id="implementation-steps">Implementation Steps</h2> -<p>In these steps you are going to create a Health Facility, CHW Areas, primary contacts for the CHW Areas, and their associated users.</p> -<h3 id="1-create-health-facilities-using-cht-confs-csv-to-docs-and-upload-docs-features">1. Create Health Facilities (using cht-conf&rsquo;s csv-to-docs and upload-docs features)</h3> -<p>To create contacts and their associated users with cht-conf, you will need to create a CSV file with the information of the contacts and the users that you would like to create. The name of the file determines the type of doc created for rows contained in the file.</p> -<p>For example, file named <code>place.district_hospital.csv</code> adds the property <code>&quot;type&quot;:&quot;district_hospital&quot;</code> and a file named <code>person.clinic.csv</code> add the property <code>&quot;type&quot;:&quot;person&quot;</code></p> -<p>Create a CSV file named <code>place.district_hospital.csv</code> and add the details of the Health Facilities you would like to create.</p> -<table> -<thead> -<tr> -<th>name</th> -</tr> -</thead> -<tbody> -<tr> -<td>Nairobi South Facility</td> -</tr> -<tr> -<td>Nairobi West Facility</td> -</tr> -<tr> -<td>Nairobi East Facility</td> -</tr> -</tbody> -</table> -<p>Save this file to a folder name <code>csv</code> in your project&rsquo;s base directory.</p> -<p>Open terminal or command line. <code>cd</code> to your project&rsquo;s base directory and then run the command</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>cht csv-to-docs -</span></span></code></pre></div><p>This will convert rows of the CSV files from the <code>csv</code> folder to JSON docs that are stored in a <code>json-docs</code> folder.</p> -<p>To upload the JSON docs to your local test instance, run the command</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>cht --url<span style="color:#ce5c00;font-weight:bold">=</span>https://&lt;username&gt;:&lt;password&gt;@localhost --accept-self-signed-certs upload-docs -</span></span></code></pre></div><p>Be sure to replace the values <code>&lt;username&gt;</code> and <code>&lt;password&gt;</code> with the actual username and password of your test instance.</p> -<br clear="all"> -<hr> -<h3 id="2-create-chw-areas-chw-contacts-and-users-using-cht-confs-create-users-feature">2. Create CHW Areas, CHW Contacts and Users (using cht-conf&rsquo;s create-users feature)</h3> -<p>Next you are going to create CHW Areas for the Health Facilities you created in the step above along with the CHW contacts and users for these CHW Areas.</p> -<p>Create a CSV file named <code>users.csv</code> and add the details of the Users, CHW contacts, and CHW Areas you would like to create. Save this file in the base project directory.</p> -<table> -<thead> -<tr> -<th>username</th> -<th>password</th> -<th>roles</th> -<th>fullname</th> -<th>phone</th> -<th>contact.name</th> -<th>contact.phone</th> -<th>contact.sex</th> -<th>contact.age</th> -<th>place.type</th> -<th>place.name</th> -<th>place.parent</th> -</tr> -</thead> -<tbody> -<tr> -<td>mmutiso</td> -<td>q3Z5-vH5</td> -<td>district_admin</td> -<td>Mary Mutiso</td> -<td>0712345678</td> -<td>Mary Mutiso</td> -<td>0712345678</td> -<td>Female</td> -<td>36</td> -<td>health_center</td> -<td>Mary Mutiso&rsquo;s Area</td> -<td><code>&lt;facility uuid&gt;</code></td> -</tr> -</tbody> -</table> -<p><br clear="all"></p> -<figure class="right col-6 col-lg-8"><a href="facility-uuid.png"> -<img src="facility-uuid.png"/> </a> -</figure> -<p>The value <code>place.parent</code> is the uuid of the Facility to which the CHW Area belongs to. You can get this value by selecting the Health Facility in the webapp and copying the last portion of the url.</p> -<br clear="all"> -<p>Run the command</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>cht --url<span style="color:#ce5c00;font-weight:bold">=</span>https://&lt;username&gt;:&lt;password&gt;@localhost --accept-self-signed-certs create-users -</span></span></code></pre></div><p>This will create the CHW Area, the CHW contact, and the user that the CHW will use to log into the application.</p> -<h2 id="frequently-asked-questions">Frequently Asked Questions</h2> -<ul> -<li><a href="https://forum.communityhealthtoolkit.org/t/can-one-person-belong-to-multiple-places-in-the-same-hierarchy/101">Can one person belong to multiple places in the same hierarchy?</a></li> -</ul>Apps: Building SMS Formshttps://docs.communityhealthtoolkit.org/apps/tutorials/sms-forms/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/sms-forms/ -<p>SMS forms allow users to submit reports from any device including <a href="https://en.wikipedia.org/wiki/Feature_phone">feature phones</a> without internet access. SMS forms are ideal in scenarios where targeted users have no way of accessing internet or where they are restricted to using feature phones.</p> -<div class="pageinfo pageinfo-primary"> -<p>This tutorial will take you through how to build SMS forms for CHT applications, including:</p> -<ul> -<li>Defining SMS forms</li> -<li>Setting validation rules for SMS forms</li> -<li>Setting automatic responses to SMS reports</li> -</ul> -<p>You will be building a pregnancy registration workflow that allows Community Health Workers to register households, register household members, and register new pregnancies for the household members.</p> -</div> -<h2 id="brief-overview-of-key-concepts">Brief Overview of Key Concepts</h2> -<p><em><a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/forms/">SMS forms</a></em> are structured text messages that contain a form code representing a report type and some information associated with the report.</p> -<p>SMS forms are defined in either the <code>base_settings.json</code> or the <code>app_settings/forms.json</code> file and compiled into the <em><a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/">app_settings.json</a></em> file with the <code>compile-app-settings</code> action in the <code>cht-conf</code> tool, then stored in the settings doc in the database.</p> -<p><em><a href="https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/">SMS gateways</a></em> allow the CHT core framework to send and receive SMS transmission to or from a mobile network operator.</p> -<p><em>SMS aggregators</em> act as intermediaries between the mobile network operators and the CHT core framework. They allow for greater customization of SMS workflows in the CHT.</p> -<p><em>Interoperability:</em> CHT apps can receive data from other software when it is submitted as reports using the same JSON/SMS form notation.</p> -<h2 id="required-resources">Required Resources</h2> -<p>You should have a <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/">functioning CHT instance with <code>cht-conf</code> installed locally</a>.</p> -<p>You also need to have some <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/application-settings/">prior knowledge on <code>app_settings.json</code></a>.</p> -<h2 id="implementation-steps">Implementation Steps</h2> -<p>SMS forms are defined <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/forms/#app_settingsjson-forms">using JSON</a> in the <code>app_settings.json</code> file.</p> -<h3 id="1-enable-transitions">1. Enable Transitions</h3> -<p>To ensure SMS forms function as expected, you will first need to enable the following transitions in <code>app_settings.json</code>.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;update_clinics&#34;</span><span style="color:#a40000">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#a40000">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;registration&#34;</span><span style="color:#a40000">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#a40000">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;accept_patient_reports&#34;</span><span style="color:#a40000">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#a40000">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;generate_shortcode_on_contacts&#34;</span><span style="color:#a40000">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#a40000">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;death_reporting&#34;</span><span style="color:#a40000">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span></code></pre></div> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/transitions/">Transitions</a></p> -<h3 id="2-define-a-place-registration-form">2. Define a Place Registration Form</h3> -<p>To define a form, edit the object corresponding to the <code>forms</code> key in <code>app_settings.json</code>.</p> -<p>Add a household registration form by adding a key and object pair to the <code>forms</code> object as shown below.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#4e9a06">&#34;forms&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;HR&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;meta&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;code&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;HR&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;label&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;en&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;New Household Registration&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;fields&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;place_name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;labels&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;tiny&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;en&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;household_name&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;description&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;en&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Name of Household&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;short&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;en&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Household name&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;position&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;string&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;length&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#0000cf;font-weight:bold">3</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#0000cf;font-weight:bold">30</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;required&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Users will register new households by sending a text message in the format <code>HR &lt;household name&gt;</code>. For example, <code>HR Mary</code> will register <code>Mary&rsquo;s Household</code>. -</div> -<p>To set the validation rules and autoresponses, edit the array corresponding to the <code>registration</code> key in <code>app_settings.json</code>. Add an object within the array as shown below.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#4e9a06">&#34;registrations&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;HR&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;events&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;on_create&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;trigger&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;add_place&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;params&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;{ \&#34;contact_type\&#34;: \&#34;clinic\&#34; }&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;bool_expr&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;validations&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;join_responses&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;list&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;property&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;place_name&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rule&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;!regex(&#39;household&#39;) &amp;&amp; !regex(&#39;house hold&#39;)&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;messages.hr.validation.no_household_in_name&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;property&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;place_name&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rule&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;lenMin(1) &amp;&amp; lenMax(30)&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;messages.hr.validation.household_name_length&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;messages&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;event_type&#34;</span><span style="color:#000;font-weight:bold">:</span><span style="color:#4e9a06">&#34;report_accepted&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;recipient&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;reporting_unit&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;messages.hr.report_accepted&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">]</span> -</span></span></code></pre></div><p>You can also define your own <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/#validations">validation rules</a>.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -<code>translation_key</code> represents the message that is sent out. This will be defined in a <a href="https://docs.communityhealthtoolkit.org/apps/reference/translations/">translations</a> file. -</div> -<h3 id="3-define-a-person-registration-form">3. Define a Person Registration Form</h3> -<p>Add a person registration form by adding a key and object pair to the <code>forms</code> object as shown below.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#4e9a06">&#34;forms&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;N&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;meta&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;code&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;N&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;label&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;en&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;New Person Registration&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;fields&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;place_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;labels&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;tiny&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;en&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;place_id&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;description&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;en&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Household ID&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;short&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;en&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;HH ID&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;position&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;string&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;length&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#0000cf;font-weight:bold">13</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;required&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;gender&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;labels&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;tiny&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;en&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;gender&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;description&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;en&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Gender&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;short&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;en&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Gender&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;position&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;string&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;length&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#0000cf;font-weight:bold">6</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;required&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;age&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;labels&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;tiny&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;en&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;age&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;description&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;en&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Age&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;short&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;en&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Age&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;position&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;integer&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;length&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#0000cf;font-weight:bold">2</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;required&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;patient_name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;labels&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;tiny&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;en&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;patient_name&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;description&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;en&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Patient name&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;short&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;en&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Patient name&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;position&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">3</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;string&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;length&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#0000cf;font-weight:bold">3</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#0000cf;font-weight:bold">30</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;required&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Users will register a new person by sending a text message in the format <code>N &lt;household id&gt; &lt;gender&gt; &lt;age&gt; &lt;name&gt;</code>. For example, <code>N 12345 F 20 Jane Cho</code> will register <code>Jane Cho</code> who belongs to the household with household ID <code>12345</code>. -</div> -<p>To set the validation rules and autoresponses, edit the array corresponding to the <code>registration</code> key in <code>app_settings.json</code>. Add an object within the array as shown below.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#4e9a06">&#34;registrations&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;N&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;events&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;on_create&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;trigger&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;add_patient&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;params&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;{ \&#34;parent_id\&#34;: \&#34;place_id\&#34; }&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;bool_expr&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;validations&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;join_responses&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;list&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;property&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;place_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rule&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;regex(&#39;^[0-9]{5,13}$&#39;)&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;messages.validation.household_id&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;property&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;gender&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rule&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;iEquals(&#39;m&#39;) || iEquals(&#39;f&#39;)&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;messages.n.validation.gender&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;property&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;age&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rule&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;between(5,130)&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;messages.n.validation.age&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;messages&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;event&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;report_accepted&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;messages.n.report_accepted&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;recipient&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;reporting_unit&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">]</span> -</span></span></code></pre></div><h3 id="4-define-a-report-submission-form">4. Define a Report Submission form</h3> -<p>Add a report submission form by adding a key and object pair to the <code>forms</code> object as shown below.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#4e9a06">&#34;forms&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;P&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;meta&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;code&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;P&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;label&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;en&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Pregnancy Registration&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;fields&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;patient_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;position&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;labels&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;tiny&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;en&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;patient_id&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;short&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;en&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Woman&#39;s ID&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;description&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;en&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Woman&#39;s ID&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;string&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;length&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#0000cf;font-weight:bold">5</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#0000cf;font-weight:bold">13</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;required&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;lmp&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;position&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;labels&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;tiny&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;en&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;lmp&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;short&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;en&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;LMP in weeks&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;description&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;en&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Last menstrual period - in weeks&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;integer&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;length&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#0000cf;font-weight:bold">2</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;required&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Users will pregnancy registration report by sending a text message in the format <code>P &lt;patient id&gt; &lt;last menstrual period&gt;</code>. For example, <code>P 23456 21</code> will register a pregnancy report for a person whose unique ID is <code>23456</code> and the last menstrual period was <code>21 weeks ago</code>. -</div> -<p>To set the validation rules and autoresponses, edit the array corresponding to the <code>registration</code> key in <code>app_settings.json</code>. Add an object within the array as shown below.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#4e9a06">&#34;registrations&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;P&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;events&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;on_create&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;trigger&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;add_expected_date&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;params&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;lmp_date&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;bool_expr&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;doc.fields.lmp &amp;&amp; /^[0-9]+$/.test(doc.fields.lmp)&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;validations&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;join_responses&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;list&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;property&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;patient_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rule&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;regex(&#39;^[0-9]{5,13}$&#39;)&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;messages.validation.patient_id&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;property&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;lmp&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rule&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;lenMin(1) ? (integer &amp;&amp; between(4,42)) : optional&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;messages.p.validation.weeks_since_last_lmp&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;messages&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;event_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;report_accepted&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;messages.p.report_accepted&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;recipient&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;reporting_unit&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;event_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;registration_not_found&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;messages.validation.woman_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;recipient&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;reporting_unit&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">]</span> -</span></span></code></pre></div><h3 id="5-upload-app-settings">5. Upload App Settings</h3> -<p>To upload app settings to your local instance, run the following command:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>cht --url<span style="color:#ce5c00;font-weight:bold">=</span>https://&lt;username&gt;:&lt;password&gt;@localhost --accept-self-signed-certs upload-app-settings -</span></span></code></pre></div> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Be sure to replace the values <code>&lt;username&gt;</code> and <code>&lt;password&gt;</code> with the actual username and password of your test instance. -</div> -<h2 id="next-steps">Next steps</h2> -<p>In the next tutorial, you will define scheduled messages that can be triggered or cleared by submitting SMS forms.</p>Apps: CHT Application Settingshttps://docs.communityhealthtoolkit.org/apps/tutorials/application-settings/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/application-settings/ -<div class="pageinfo pageinfo-primary"> -<p>This tutorial will take you through how to manage the CHT application settings, including;</p> -<ul> -<li>Setting user roles and permissions</li> -<li>Enabling and disabling transitions</li> -<li>Configuring contact hierarchy and configuring replication.</li> -</ul> -<p>App settings allow you to both persist information that is critical to the application outside the code, and to create profiles that store the preferences for project deployments.</p> -</div> -<h2 id="brief-overview-of-key-concepts">Brief Overview of Key Concepts</h2> -<p>The settings which control CHT apps are defined in the <em><a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/">app_settings.json</a></em> file, and stored in the settings doc in the database.</p> -<p><em><a href="https://docs.communityhealthtoolkit.org/apps/concepts/users/#permissions">Permissions</a></em> are settings that control access to specific app features and functionality.</p> -<p><em><a href="https://docs.communityhealthtoolkit.org/apps/concepts/users/#roles">Roles</a></em> define permissions for users to access a group of app features and functionality.</p> -<p><em><a href="https://docs.communityhealthtoolkit.org/apps/guides/performance/replication/">Replication</a></em> is when users download a copy of the data on to their device. <em>Replication depth</em> refers to the number of levels within a hierarchy a specific user role is able to replicate.</p> -<p><em><a href="https://docs.communityhealthtoolkit.org/core/overview/transitions/">Transitions</a></em> are Javascript code that run when a document is changed. A transition can edit the changed doc or do anything server side code can do for that matter.</p> -<h2 id="required-resources">Required Resources</h2> -<p>You should have a functioning CHT instance and have cht-conf installed locally. -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/">How to set up a CHT local configuration environment</a></p> -</p> -<h2 id="implementation-steps">Implementation Steps</h2> -<p>In this section, you will define a new role, set permissions for the role, set transitions, configure a hierarchy, and upload your modified app settings file to your local environment.</p> -<h3 id="1-set-roles-and-permissions">1. Set Roles and Permissions</h3> -<p>To add a new role, edit the object corresponding to <code>&quot;roles&quot;</code> key in <code>app_settings.json</code>. Add the new role as a key and within it, have an object with key/value pairs indicating the translation key of the role and whether it is an online or offline role.</p> -<p>Configure a CHW role by adding the following snippet to the <code>&quot;roles&quot;</code> object:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;chw&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;usertype.chw&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;offline&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-roles/">Roles</a></p> -<p>Set permissions for the new role by adding the role to the relevant permission in the <code>&quot;permissions&quot;</code> object.</p> -<p>For instance, to grant the CHW role permission to create people, add the role to the array with the key <code>&quot;can_create_people&quot;</code>.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;permissions&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#a40000">...</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;can_create_people&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;program_officer&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;chw_supervisor&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;chw&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#a40000">...</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-permissions/">Permissions</a></p> -<h3 id="2-set-transitions">2. Set Transitions</h3> -<p>To enable or disable a transition, edit the object corresponding to the <code>&quot;transitions&quot;</code> key in <code>app_settings.json</code>. Enable the <code>transition</code> by setting its corresponding value to <code>true</code>, disable it by settings its value to <code>false</code>. -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/transitions/">Transitions</a></p> -</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;transitions&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;accept_patient_reports&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;conditional_alerts&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;default_responses&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;update_sent_by&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;registration&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;update_clinics&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;update_notifications&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;update_scheduled_reports&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;update_sent_forms&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;generate_patient_id_on_people&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;death_reporting&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>In this example, the <code>generate_patient_id_on_people</code> and <code>death_reporting</code> transitions are enabled while the rest are disabled.</p> -<h3 id="3-set-hierarchy">3. Set Hierarchy</h3> -<p>You can configure hierarchies by editing the object corresponding to the <code>&quot;contact_types&quot;</code> key in <code>app_settings.json</code>. The following code sample represents the default hierarchy configuration. You can modify existing contact types by editing the objects within the array.</p> -<p>When configuring a new deployment, it&rsquo;s important to plan your hierarchy well. While there are no limits in the CHT to the hierarchical depth or breadth it will support, there are some trade-offs and caveats:</p> -<ul> -<li>While it is possible to <a href="https://docs.communityhealthtoolkit.org/apps/guides/updates/moving-contacts/">update the hierarchy configuration</a> after launch it can be difficult and potentially disruptive to users. Try and avoid this by planning ahead as best as possible.</li> -<li>If your hierarchy is too shallow, users will download more docs than are necessary for their work which will impact the performance of the app. Taking the time to get the hierarchy configuration right makes it easy to give users access to only the docs they need.</li> -</ul> -<p><strong>Important Note on Creating and Editing Contacts:</strong> The ability to create and edit contacts in the CHT, including person entities, is dependent on the presence of corresponding forms in the <code>forms/contact</code> directory. Each contact type, such as &ldquo;person&rdquo;, &ldquo;clinic&rdquo;, &ldquo;health_center&rdquo;, etc., must have associated <code>create</code> and <code>edit</code> forms defined. Without these forms, the functionality to add or modify these contact types in the CHT application will not be available. Ensure that the necessary forms are created and correctly referenced in the <code>app_settings.json</code> file under their respective contact type definitions. For guidance on creating these contact forms, please refer to the <a href="https://docs.communityhealthtoolkit.org/apps/reference/forms/contact/">CHT documentation on contact forms</a>. -For an example of where all the forms are represented in the default configuration, please see the <a href="https://github.com/medic/cht-core/tree/master/config/default/">default config directory</a>.</p> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/hierarchy/">Hierarchy</a></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;contact_types&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;district_hospital&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.district_hospital&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;group_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.district_hospital.plural&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;create_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.district_hospital.new&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;edit_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.place.edit&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;icon&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;medic-district-hospital&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;create_form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;form:contact:district_hospital:create&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;edit_form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;form:contact:district_hospital:edit&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;health_center&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.health_center&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;group_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.health_center.plural&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;create_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.health_center.new&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;edit_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.place.edit&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parents&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;district_hospital&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;icon&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;medic-health-center&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;create_form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;form:contact:health_center:create&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;edit_form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;form:contact:health_center:edit&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;clinic&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.clinic&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;group_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.clinic.plural&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;create_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.clinic.new&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;edit_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.place.edit&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parents&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;health_center&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;icon&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;medic-clinic&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;create_form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;form:contact:clinic:create&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;edit_form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;form:contact:clinic:edit&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;count_visits&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;person&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.person&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;group_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.person.plural&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;create_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.person.new&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;edit_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.person.edit&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;primary_contact_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;clinic.field.contact&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parents&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;district_hospital&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;health_center&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;clinic&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;icon&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;medic-person&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;create_form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;form:contact:person:create&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;edit_form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;form:contact:person:edit&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;person&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span></code></pre></div><h3 id="4-set-replication-depth">4. Set Replication Depth</h3> -<p>To configure replication depth for a specific role, edit the object corresponding to the <code>&quot;replication_depth&quot;</code> key in <code>app_settings.json</code>.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;replication_depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">&#34;role&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;district_manager&#34;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">&#34;depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">&#34;role&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;national_manager&#34;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">&#34;depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">2</span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>Configure the CHW role&rsquo;s depth to 2 by adding the following key/value pairs to the array above.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">&#34;role&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;chw&#34;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">&#34;depth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">2</span> <span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/guides/performance/replication/">Replication Depth</a></p> -<h3 id="5-upload-app-settings">5. Upload App Settings</h3> -<p>To upload app settings to your local instance, run the following command:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>cht --url<span style="color:#ce5c00;font-weight:bold">=</span>https://&lt;username&gt;:&lt;password&gt;@localhost --accept-self-signed-certs upload-app-settings -</span></span></code></pre></div> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Be sure to replace the values <code>&lt;username&gt;</code> and <code>&lt;password&gt;</code> with the actual username and password of your test instance. -</div> -<h2 id="frequently-asked-questions">Frequently Asked Questions</h2> -<ul> -<li><a href="https://forum.communityhealthtoolkit.org/t/default-config-do-not-compile/536">Compiling default app settings</a></li> -<li><a href="https://forum.communityhealthtoolkit.org/t/documentation-for-role-permissions/502">Documentation for role permissions</a></li> -<li><a href="https://forum.communityhealthtoolkit.org/t/is-it-possible-to-prevent-editing-for-some-forms-but-allow-it-for-others/93">Is it possible to prevent editing for some forms but allow it for others?</a></li> -<li><a href="https://forum.communityhealthtoolkit.org/t/can-one-person-belong-to-multiple-places-in-the-same-hierarchy/101/2">Can one person belong to multiple places in the same hierarchy?</a></li> -</ul>Apps: Setting up Multi-facility usershttps://docs.communityhealthtoolkit.org/apps/tutorials/multi-facility-users/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/multi-facility-users/ -<div class="pageinfo pageinfo-primary"> -<p>This tutorial will take you through how to create and assign users to multiple places in the CHT UI. Assigning users to multiple places is only available from <strong>CHT 4.9.0</strong>.</p> -<p>This tutorial covers;</p> -<ul> -<li>Creating contacts and their associated users</li> -<li>Creating places and assign contacts to those places</li> -<li>Assigning users to multiple places</li> -</ul> -<p>The <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/application-settings/">CHT application settings</a> allows you to both persist information that is critical to the application outside the code, and to create profiles that store the preferences for project deployments.</p> -</div> -<h2 id="brief-overview-of-key-concepts">Brief Overview of Key Concepts</h2> -<p><em><a href="https://docs.communityhealthtoolkit.org/apps/concepts/users/#permissions">Permissions</a></em> are settings that control access to specific app features and functionality.</p> -<p><em><a href="https://docs.communityhealthtoolkit.org/apps/concepts/users/#roles">Roles</a></em> define permissions for users to access a group of app features and functionality.</p> -<p><em><a href="https://docs.communityhealthtoolkit.org/apps/features/contacts/">Contacts</a></em> are people or places that are created in the CHT application.</p> -<p><em><a href="https://docs.communityhealthtoolkit.org/apps/features/contacts/">People</a></em> are both patients in the system and users of the system, such as CHWs or Nurses.</p> -<p><em><a href="https://docs.communityhealthtoolkit.org/apps/features/contacts/">Places</a></em> represent either an actual physical location such as a health facility, clinic, or a grouping such as a household or CHW Area.</p> -<h2 id="pre-requisites">Pre-requisites</h2> -<ul> -<li>You should be familiar with contact and user management. View detailed tutorial on <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/contact-and-users-1/">How to create contacts and their associated users</a></li> -<li>You should be familiar with managing CHT application settings. Read <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/application-settings/">How to manage CHT application settings</a></li> -</ul> -<h2 id="implementation-steps">Implementation Steps</h2> -<p>In this tutorial, you will work with the default contact forms and the default hierarchy.</p> -<p>The default <code>app_settings.json</code> in the CHT found in the <code>/config/default</code> folder has an existing <code>chw_supervisor</code> role and the <code>&quot;can_have_multiple_places&quot;: []</code> permission.</p> -<p>While logged in as an admin user, you will create two Health Facilities, CHW Supervisor, and CHW Area. You will then create the user for the CHW Supervisor so that they can log in and see the facilities, and the CHW Areas they supervise.</p> -<h3 id="creating-multi-facility-user-using-the-cht-default-config">Creating multi-facility user using the CHT default config.</h3> -<h4 id="1-create-new-health-facility">1. Create New Health Facility.</h4> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-facility/select-new-facility.png"> -<img src="new-facility/select-new-facility.png"/> </a> -</figure> -<p>While logged into the CHT application, go to the <strong>People tab</strong> and select <strong>New Health Facility</strong></p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-facility/select-primary-contact.png"> -<img src="new-facility/select-primary-contact.png"/> </a> -</figure> -<p>Select the option that lets you create a new person within the form. This person will automatically become the primary contact for the created place.</p> -<br clear="all"> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-facility/select-role.png"> -<img src="new-facility/select-role.png"/> </a> -</figure> -<p>When filling in the required fields select <strong>CHW Supervisor</strong> as the role.</p> -<p>Once you have filled the required fields, click <strong>Next</strong> to go to the next section.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-facility/enter-facility-name.png"> -<img src="new-facility/enter-facility-name.png"/> </a> -</figure> -<p>Select the option that lets you name the facility manually. Enter the <strong>Name</strong> of the Health Facility and submit the form.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-facility/created-facility.png"> -<img src="new-facility/created-facility.png"/> </a> -</figure> -<p>You should see the newly created Health Facility appear on the left-hand side and when you select it, you should see the Health Facility name and the primary contact of the Health Facility.</p> -<br clear="all"> -<hr> -<h3 id="2-create-chw-area-and-chw">2. Create CHW Area and CHW</h3> -<p>We will now create a CHW Area within the Health Facility that we previously created.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-chw-area/new-chw-area.png"> -<img src="new-chw-area/new-chw-area.png"/> </a> -</figure> -<p>Select the <strong>Health Facility</strong> on the left-hand side. Click on the <strong>Blue Action Button</strong> and select <strong>New Area</strong>.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-chw-area/create-new-person.png"> -<img src="new-chw-area/create-new-person.png"/> </a> -</figure> -<p>Select the option that lets you create a new person within the form. This person will automatically become the primary contact for the created place.</p> -<p>When filling in the required fields select <strong>CHW</strong> as the role.</p> -<p>Once you have filled the required fields, click <strong>Next</strong> to go to the next section.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-chw-area/name-after-primary-contact.png"> -<img src="new-chw-area/name-after-primary-contact.png"/> </a> -</figure> -<p>You will get an option to name the Place after the created contact person or name it yourself. If you select <strong>Yes</strong>, the new place will be named <code>&lt;contact-name&gt;'s Area</code>. For example <code>Jane Doe's Area</code>.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-chw-area/created-chw-area.png"> -<img src="new-chw-area/created-chw-area.png"/> </a> -</figure> -<p>Once you submit, a new CHW Area will be created. On the right-hand you should see the CHW Area name, the primary contact of the CHW Area, and the Health Facility that the CHW Area belongs to.</p> -<br clear="all"> -<hr> -<h4 id="3-create-second-health-facility">3. Create Second Health Facility.</h4> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-facility/skip-primary-contact.png"> -<img src="new-facility/skip-primary-contact.png"/> </a> -</figure> -<p>For the second Health Facility, we will skip creating or assigning a primary contact and focus on creating the second Health Facility.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-facility/enter-second-facility-name.png"> -<img src="new-facility/enter-second-facility-name.png"/> </a> -</figure> -<p>Enter the details of the Health Facility and submit the form.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-facility/multiple-facilities.png"> -<img src="new-facility/multiple-facilities.png"/> </a> -</figure> -<p>You should see the two Health Facilities appear on the left-hand side.</p> -<br clear="all"> -<h3 id="4-create-the-chw-supervisor-user">4. Create the CHW Supervisor User</h3> -<p>Let&rsquo;s create a CHW Supervisor user who&rsquo;s linked to the CHW Supervisor contact we created earlier.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-chw-supervisor/app-settings.png"> -<img src="new-chw-supervisor/app-settings.png"/> </a> -</figure> -<p>Go to the <strong>hamburger menu</strong> and select <strong>App Management</strong>.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-chw-supervisor/add-user.png"> -<img src="new-chw-supervisor/add-user.png"/> </a> -</figure> -<p>When you are on the <strong>App Management</strong> page, select <strong>Users</strong> on the left-hand side and then select <strong>Add User</strong> on the right-hand side.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-chw-supervisor/fill-user-details.png"> -<img src="new-chw-supervisor/fill-user-details.png"/> </a> -</figure> -<p>You should now see an <strong>Add User Form</strong>. Fill in the user name, then select the role as <strong>CHW Supervisor</strong>.</p> -<p>In the <strong>Place</strong> field, search for the first Health Facility by typing the first few letters of the Health Facility name. Once you have selected the first Health Facility, type the name of the Second Facility and select it as well.</p> -<p>Once that is done, under the <strong>Associate Contact</strong> field select the name of the CHW Supervisor whose user you are creating. Finally, input a password and hit <strong>Submit</strong>.</p> -<br clear="all"> -<p>Once this is done, you have created a supervisor who is assigned to multiple Health Facilities. You can logout and log into the app using the <code>username</code> and <code>password</code> that you just created.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -<ul> -<li>The new multi-facility feature be applied to any <code>role</code> in a hierarchy. It can also be applied to multiple roles in a hierarchy. You need to add the role to the array with the key <code>&quot;can_have_multiple_places&quot;</code>.</li> -</ul> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;permissions&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#a40000">...</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;can_have_multiple_places&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;program_officer&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;chw_supervisor&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#a40000">...</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><ul> -<li>The multi-facility feature supports a Contact belonging to Multiple Places. For instance, it is meant to support a CHW Supervisor belonging to multiple facilities. It is not meant to support One Facility having multiple CHW Supervisors.</li> -</ul> -</div> -<h2 id="frequently-asked-questions">Frequently Asked Questions</h2> -<ul> -<li><a href="https://forum.communityhealthtoolkit.org/t/default-config-do-not-compile/536">Compiling default app settings</a></li> -<li><a href="https://forum.communityhealthtoolkit.org/t/documentation-for-role-permissions/502">Documentation for role permissions</a></li> -<li><a href="https://forum.communityhealthtoolkit.org/t/support-for-supervisors-who-need-to-manage-multiple-areas/3497">Where can I get more information about this feature</a></li> -</ul>Apps: Building SMS Scheduleshttps://docs.communityhealthtoolkit.org/apps/tutorials/sms-schedules/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/sms-schedules/ -<p>SMS schedules allow you to send reminder messages at predetermined times. These reminders serve as useful prompts for end-users to take specific actions.</p> -<div class="pageinfo pageinfo-primary"> -<p>This tutorial takes you through how to set up SMS schedules for CHT applications. It uses a pregnancy registration workflow and follow-up reminders for a Community Health Worker as an example. The same methodology can be applied to other workflows and reminders as needed.</p> -</div> -<h2 id="brief-overview-of-key-concepts">Brief Overview of Key Concepts</h2> -<p><em><a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/schedules/">SMS schedules</a></em> are a series of SMS messages that are to be sent to specific contacts at future dates and times. They are defined in either the <code>base_settings.json</code> or the <code>app_settings/schedules.json</code> file and compiled into the <em><a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/">app_settings.json</a></em> file with the <code>compile-app-settings</code> action in the <code>cht-conf</code> tool.</p> -<p>SMS schedules can be triggered by <em><a href="https://docs.communityhealthtoolkit.org/apps/tutorials/sms-forms/">SMS forms</a></em> or <em><a href="https://docs.communityhealthtoolkit.org/apps/reference/forms/app/">App forms</a></em>.</p> -<h2 id="required-resources">Required Resources</h2> -<p>You should have <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/sms-forms/#4-define-a-report-submission-form">built a pregnancy SMS form</a>.</p> -<h2 id="implementation-steps">Implementation Steps</h2> -<p>SMS schedules are defined <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/schedules/#app_settingsjson-schedules">using JSON</a> in the <code>app_settings.json</code> file.</p> -<h3 id="1-define-a-pregnancy-follow-up-schedule">1. Define a Pregnancy Follow Up Schedule</h3> -<p>To set the pregnancy follow up schedule, edit the array corresponding to the <code>schedules</code> key in <code>app_settings.json</code>. Add an object within the array as shown. This will set a schedule of reminders that will be sent after a time interval (<code>offset</code>) relative to a base date (<code>start_from</code>).</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#a40000">schedules:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Pregnancy Follow Up Reminders&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;summary&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;description&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;start_from&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;fields.lmp_date&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;messages&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;message&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;content&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Hello {{contact.name}}, please remind {{patient_name}} ({{patient_id}}) to go for her clinic visit this week.&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;locale&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;en&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;group&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;offset&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;23 days&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;send_day&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;send_time&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;09:00&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;recipient&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;reporting_unit&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;message&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;content&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Hello {{contact.name}}, please remind {{patient_name}} ({{patient_id}}) to go for her clinic visit this week.&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;locale&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;en&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;group&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;offset&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;51 days&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;send_day&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;send_time&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;09:00&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;recipient&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;reporting_unit&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;message&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;content&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Hello {{contact.name}}, please remind {{patient_name}} ({{patient_id}}) to go for her clinic visit this week.&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;locale&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;en&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;group&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">3</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;offset&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;79 days&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;send_day&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;send_time&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;09:00&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;recipient&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;reporting_unit&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;message&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;content&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Hello {{contact.name}}, please remind {{patient_name}} ({{patient_id}}) to go for her clinic visit this week.&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;locale&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;en&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;group&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;offset&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;107 days&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;send_day&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;send_time&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;09:00&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;recipient&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;reporting_unit&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">]</span> -</span></span></code></pre></div> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/schedules/">Schedule properties</a></p> -<h3 id="2-assign-the-schedule">2. Assign the Schedule</h3> -<p>In the <code>registrations</code> array where you have defined the pregnancy registration events, add an event to assign the <code>Pregnancy Follow Up Reminders</code> schedule. This will assign a new schedule for every pregnancy that is registered.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;on_create&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;trigger&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;assign_schedule&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;params&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Pregnancy Follow Up Reminders&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;bool_expr&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;doc.fields.lmp &amp;&amp; /^[0-9]+$/.test(doc.fields.lmp)&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>The final result should look like this:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#4e9a06">&#34;registrations&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;P&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;events&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;on_create&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;trigger&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;add_expected_date&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;params&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;lmp_date&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;bool_expr&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;doc.fields.lmp &amp;&amp; /^[0-9]+$/.test(doc.fields.lmp)&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;on_create&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;trigger&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;assign_schedule&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;params&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Pregnancy Follow Up Reminders&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;bool_expr&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;doc.fields.lmp &amp;&amp; /^[0-9]+$/.test(doc.fields.lmp)&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;validations&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;join_responses&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;list&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;property&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;patient_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rule&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;regex(&#39;^[0-9]{5,13}$&#39;)&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;messages.validation.patient_id&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;property&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;lmp&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;rule&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;lenMin(1) ? (integer &amp;&amp; between(4,42)) : optional&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;messages.p.validation.weeks_since_last_lmp&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;messages&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;event_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;report_accepted&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;messages.p.report_accepted&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;recipient&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;reporting_unit&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;event_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;registration_not_found&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;translation_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;messages.validation.woman_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;recipient&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;reporting_unit&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">]</span> -</span></span></code></pre></div><h3 id="3-upload-app-settings">3. Upload App Settings</h3> -<p>To upload app settings to your local instance, run the following command:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>cht --url<span style="color:#ce5c00;font-weight:bold">=</span>https://&lt;username&gt;:&lt;password&gt;@localhost --accept-self-signed-certs upload-app-settings -</span></span></code></pre></div> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Be sure to replace the values <code>&lt;username&gt;</code> and <code>&lt;password&gt;</code> with the actual username and password of your test instance. -</div> -<h2 id="frequenty-asked-questions">Frequenty Asked Questions</h2> -<ul> -<li><a href="https://forum.communityhealthtoolkit.org/t/can-you-schedule-sms-based-on-date-field-in-report/87">Can you schedule SMS based on date field in report?</a></li> -<li><a href="https://forum.communityhealthtoolkit.org/t/can-sms-schedules-be-cleared-based-on-a-field-in-a-form/651">Can SMS schedules be cleared based on a field in a form?</a></li> -</ul>Apps: Building App Formshttps://docs.communityhealthtoolkit.org/apps/tutorials/app-forms/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/app-forms/ -<p>App forms allow users to submit reports from Android devices</p> -<div class="pageinfo pageinfo-primary"> -<p>This tutorial will take you through how to build App forms for CHT applications, including:</p> -<ul> -<li>Authoring forms in Excel, Google sheets or other spreadsheet applications.</li> -<li>Converting XLSForms to XForms</li> -<li>Uploading XForms to CHT</li> -</ul> -<p>You will be building assessment workflow that allows Community Health Workers to conduct a health assessment for children under the age of 5.</p> -</div> -<h2 id="brief-overview-of-key-concepts">Brief Overview of Key Concepts</h2> -<p><em><a href="https://docs.communityhealthtoolkit.org/apps/reference/forms/app/">App forms</a></em> serve as actions within the app.</p> -<p><em><a href="https://docs.communityhealthtoolkit.org/apps/reference/forms/app/#xlsform">XLSForm</a></em> is a form <a href="http://xlsform.org/en/">standard</a> created to help simplify the authoring of forms in Excel.</p> -<p><em><a href="https://docs.communityhealthtoolkit.org/apps/reference/forms/app/#xform">XForm</a></em> is a CHT-enhanced version of the <a href="https://getodk.github.io/xforms-spec/">ODK XForm</a> standard.</p> -<h2 id="required-resources">Required Resources</h2> -<p>You should have a <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/">functioning CHT instance with <code>cht-conf</code> installed locally</a> and a <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/#3-create-and-upload-a-blank-project">project folder set up</a> already.</p> -<h2 id="implementation-steps">Implementation Steps</h2> -<p>Create a new spread sheet in Google sheets or other preferred editor like Excel or Open Office. Name the spreadsheet <code>assessment</code>. The final file name should be <code>assessment.xlsx</code>.</p> -<p>Create 2 additional sheets. Rename the sheets <code>survey</code>, <code>choices</code> and <code>settings</code>.</p> -<h3 id="1-define-xls-surveyform-fields">1. Define XLS Survey/Form Fields</h3> -<p>Create the following columns in the survey sheet and then add the following rows that are populated automatically before the form is rendered to the user. These fields are usually hidden by default but can be accessed to display certain information about the person being assessed:</p> -<table> -<thead> -<tr> -<th>type</th> -<th>name</th> -<th>label</th> -<th>required</th> -<th>relevant</th> -<th>appearance</th> -<th>constraint</th> -<th>constraint_message</th> -<th>calculation</th> -<th>choice_filter</th> -<th>hint</th> -<th>default</th> -</tr> -</thead> -<tbody> -<tr> -<td>begin group</td> -<td>inputs</td> -<td>Patient</td> -<td></td> -<td>./source = &lsquo;user&rsquo;</td> -<td>field-list</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>hidden</td> -<td>source</td> -<td>Source</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td>user</td> -</tr> -<tr> -<td>hidden</td> -<td>source_id</td> -<td>Source_ID</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>hidden</td> -<td>task_id</td> -<td>Task_ID</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>begin group</td> -<td>contact</td> -<td>Contact</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>string</td> -<td>_id</td> -<td>Patient ID</td> -<td></td> -<td></td> -<td>select-contact type-person</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td>Select a person from the list</td> -<td></td> -</tr> -<tr> -<td>hidden</td> -<td>patient_id</td> -<td>Medic ID</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>hidden</td> -<td>name</td> -<td>Patient Name</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>begin group</td> -<td>parent</td> -<td>Parent</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>hidden</td> -<td>_id</td> -<td>Family UUID</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>begin group</td> -<td>parent</td> -<td>Grandparent</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>hidden</td> -<td>_id</td> -<td>CHW Area UUID</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>hidden</td> -<td>name</td> -<td>CHW Name</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>hidden</td> -<td>phone</td> -<td>CHW Phone</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>begin group</td> -<td>parent</td> -<td>Great Grandparent</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>hidden</td> -<td>_id</td> -<td>CU UUID</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>end group</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>end group</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>end group</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>end group</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>end group</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>calculate</td> -<td>patient_id</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td>../inputs/contact/_id</td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>calculate</td> -<td>patient_name</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td>../inputs/contact/name</td> -<td></td> -<td></td> -<td></td> -</tr> -</tbody> -</table> -<p>Add the following rows that define the data collection fields below the existing rows (leave out the column names):</p> -<table> -<thead> -<tr> -<th>type</th> -<th>name</th> -<th>label</th> -<th>required</th> -<th>relevant</th> -<th>appearance</th> -<th>constraint</th> -<th>constraint_message</th> -<th>calculation</th> -<th>choice_filter</th> -<th>hint</th> -<th>default</th> -</tr> -</thead> -<tbody> -<tr> -<td>begin group</td> -<td>group_assessment</td> -<td>Assessment</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>select_one yes_no</td> -<td>cough</td> -<td>Does ${patient_name} have a cough?</td> -<td>yes</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>select_one symptom_duration</td> -<td>cough_duration</td> -<td>How long has the cough lasted?</td> -<td>yes</td> -<td>${cough} = &lsquo;yes&rsquo;</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -<tr> -<td>end group</td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -<td></td> -</tr> -</tbody> -</table> -<h3 id="2-define-the-choices">2. Define the Choices</h3> -<p>Add the following column names and rows to the choices sheet:</p> -<table> -<thead> -<tr> -<th>list_name</th> -<th>name</th> -<th>label</th> -</tr> -</thead> -<tbody> -<tr> -<td>yes_no</td> -<td>yes</td> -<td>Yes</td> -</tr> -<tr> -<td>yes_no</td> -<td>no</td> -<td>No</td> -</tr> -<tr> -<td>symptom_duration</td> -<td>3</td> -<td>3 days or less</td> -</tr> -<tr> -<td>symptom_duration</td> -<td>7</td> -<td>4 - 7 days</td> -</tr> -<tr> -<td>symptom_duration</td> -<td>13</td> -<td>8 - 13 days</td> -</tr> -<tr> -<td>symptom_duration</td> -<td>14</td> -<td>14 days or more</td> -</tr> -</tbody> -</table> -<h3 id="3-define-the-xls-settings">3. Define the XLS Settings</h3> -<p>Add the following column names and rows to the settings sheet:</p> -<table> -<thead> -<tr> -<th>form_title</th> -<th>form_id</th> -<th>version</th> -<th>style</th> -<th>path</th> -<th>instance_name</th> -<th>default_language</th> -</tr> -</thead> -<tbody> -<tr> -<td>Assess patient</td> -<td>assessment</td> -<td>1</td> -<td>pages</td> -<td>data</td> -<td></td> -<td>en</td> -</tr> -</tbody> -</table> -<h3 id="4-convert-the-xlsform-and-upload-the-xform">4. Convert the XLSForm and Upload the XForm</h3> -<p>Add the file to the <code>forms/app</code> subfolder in your project.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>project-name -</span></span><span style="display:flex;"><span> forms -</span></span><span style="display:flex;"><span> app -</span></span><span style="display:flex;"><span> assessment.xlsx -</span></span></code></pre></div><p>To convert and upload the form to your local instance, run the following command from the root folder:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>cht --url<span style="color:#ce5c00;font-weight:bold">=</span>https://&lt;username&gt;:&lt;password&gt;@localhost --accept-self-signed-certs convert-app-forms upload-app-forms -- assessment -</span></span></code></pre></div> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Be sure to replace the values <code>&lt;username&gt;</code> and <code>&lt;password&gt;</code> with the actual username and password of your test instance. -</div> -<h2 id="next-steps">Next steps</h2> -<p>In the next tutorial, you will define the form <code>&lt;form_id&gt;.properties.json</code> which will allow you to define the form’s title and icon, as well as when and where the form should be available.</p>Apps: Setting Form Propertieshttps://docs.communityhealthtoolkit.org/apps/tutorials/form-properties/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/form-properties/ -<div class="pageinfo pageinfo-primary"> -<p>This tutorial will take you through how to write the <code>&lt;form_name&gt;.properties.json</code> file.</p> -<p>The <code>&lt;form_name&gt;.properties.json</code> file allows you to add logic that ensures that the right action appears for the right contacts (people and places). For instance, an assessment form for children under-5 will only appear for person contacts on the CHT whose age is less than 5.</p> -<p>You will be adding meta-data and context to an assessment workflow that allows Community Health Workers to conduct a health assessment for children under the age of 5.</p> -</div> -<h2 id="brief-overview-of-key-concepts">Brief Overview of Key Concepts</h2> -<p><em><a href="https://docs.communityhealthtoolkit.org/apps/reference/forms/app/#formsappform_namepropertiesjson">Form context</a></em> defines when and where the form should be available in the app.</p> -<h2 id="required-resources">Required Resources</h2> -<p>You should have a functioning <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/">CHT instance with <code>cht-conf</code> installed locally</a>, completed a <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/#3-create-and-upload-a-blank-project">project folder</a> setup, and an <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/app-forms/">assessment form</a>.</p> -<h2 id="implementation-steps">Implementation Steps</h2> -<p>Create a new file in the same folder as your <code>assessment.xlsx</code> file and name it <code>assessment.properties.json</code>.</p> -<h3 id="1-define-the-forms-title">1. Define the Form&rsquo;s Title</h3> -<p>Edit the <code>assessment.properties.json</code> file and add a <code>title</code> key with the value corresponding to the desired file title.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;title&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Assessment&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="2-define-the-forms-icon">2. Define the Form&rsquo;s Icon</h3> -<p>Add a <code>resources</code> folder in your project folder and put your preferred icon for assessment in it. Name the icon file <code>icon-healthcare-assessment.png</code> if it is a <code>png</code> file or <code>icon-healthcare-assessment.svg</code> if it is an <code>svg</code> file.</p> -<p>Create a <code>resources.json</code> <em>file</em> in your project folder and add key/value pairs for your icon resources.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;icon-healthcare-assessment&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;icon-healthcare-assessment.png&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/design/icons/">Icon Library</a></p> -<p>Add an <code>icon</code> key in the <code>assessment.properties.json</code> file. Pick the key of the icon you require from the <code>resources.json</code> file and add it as the <code>icon</code> value.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;title&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Assessment&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;icon&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;icon-healthcare-assessment&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="3-define-the-forms-context">3. Define the Form&rsquo;s Context</h3> -<p>First, add a <code>context</code> key in the <code>assessment.properties.json</code> file. Next, add an object with <code>person</code>, <code>place</code> and <code>expression</code> keys. Then, add the boolean value <code>true</code> for the <code>person</code> key, the boolean value <code>false</code> for the <code>place</code> key and the expression <code>ageInYears(contact) &lt; 5</code> for the <code>expression</code> key.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;title&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Assessment&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;icon&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;icon-healthcare-assessment&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;context&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;person&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;place&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;expression&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;ageInYears(contact) &lt; 5&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="4-upload-resources-and-the-form_namepropertiesjson-file">4. Upload resources and the <code>&lt;form_name&gt;.properties.json</code> File</h3> -<p>Run the following command from the root folder to upload the resources folder and <code>resources.json</code> file:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>cht --url<span style="color:#ce5c00;font-weight:bold">=</span>https://&lt;username&gt;:&lt;password&gt;@localhost --accept-self-signed-certs upload-resources -</span></span></code></pre></div><p>Run the following command from the root folder to upload the <code>assessment.properties.json</code> file:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>cht --url<span style="color:#ce5c00;font-weight:bold">=</span>https://&lt;username&gt;:&lt;password&gt;@localhost --accept-self-signed-certs upload-app-forms -- assessment -</span></span></code></pre></div> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Be sure to replace the values <code>&lt;username&gt;</code> and <code>&lt;password&gt;</code> with the actual username and password of your test instance. -</div> -<p>Once you successfully upload the <code>assessment.properties.json</code> file, &lsquo;Assessment&rsquo; will appear as an action <em>only</em> for person contacts who are less that 5 years old. Additionally, the <code>icon-healthcare-assessment</code> icon will now show alongside the action name.</p> -<h2 id="frequently-asked-questions">Frequently Asked Questions</h2> -<ul> -<li><a href="https://forum.communityhealthtoolkit.org/t/can-you-associate-an-icon-to-a-xml-form/88">Can you associate an icon to a xml form?</a></li> -</ul>Apps: Building A Simple Taskhttps://docs.communityhealthtoolkit.org/apps/tutorials/tasks-1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/tasks-1/ -<div class="pageinfo pageinfo-primary"> -<p>Tasks prompt users to complete activities on a programmatic schedule. This guide will explain how to write a task which prompts CHW users to complete an <em>assessment</em> <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/app-forms/">app form</a> for new patients within 7 days of registration.</p> -<ul> -<li>Creating a straight-forward task</li> -<li>Running and testing that task</li> -</ul> -</div> -<h2 id="prerequisites">Prerequisites</h2> -<ul> -<li>Complete the <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/app-forms/">App Forms Tutorial</a> - Tasks prompt users to <em>complete activities</em> by opening an app form. The app forms tutorial produces an <em>assessment</em> app form which we will use here. You can also elect to substitute that with any <a href="https://github.com/medic/cht-core/tree/master/config/default/forms/app">example app form</a>.</li> -<li>Complete the <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/contact-and-users-2/">Contact and User Management - Part 1 Tutorial</a> to create a hierarchy of contacts and an offline CHW user.</li> -<li>Read <a href="https://docs.communityhealthtoolkit.org/apps/guides/tasks/task-schema-parameters/">Understanding the data available in tasks and targets</a></li> -</ul> -<h2 id="implementation-steps">Implementation Steps</h2> -<p>Create a <code>tasks.js</code> file (this may have already been created by the <code>initialise-project-layout</code> command).</p> -<h3 id="1-define-a-simple-task">1. Define a Simple Task</h3> -<p>The appearance, behaviour, and schedule of tasks are all controlled through the JavaScript in the <code>tasks.js</code> file. Let&rsquo;s start in that file with a simple first task:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#000">module</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">exports</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">[{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">name</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;assessment-after-registration&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">title</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;First Assessment&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;icon-healthcare&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesTo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contacts&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;patient&#39;</span><span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">c</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000">user</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">user</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact_type</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;chw_area&#39;</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#000">c</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">date_of_death</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#000">c</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">muted</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">actions</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[{</span> <span style="color:#000">form</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;assessment&#39;</span> <span style="color:#000;font-weight:bold">}],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">events</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">start</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">7</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">days</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">7</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">end</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}],</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}];</span> -</span></span></code></pre></div><p><strong>What is this code doing?</strong></p> -<p>The <code>tasks.js</code> file follows the JavaScript ES6 Module syntax and <em>exports</em> an array of objects matching the <a href="https://docs.communityhealthtoolkit.org/apps/reference/tasks/#tasksjs">task.js schema</a>*. In the code above, the <code>tasks.js</code> file is exporting one task object with the following:</p> -<ul> -<li><code>name</code> - This is used exclusively in the task&rsquo;s backend data. The <em>name</em> isn&rsquo;t controlling any element of the tasks&rsquo;s behaviour, appearance, or schedule.</li> -<li><code>title</code> - This is controlling the &ldquo;Task title&rdquo; as defined in the <a href="https://docs.communityhealthtoolkit.org/design/best-practices/#anatomy-of-a-task">anatomy of a task</a>.</li> -<li><code>icon</code> - This references a <a href="https://docs.communityhealthtoolkit.org/apps/reference/resources/">resource</a> to be used as the task&rsquo;s icon. Refer to <a href="https://docs.communityhealthtoolkit.org/design/best-practices/#anatomy-of-a-task">anatomy of a task</a>.</li> -<li><code>appliesTo</code> - We use <code>contacts</code> because we want one task <em>per contact</em>. For more details, read <a href="https://docs.communityhealthtoolkit.org/apps/guides/tasks/task-schema-parameters/">Understanding the data available in tasks and targets</a>.</li> -<li><code>appliesToType</code> - The task should only show for contacts with <code>contact_type</code> equal to <code>patient</code>. This <code>appliesToType</code> is a <em>short-hand</em> equivalent to <code>appliesIf: c =&gt; c.contact.contact_type === 'patient'</code>.</li> -<li><code>appliesIf</code> - A predicate which gates the creation of the task&rsquo;s event schedule. For more details, read <a href="https://docs.communityhealthtoolkit.org/apps/guides/tasks/task-schema-parameters/">Understanding the data available in tasks and targets</a>. -<ul> -<li><code>user.parent.contact_type</code> - The user is a CHW iff their parent is of type <code>chw_area</code>. The user object is hydrated.</li> -<li><code>!c.contact.date_of_death</code> - The contact must be alive</li> -<li><code>!c.contact.muted</code> - The contact must be unmuted</li> -</ul> -</li> -<li><code>actions</code> - Actions control what happens when the user &ldquo;selects&rdquo; the task (clicks on it or touches it). We want to have the single option of completing the <em>assessment form</em>.</li> -<li><code>events</code> - This controls the task&rsquo;s schedule. We want a single event because this is a one-time follow-up.</li> -<li><code>events[0].days</code> - The task event is due 7 days after the contact&rsquo;s creation date.</li> -<li><code>events[0].start</code> - The task event should appear 7 days before the due date, or immediately when the contact is created.</li> -<li><code>events[0].end</code> - The task event should disappear the day after the due date.</li> -</ul> -<h3 id="2-uploading-the-task">2. Uploading the Task</h3> -<p>To run the <code>tasks.js</code> code, you&rsquo;ll need to load the code into your running CHT application.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>cht --url<span style="color:#ce5c00;font-weight:bold">=</span>https://&lt;username&gt;:&lt;password&gt;@localhost -</span></span></code></pre></div><p>or for a faster experience, compile and upload only the relevant changes:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>cht --url<span style="color:#ce5c00;font-weight:bold">=</span>https://&lt;username&gt;:&lt;password&gt;@localhost compile-app-settings upload-app-settings -</span></span></code></pre></div><h3 id="3-testing-the-task">3. Testing the Task</h3> -<p>Tasks are only available to <a href="https://docs.communityhealthtoolkit.org/apps/concepts/users/#offline-users">offline users</a>. To view and test this simple task, you&rsquo;ll need to login as an offline user like the CHW-level user created in the <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/contact-and-users-1/">Contact and User Management - Part 1 Tutorial</a>. Once logged in, sync to make sure you have the latest configuration. You may be prompted to reload the application.</p> -<p>Create a new contact in the hierarchy and navigate to the <code>Tasks</code> tab. You should see the new <code>assessment-after-registration</code> task!</p> -<figure><a href="first-task.png"> -<img src="first-task.png"/> </a> -</figure> -<p>Next, test a few of the expected behaviours for the task:</p> -<ul> -<li>Confirm that clicking on the task causes the <em>assessment app form</em> to load.</li> -<li>Confirm that completing the <em>assessment app form</em> causes the task to disappear.</li> -<li>Move your system clock forward 7 days and reload the tasks tab. You should see that the task is now &ldquo;Due Today&rdquo;.</li> -<li>Move your system clock forward 8 days and reload the tasks tab. The task should disappear since the 7 day window has expired.</li> -<li>Login as a supervisor user. You should not be able to see the task.</li> -<li>The task should not appear only for patients - not for places or CHWs.</li> -<li>If you mute a contact or <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/death-reporting/">report the contact dead</a>, the task should disappear.</li> -</ul> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Remember to reset your system clock to be accurate when you are done testing. -</div> -<h2 id="frequently-asked-questions">Frequently Asked Questions</h2> -<ul> -<li><a href="https://forum.communityhealthtoolkit.org/t/error-fetching-tasks-tasks-not-appearing/537">&ldquo;Error fetching tasks&rdquo; - Tasks not appearing</a></li> -<li><a href="https://forum.communityhealthtoolkit.org/t/tasks-for-online-users/574">Tasks for online users</a></li> -<li><a href="https://forum.communityhealthtoolkit.org/t/how-can-i-debug-task-rules/108">How can I debug task rules?</a></li> -</ul>Apps: Building Target Widgetshttps://docs.communityhealthtoolkit.org/apps/tutorials/targets/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/targets/ -<div class="pageinfo pageinfo-primary"> -<p>This tutorial will take you through how to build target widgets.</p> -<p>Target widgets provide a summary or analysis of the data in submitted reports.</p> -<p>You will be adding target widgets that will allow Community Health Workers (CHWs) to track various metrics based on assessment reports submitted.</p> -</div> -<h2 id="brief-overview-of-key-concepts">Brief Overview of Key Concepts</h2> -<p><em><a href="https://docs.communityhealthtoolkit.org/apps/features/targets/">Targets</a></em> is the user dashboard or analytics tab.</p> -<p><em><a href="https://docs.communityhealthtoolkit.org/apps/features/targets/#types-of-widgets">Target widgets</a></em> provide a summary or analysis of the data in submitted reports.</p> -<p><em><a href="https://docs.communityhealthtoolkit.org/apps/features/targets/#count-widgets">Count widgets</a></em> show a tally of a particular report that has been submitted or data within a report that matches a set of criteria.</p> -<p><em><a href="https://docs.communityhealthtoolkit.org/apps/features/targets/#percent-widgets">Percent widgets</a></em> display a ratio, which helps to provide insight into the proportion that matches a defined criteria.</p> -<p><em><a href="https://docs.communityhealthtoolkit.org/apps/reference/targets/#targetsjs">Target schema</a></em> details a set of properties for targets.</p> -<p><em>Target instance</em> is an object emitted and counted or aggregated based on target configuration.</p> -<h2 id="required-resources">Required Resources</h2> -<p>You should have a functioning <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/">CHT instance with <code>cht-conf</code> installed locally</a>, completed a <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/#3-create-and-upload-a-blank-project">project folder</a> setup, and an <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/app-forms/">assessment form</a>.</p> -<h2 id="implementation-steps">Implementation Steps</h2> -<p>It is good practice to set up a reference document outlining the specifications of the target widgets similar to the one below. Other formats may also be used.</p> -<table> -<thead> -<tr> -<th>Source</th> -<th>UI Label</th> -<th>Definition</th> -<th>Type</th> -<th>Reporting Period</th> -<th>Goal</th> -</tr> -</thead> -<tbody> -<tr> -<td>Assessment form</td> -<td>Total assessments</td> -<td>Total number of assessment reports submitted</td> -<td>Count</td> -<td>All time</td> -<td>_</td> -</tr> -<tr> -<td>Assessment form</td> -<td>Total assessments</td> -<td>Total number of assessment reports submitted this month</td> -<td>Count</td> -<td>This month</td> -<td>2</td> -</tr> -<tr> -<td>Assessment form</td> -<td>Total population with cough</td> -<td>Total number of household members with cough</td> -<td>Count</td> -<td>This month</td> -<td>_</td> -</tr> -<tr> -<td>Assessment form</td> -<td>% Population with cough</td> -<td>Total number of contacts with cough/Total number of contacts assessed</td> -<td>Percent</td> -<td>This month</td> -<td>_</td> -</tr> -<tr> -<td>Assessment form</td> -<td>Total households with assessments</td> -<td>Total number of households with at least one submitted assessment form</td> -<td>Count</td> -<td>This month</td> -<td>2</td> -</tr> -<tr> -<td>Assessment form</td> -<td>% Household with &gt;=2 assessments</td> -<td>Total number of households with at least two patients assessed/Total number of households</td> -<td>Percent</td> -<td>All time</td> -<td>60</td> -</tr> -</tbody> -</table> -<p>Create a <code>targets.js</code> file (this may have already been created by the <code>initialise-project-layout</code> command).</p> -<h3 id="1-define-an-all-time-target-widget">1. Define an All-Time Target Widget</h3> -<p>This widget counts the total number of assessment reports that have been submitted by the user from the time that they started reporting. -Edit the <code>targets.js</code> file to define the total assessments all-time widget as shown below:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;assessments-all-time&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;count&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;icon-healthcare-assessment&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">goal</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.assessments.title&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">subtitle_translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.all_time.subtitle&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesTo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;reports&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;assessment&#39;</span><span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">date</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;now&#39;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="2-define-the-total-assessments-monthly-target-widget">2. Define the Total Assessments Monthly Target Widget</h3> -<p>This widget counts the total number of assessment reports that have been submitted by the user for this month. -Edit the <code>targets.js</code> file and add another target widget definition object to define the total assessments monthly widget as shown below:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;assessments-this-month&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;count&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;icon-healthcare-assessment&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">goal</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.assessments.title&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">subtitle_translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.this_month.subtitle&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesTo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;reports&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;assessment&#39;</span><span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">date</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;reported&#39;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -All-time widgets have the <code>date</code> property set to <code>now</code> while monthly widgets have the <code>date</code> property set to <code>reported</code>. -</div> -<p> -<figure class="right col-6 col-lg-4"><a href="assessments_all_time_no_translation.png"> -<img src="assessments_all_time_no_translation.png"/> </a> -</figure> -<figure class="right col-6 col-lg-4"><a href="assessments_this_month_no_translation.png"> -<img src="assessments_this_month_no_translation.png"/> </a> -</figure> -</p> -<p>The images show the <code>monthly</code> and <code>all-time</code> target widgets respectively, with the resulting figures varying depending on the number of assessment reports submitted within the respective durations.</p> -<h3 id="3-define-the-cough-count-widget">3. Define the Cough Count Widget</h3> -<p>This widget calculates the total number of patients assessed that have been indicated to have a cough this month, regardless of the number of reports submitted for them. Note that <code>idType</code> counts the contact IDs. -Edit the <code>targets.js</code> file and add the target widget as shown below:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;total-contacts-with-cough-this-month&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;count&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;icon-cough&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">goal</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.assessments.total.cough.title&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">subtitle_translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.this_month.subtitle&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesTo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;reports&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;assessment&#39;</span><span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">report</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">Utils</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getField</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;group_assessment.cough&#39;</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;yes&#39;</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">idType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">date</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;reported&#39;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="4-define-the-cough-percentage-widget">4. Define the Cough Percentage Widget</h3> -<p>This widget calculates the percentage patients assessed that have been indicated to have a cough this month, regardless of the number of reports submitted for them. -Edit the <code>targets.js</code> file and add the target widget as shown below:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;percentage-contacts-with-cough-this-month&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;percent&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;icon-cough&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">goal</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.assessments.percentage.cough.title&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">percentage_count_translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.assessments.percentage.with.cough&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">subtitle_translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.this_month.subtitle&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesTo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;reports&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;assessment&#39;</span><span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">passesIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">report</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">Utils</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getField</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;group_assessment.cough&#39;</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;yes&#39;</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">idType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">date</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;reported&#39;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="5-define-total-households-with-assessments-widget">5. Define Total Households with Assessments Widget</h3> -<p>This widget calculates the number of households that have patients assessed this month. Note that <code>emitCustom</code> emits a target with custom conditions that may otherwise not be directly configurable through the target schema. Filter based on reports, and use the contact information from the reports to emit targets. The target ID is the household ID. -Edit the <code>targets.js</code> file and add the target widget as shown below:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;households-with-assessments-this-month&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;count&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;icon-household&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">goal</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.households.with.assessments.title&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">subtitle_translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.this_month.subtitle&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesTo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;reports&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;assessment&#39;</span><span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">date</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;reported&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">emitCustom</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">emit</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">original</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">householdId</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">_id</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">emit</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87">Object</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">assign</span><span style="color:#000;font-weight:bold">({},</span> <span style="color:#000">original</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">_id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">householdId</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">pass</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}));</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span></code></pre></div><p> -<figure class="right col-6 col-lg-4"><a href="household_assessments_reached_goal.png"> -<img src="household_assessments_reached_goal.png"/> </a> -</figure> -<figure class="right col-6 col-lg-4"><a href="household_assessments_zero_reports.png"> -<img src="household_assessments_zero_reports.png"/> </a> -</figure> -</p> -<p>Note the difference in appearance on the resulting <code>count</code> target widgets based on whether the <code>goal</code> is achieved. The figures vary depending on the number of assessment reports submitted for household members.</p> -<h3 id="6-define-percentage-households-with-2-assessments-widget">6. Define Percentage Households with &gt;=2 Assessments Widget</h3> -<p>This widget calculates the percentage of households that have two or more patients assessed all time. Use <code>emitCustom</code> to emit a custom target instance. The target instance ID is the household ID. -Edit the <code>targets.js</code> file and add the target widget as shown below:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">//Define a function to get the household ID based on the hierarchy configuration -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">getHouseholdId</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">type</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;clinic&#39;</span> <span style="color:#ce5c00;font-weight:bold">?</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">_id</span> <span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">_id</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">//Define a function to determine if contact is patient -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">isPatient</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">type</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;person&#39;</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;households-with-gt2-assessments-this-month&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;percent&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;icon-household&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">goal</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">60</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.households.with.gt2.assessments.title&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">subtitle_translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.all_time.subtitle&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesTo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contacts&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;person&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;clinic&#39;</span><span style="color:#000;font-weight:bold">],</span> <span style="color:#8f5902;font-style:italic">//Need the total number of households as denominator -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">date</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;now&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">emitCustom</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">emit</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">original</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">householdId</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">getHouseholdId</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">isPatient</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">))</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reports</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">some</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000">report</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">form</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;assessment&#39;</span><span style="color:#000;font-weight:bold">))</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">emit</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87">Object</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">assign</span><span style="color:#000;font-weight:bold">({},</span> <span style="color:#000">original</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">targetInstance</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">_id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">householdId</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#8f5902;font-style:italic">//Emits a passing target instance with the household ID as the target instance ID -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">pass</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}));</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">if</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">type</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;clinic&#39;</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#8f5902;font-style:italic">//This represents the denominator, which is the total number of households -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">emit</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87">Object</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">assign</span><span style="color:#000;font-weight:bold">({},</span> <span style="color:#000">original</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">_id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">householdId</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">pass</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#8f5902;font-style:italic">//Set to false so that it is counted in the denominator -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">}));</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">groupBy</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">contact</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000">getHouseholdId</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">),</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">passesIfGroupCount</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">gte</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">2</span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/concepts/hierarchy/">Hierarchies</a></p> -<p> -<figure class="right col-6 col-lg-4"><a href="households_gt2_goal_reached.png"> -<img src="households_gt2_goal_reached.png"/> </a> -</figure> -<figure class="right col-6 col-lg-4"><a href="households_gt2_goal_not_reached.png"> -<img src="households_gt2_goal_not_reached.png"/> </a> -</figure> -</p> -<p>The images show the resulting <code>percent</code> target widgets, with the figures varying depending on the number of assessment reports submitted for household members. Note the difference in appearance based on whether the <code>goal</code> is achieved.</p> -<h3 id="7-final-targetsjs-file">7. Final <code>targets.js</code> file</h3> -<p>Include the functions and replace appropriately in the file. The final content of the targets file should be similar the one below:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">//Define a function to get the household ID based on the hierarchy configuration -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">getHouseholdId</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">type</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;clinic&#39;</span> <span style="color:#ce5c00;font-weight:bold">?</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">_id</span> <span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">_id</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">//Define a function to determine if contact is patient -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">isPatient</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">type</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;person&#39;</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#000">module</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">exports</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;assessments-all-time&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;count&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;icon-healthcare-assessment&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">goal</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.assessments.title&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">subtitle_translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.all_time.subtitle&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesTo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;reports&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;assessment&#39;</span><span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">date</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;now&#39;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;assessments-this-month&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;count&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;icon-healthcare-assessment&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">goal</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.assessments.title&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">subtitle_translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.this_month.subtitle&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesTo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;reports&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;assessment&#39;</span><span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">date</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;reported&#39;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;total-contacts-with-cough-this-month&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;count&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;icon-cough&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">goal</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.assessments.total.cough.title&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">subtitle_translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.this_month.subtitle&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesTo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;reports&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;assessment&#39;</span><span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">report</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">Utils</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getField</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;group_assessment.cough&#39;</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;yes&#39;</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">idType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">date</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;reported&#39;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;percentage-contacts-with-cough-this-month&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;percent&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;icon-cough&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">goal</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.assessments.percentage.cough.title&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">subtitle_translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.this_month.subtitle&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">percentage_count_translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.assessments.percentage.with.cough&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesTo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;reports&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;assessment&#39;</span><span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">isPatient</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">passesIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">report</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">Utils</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getField</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;group_assessment.cough&#39;</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;yes&#39;</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">idType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">date</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;reported&#39;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;households-with-assessments-this-month&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;count&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;icon-household&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">goal</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.households.with.assessments.title&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">subtitle_translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.this_month.subtitle&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesTo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;reports&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;assessment&#39;</span><span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">date</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;reported&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">emitCustom</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">emit</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">original</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">householdId</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">getHouseholdId</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">emit</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87">Object</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">assign</span><span style="color:#000;font-weight:bold">({},</span> <span style="color:#000">original</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">_id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">householdId</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">pass</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}));</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;households-with-gt2-assessments-this-month&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;percent&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;icon-household&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">goal</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">60</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.households.with.gt2.assessments.title&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">subtitle_translation_key</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;targets.all_time.subtitle&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesTo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contacts&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;person&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;clinic&#39;</span><span style="color:#000;font-weight:bold">],</span> <span style="color:#8f5902;font-style:italic">//Need the total number of households as denominator -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">date</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;now&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">emitCustom</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">emit</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">original</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">householdId</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">getHouseholdId</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">isPatient</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">))</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reports</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">some</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000">report</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">form</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;assessment&#39;</span><span style="color:#000;font-weight:bold">))</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">emit</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87">Object</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">assign</span><span style="color:#000;font-weight:bold">({},</span> <span style="color:#000">original</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">targetInstance</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">_id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">householdId</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#8f5902;font-style:italic">//Emits a passing target instance with the household ID as the target instance ID -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">pass</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}));</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">if</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">type</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;clinic&#39;</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#8f5902;font-style:italic">//This represents the denominator, which is the total number of households -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">emit</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87">Object</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">assign</span><span style="color:#000;font-weight:bold">({},</span> <span style="color:#000">original</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">_id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">householdId</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">pass</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#8f5902;font-style:italic">//Set to false so that it is counted in the denominator -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">}));</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">groupBy</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">contact</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000">getHouseholdId</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">),</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">passesIfGroupCount</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">gte</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">2</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">];</span> -</span></span></code></pre></div> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/reference/targets/">Targets overview</a></p> -<h3 id="8-compile-and-upload-app-settings">8. Compile and Upload App Settings</h3> -<p>To compile and upload app settings to your local instance, run the following command:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>cht --url<span style="color:#ce5c00;font-weight:bold">=</span>https://&lt;username&gt;:&lt;password&gt;@localhost --accept-self-signed-certs compile-app-settings upload-app-settings -</span></span></code></pre></div> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Be sure to replace the values <code>&lt;username&gt;</code> and <code>&lt;password&gt;</code> with the actual username and password of your test instance. -</div> -<figure class="right col-6 col-lg-8"><a href="targets_no_translations.png"> -<img src="targets_no_translations.png"/> </a> -</figure> -<p>The image on the right-hand side shows the expected outcome on the <code>analytics tab</code>, with figures varying depending on the number of reports submitted on your instance.</p> -<h3 id="9-upload-translations">9. Upload translations</h3> -<p>To update the titles of the target widgets, ensure that the <code>translation keys</code> are in the translations file. Add the following translation keys and their values in the <code>messages-en.properties</code> file. You may word your translation keys and values differently.</p> -<pre tabindex="0"><code>targets.assessments.title = Total assessments -targets.assessments.total.cough.title = Total population with cough -targets.assessments.percentage.cough.title = % Population with cough -targets.households.with.assessments.title = Total households with assessments -targets.households.with.gt2.assessments.title = % Household with &gt;=2 assessments -</code></pre><p>To upload <em><a href="https://docs.communityhealthtoolkit.org/apps/reference/translations/#translations">translations</a></em> to your local instance, run the following command:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>cht --url<span style="color:#ce5c00;font-weight:bold">=</span>https://&lt;username&gt;:&lt;password&gt;@localhost --accept-self-signed-certs upload-custom-translations -</span></span></code></pre></div> -<figure class="right col-6 col-lg-8"><a href="targets_with_translations.png"> -<img src="targets_with_translations.png"/> </a> -</figure> -<p>The image on the right-hand side shows the updated target titles. Your image may be different depending on your wording.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Be sure to have the correct translation key in your target widget’s <code>translation_key</code> property. -</div> -<h3 id="10-target-icons">10. Target icons</h3> -<p>You may add <code>icons</code> to your target widgets to enhance their appearance and to help users locate specific widgets more quickly. Use the icons in the <em><a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/">targets icon library</a></em>, or icons of your choice for the target widgets. Add your selected icons to the <code>resources</code> folder in your project folder. In your <code>resources.json</code> <em>file</em>, add key/value pairs for your icon resources.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;icon-healthcare-assessment&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;icon-healthcare-assessment.svg&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;icon-household&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;icon-places-household.svg&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;icon-cough&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;icon-condition-cough.svg&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -The <code>key</code> in the <code>resources.json</code> file is the value of the <code>icon</code> property in the target widget configuration. -</div> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/design/icons/">Icon Library</a></p> -<p>To upload <em><a href="https://docs.communityhealthtoolkit.org/apps/reference/resources/#icons">resources</a></em> to your local instance, run the following command:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>cht --url<span style="color:#ce5c00;font-weight:bold">=</span>https://&lt;username&gt;:&lt;password&gt;@localhost --accept-self-signed-certs upload-resources -</span></span></code></pre></div> -<figure class="right col-6 col-lg-8"><a href="final_targets.png"> -<img src="final_targets.png"/> </a> -</figure> -<p>The image on the right-hand side shows the expected final appearance of the target widgets on adding and uploading resources. The figures on the widgets will depend on the number of reports submitted for contacts and the number of contacts you have created on your instance.</p> -<h2 id="frequently-asked-questions">Frequently Asked Questions</h2> -<ul> -<li><a href="https://forum.communityhealthtoolkit.org/t/how-are-targets-ordered/547">How are targets ordered?</a></li> -<li><a href="https://forum.communityhealthtoolkit.org/t/targets-are-disabled-for-admin-users-if-you-need-to-see-targets-login-as-a-normal-user/912">What types of users can see target widgets?</a></li> -</ul>Apps: Building Contact Summaryhttps://docs.communityhealthtoolkit.org/apps/tutorials/contact-summary/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/contact-summary/ -<div class="pageinfo pageinfo-primary"> -<p>This tutorial will take you through building a contact summary for CHT applications.</p> -<p>Contact summaries display basic information about the contact.</p> -<p>You will be adding a contact summary that displays information about a person&rsquo;s <em>patient id</em>, <em>age</em>, <em>sex</em>, <em>phone number</em>, and information about the place they belong to ie. <em>parent</em>.</p> -</div> -<h2 id="brief-overview-of-key-concepts">Brief Overview of Key Concepts</h2> -<p>Each <em>field</em> that can be shown on a contact’s profile is defined as an object in the <em><a href="https://docs.communityhealthtoolkit.org/apps/reference/contact-page/#contact-summarytemplatedjs-fields">fields array</a></em> of <code>contact-summary.templated.js</code>.</p> -<p>The <em>properties</em> for each object determine how and when the field is shown.</p> -<h2 id="required-resources">Required Resources</h2> -<p>You should have a functioning <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/">CHT instance with <code>cht-conf</code> installed locally</a>, and completed a <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/#3-create-and-upload-a-blank-project">project folder</a> setup.</p> -<h2 id="implementation-steps">Implementation Steps</h2> -<p>Create a <code>contact-summary.templated.js</code> file. (This may have already been created by the initialise-project-layout command.)</p> -<h3 id="1-add-dependencies-and-variable-definitions">1. Add Dependencies and Variable Definitions</h3> -<p>Add the following dependencies and variable definitions at the top of the file:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">thisContact</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">thisLineage</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">lineage</span><span style="color:#000;font-weight:bold">;</span> -</span></span></code></pre></div> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -contact, reports, lineage are globally available for contact-summary. -</div> -<br clear="all"> -<hr> -<h3 id="2-define-contact-summary-fields">2. Define Contact Summary Fields</h3> -<p>Define the <code>patient_id</code>, <code>age</code>, <code>sex</code>, <code>phone</code>, and <code>parent</code> contact fields as shown below:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">thisContact</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">thisLineage</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">lineage</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">fields</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;person&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;patient_id&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">thisContact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">patient_id</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">width</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">4</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;person&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.age&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">thisContact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">date_of_birth</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">width</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">filter</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;age&#39;</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;person&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.sex&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.sex.&#39;</span> <span style="color:#ce5c00;font-weight:bold">+</span> <span style="color:#000">thisContact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">sex</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">translate</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">width</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">4</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;person&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;person.field.phone&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">thisContact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">phone</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">width</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">4</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;person&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.parent&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">thisLineage</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">filter</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;lineage&#39;</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">];</span> -</span></span></code></pre></div> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -The contact field e.g. <code>patient_id</code> and <code>date_of_birth</code>, should exist in the contact’s document for it to return a value. -</div> -<br clear="all"> -<hr> -<h3 id="3-export-fields">3. Export <code>fields</code></h3> -<p>Export the defined fields as shown below:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">thisContact</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">thisLineage</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">lineage</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">fields</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;person&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;patient_id&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">thisContact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">patient_id</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">width</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">4</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;person&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.age&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">thisContact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">date_of_birth</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">width</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">filter</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;age&#39;</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;person&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.sex&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.sex.&#39;</span> <span style="color:#ce5c00;font-weight:bold">+</span> <span style="color:#000">thisContact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">sex</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">translate</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">width</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">4</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;person&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;person.field.phone&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">thisContact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">phone</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">width</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">4</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;person&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.parent&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">thisLineage</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">filter</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;lineage&#39;</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">];</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#000">module</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">exports</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">fields</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">fields</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">};</span> -</span></span></code></pre></div><br clear="all"> -<hr> -<h3 id="4-compile-and-upload-app-settings">4. Compile and Upload App Settings</h3> -<p>To compile and upload app settings to your local instance, run the following command:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>cht --url<span style="color:#ce5c00;font-weight:bold">=</span>https://&lt;username&gt;:&lt;password&gt;@localhost --accept-self-signed-certs compile-app-settings upload-app-settings -</span></span></code></pre></div> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Be sure to replace the values <code>&lt;username&gt;</code> and <code>&lt;password&gt;</code> with the actual username and password of your test instance. -</div>Apps: Building Death Report Workflowshttps://docs.communityhealthtoolkit.org/apps/tutorials/death-reporting/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/death-reporting/ -<h1 id="death-reporting">Death Reporting</h1> -<h4 id="guide-for-setting-up-a-comprehensive-death-report-workflow">Guide for setting up a comprehensive death report workflow</h4> -<div class="pageinfo pageinfo-primary"> -<p>In this tutorial you will learn how to set up a death report workflow. This includes laying out a death report form as well as handling all the configurations needed for wiring it up in the CHT. -By the end of the tutorial you should be able to:</p> -<ul> -<li>Mark select contacts as deceased</li> -<li>Make relevant app updates for dead contacts</li> -</ul> -</div> -<h2 id="brief-overview-of-key-concepts">Brief Overview of Key Concepts</h2> -<p>When a contact is marked as deceased within the CHT, the contact will be hidden by default on the contacts tab.</p> -<figure class="right col-6 col-lg-8"><a href="contacts-tab-with-deceased.png"> -<img src="contacts-tab-with-deceased.png"/> </a> -</figure> -<h2 id="required-resources">Required Resources</h2> -<p>You will need to:</p> -<ol> -<li><a href="https://docs.communityhealthtoolkit.org/apps/tutorials/application-settings/">Configure your application hierarchy</a></li> -<li><a href="https://docs.communityhealthtoolkit.org/apps/tutorials/contact-and-users-1/">Create some contacts</a></li> -<li><a href="https://docs.communityhealthtoolkit.org/apps/tutorials/app-forms/">Know how to create an app form</a></li> -<li><a href="https://docs.communityhealthtoolkit.org/apps/tutorials/form-properties/">Know how to set form properties</a></li> -</ol> -<h2 id="implementation-steps">Implementation Steps</h2> -<ol> -<li>Create a new app form with a name like &ldquo;Death Report&rdquo;. This will be used to flag a contact as deceased.</li> -<li>Set the form properties to show for contacts that can die.</li> -<li>Enable the <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/transitions/#death_reporting">death_reporting transition</a>.</li> -<li>Make some recommended updates to tasks, targets, and contact-summary.</li> -</ol> -<h3 id="1-create-the-death-form">1. Create the Death Form</h3> -<p>Create a <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/app-forms/">new app form</a> with your desired experience for reporting a death. Or you can use the <code>death_report.xlsx</code> and <code>death_report.properties.json</code> files <a href="https://github.com/medic/cht-core/tree/master/config/default/forms/app">from this reference application</a>.</p> -<p>It is common to want to know the date of death, place of death, or cause of death when reporting a death. If you want to ask date of the contact&rsquo;s death, use a field of type <code>date</code>. This information will be used again in step 3.</p> -<h3 id="2-edit-the-form-propertiesjson-file">2. Edit the Form Properties.json File</h3> -<p>It doesn&rsquo;t make sense to have &ldquo;places&rdquo; in your hierarchy that can be deceased. It also doesn&rsquo;t make sense for somebody who is dead to die again. But can the administration of a health facility die? That is for you to decide.</p> -<p>This snippet is an example <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/form-properties/">form properties file</a> which constrains the death form to show only for contacts which:</p> -<ol> -<li>Are currently alive</li> -<li>Are within a family</li> -<li>Have a <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/hierarchy/">contact_type with &ldquo;person: true&rdquo;</a></li> -</ol> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;context&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;expression&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;!contact.date_of_death &amp;&amp; user.parent.contact_type === &#39;family&#39;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;person&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;place&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;icon&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;icon-death-general&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;title&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;locale&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;en&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;content&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Report death&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="3-enable-the-death_reporting-transition">3. Enable the <code>death_reporting</code> Transition</h3> -<p>The <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/transitions/#death_reporting">death_reporting transition</a> will process your death report and update the deceased contact document by adding a <code>date_of_death</code> attribute to the document.</p> -<p>To enable death reporting:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#4e9a06">&#34;transitions&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#a40000">...,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;death_reporting&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span><span style="color:#a40000">,</span> -</span></span><span style="display:flex;"><span><span style="color:#4e9a06">&#34;death_reporting&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;mark_deceased_forms&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;death_report&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;date_field&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;fields.date_of_death&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>The <code>date_field</code> is optional. If a date of death is not provided, the date of the death report will be used. If your form has a field of type <code>date</code> asking for the date of the contact&rsquo;s death, use a path to that field in <code>date_field</code>.</p> -<h3 id="4-test">4. Test</h3> -<ol> -<li>Create a contact that you expect to be able to die. View the contact&rsquo;s profile in the contacts tab.</li> -<li>Click on the &ldquo;+ New Action&rdquo; tab. You should see your &ldquo;Death Report&rdquo; form there with an appropriate title and icon.</li> -<li>Select and complete your Death Report.</li> -<li>View the &ldquo;place&rdquo; containing the deceased contact in the contacts tab. The contact will not appear as &ldquo;deceased&rdquo;</li> -<li>Sync your documents (this pushes the death report to the server)</li> -<li>Sync your documents again (this pulls down the transitioned contact document from the server)</li> -<li>The contact should now be hidden and accessible only via the &ldquo;View deceased&rdquo; flyout</li> -<li>If you specified a <code>date_field</code> in Step 3, confirm that the <code>date_of_death</code> attribute on the deceased contact matches the selected date in the death report.</li> -<li>Create contacts that you <strong>do not expect</strong> to be able to die. View the contact&rsquo;s profiles and confirm the &ldquo;Death Report&rdquo; does not show in the &ldquo;+ New Action&rdquo; tab.</li> -</ol> -<h3 id="recommended-updates">Recommended updates</h3> -<h4 id="disable-tasks-for-deceased-contacts">Disable tasks for deceased contacts</h4> -<p>If a contact is dead, you may want to disable the majority of tasks for that contact. You will need to update each of your <a href="https://docs.communityhealthtoolkit.org/apps/reference/tasks/#tasksjs">task&rsquo;s definitions</a>.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">date_of_death</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">myLogic</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">...</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h4 id="prevent-the-display-of-other-forms-for-deceased-persons">Prevent the display of other forms for deceased persons</h4> -<p>You typically don&rsquo;t want users doing actions like &ldquo;health assessment&rdquo; for deceased contacts. You can achieve this by updating your other app form&rsquo;s <code>properties.json</code> files to only display for alive contacts.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;context&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;expression&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;!contact.date_of_death &amp;&amp; myLogic&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#a40000">...</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h4 id="condition-card-for-date-of-death">Condition card for &ldquo;Date of Death&rdquo;</h4> -<p>On your <a href="https://docs.communityhealthtoolkit.org/apps/reference/contact-page/">contact page</a> you may want to add a condition card to display the date of the patient&rsquo;s death.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">cards</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.profile.death.title&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;person&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000">contact</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">date_of_date</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">fields</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">([</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.profile.death.date&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">date_of_death</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">filter</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;simpleDate&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">translate</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">width</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">6</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]),</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">];</span> -</span></span></code></pre></div><h4 id="targets">Targets</h4> -<p>Should your targets count deceased contacts in the denominator? This change is left as an exercise for the reader.</p> -<h2 id="undoing-a-death-report">Undoing a death report</h2> -<p>To undo a death report, you&rsquo;ll need to create a new app form to undo the death report. Add it to <code>undo_deceased_forms</code> (similar to <code>mark_deceased_forms</code>) in app_settings step 3 above.</p>Apps: Building Condition Cardshttps://docs.communityhealthtoolkit.org/apps/tutorials/condition-cards/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/condition-cards/ -<div class="pageinfo pageinfo-primary"> -<p>This tutorial will take you through building a condition card for CHT applications.</p> -<p>Condition cards, like contact summaries display information about the contact. The data displayed in condition cards can be pulled from submitted reports.</p> -<p>In this tutorial,you will be adding a condition card that displays information about a person&rsquo;s most recent assessment, including: <em>the date of the most recent assessment</em>, and <em>whether or not they had a cough</em>.</p> -</div> -<h2 id="brief-overview-of-key-concepts">Brief Overview of Key Concepts</h2> -<p>Condition cards can be <em>permanent or conditional</em>. They can be set to appear only when a specific type of report is submitted. They can also be set to disappear when a condition is resolved or a certain amount of time has passed.</p> -<p>Condition cards have several configurable elements including:</p> -<ul> -<li>Title</li> -<li>Label for each data point displayed</li> -<li>Data point for the field</li> -<li>Icon for the field, if desired</li> -<li>Conditions under which to display</li> -</ul> -<h2 id="required-resources">Required Resources</h2> -<p>You should have a functioning <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/">CHT instance with <code>cht-conf</code> installed locally</a>, completed a <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/#3-create-and-upload-a-blank-project">project folder</a> setup, and an <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/app-forms/">assessment form</a>.</p> -<h2 id="implementation-steps">Implementation Steps</h2> -<p>Create a <code>contact-summary.templated.js</code> file if it doesn&rsquo;t exist. (This may have already been created by the initialise-project-layout command.)</p> -<h3 id="1-add-dependencies-and-variable-definitions">1. Add Dependencies and Variable Definitions</h3> -<p>Add the following dependencies and variable definitions at the top of the file (some of them may have been added from the <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/contact-summary/">contact summary tutorial</a>):</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">thisContact</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">thisLineage</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">lineage</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">allReports</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">reports</span><span style="color:#000;font-weight:bold">;</span> -</span></span></code></pre></div> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -contact, reports, lineage are globally available for contact-summary. -</div> -<br clear="all"> -<hr> -<h3 id="2-define-cards-and-add-a-condition-card-object">2. Define <code>cards</code> and Add a Condition Card Object</h3> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">thisContact</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">thisLineage</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">lineage</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">allReports</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">reports</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">cards</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.profile.assessment_history&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;report&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">assessmentForm</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">getNewestReport</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">allReports</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">assessmentForms</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">assessmentForm</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reported_date</span> <span style="color:#ce5c00;font-weight:bold">&gt;=</span> <span style="color:#000">report</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reported_date</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">fields</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.profile.most_recent_assessment.date&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">report</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reported_date</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">filter</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;simpleDate&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">width</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">6</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.profile.cough&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">report</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">fields</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">cough</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">width</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">6</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">];</span> -</span></span></code></pre></div><br clear="all"> -<hr> -<h3 id="3-export-cards">3. Export <code>cards</code></h3> -<p>Export the defined fields as shown below:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">thisContact</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">thisLineage</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">lineage</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">allReports</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">reports</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">cards</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.profile.assessment_history&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;report&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">assessmentForm</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">getNewestReport</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">allReports</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">assessmentForms</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">assessmentForm</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reported_date</span> <span style="color:#ce5c00;font-weight:bold">&gt;=</span> <span style="color:#000">report</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reported_date</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">fields</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.profile.most_recent_assessment.date&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">report</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reported_date</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">filter</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;simpleDate&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">width</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">6</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.profile.cough&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">report</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">report</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">fields</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">cough</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">width</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">6</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">];</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#000">module</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">exports</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">cards</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">cards</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">};</span> -</span></span></code></pre></div><br clear="all"> -<hr> -<h3 id="4-compile-and-upload-app-settings">4. Compile and Upload App Settings</h3> -<p>To compile and upload app settings to your local instance, run the following command:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>cht --url<span style="color:#ce5c00;font-weight:bold">=</span>https://&lt;username&gt;:&lt;password&gt;@localhost --accept-self-signed-certs compile-app-settings upload-app-settings -</span></span></code></pre></div> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Be sure to replace the values <code>&lt;username&gt;</code> and <code>&lt;password&gt;</code> with the actual username and password of your test instance. -</div>Apps: Localizationhttps://docs.communityhealthtoolkit.org/apps/tutorials/localizing-cht/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/localizing-cht/ -<div class="pageinfo pageinfo-primary"> -<p>Given that CHT apps are used around the world, the Core Framework was designed with localization in mind. The Core Framework itself is available in English, French, Hindi, Nepali, Spanish, Swahili, and Indonesian.</p> -<p>This tutorial will take you through localizing the CHT to a custom language (Swahili). This will include setting up the user interface labels as well as outgoing text messages.</p> -<p>By the end of the tutorial you should be able to:</p> -<ul> -<li>Change the CHT user interface labels to a custom language.</li> -<li>Change outgoing text messages to a custom language(Swahili will be used in the guide).</li> -</ul> -</div> -<h2 id="brief-overview-of-key-concepts">Brief Overview of Key Concepts</h2> -<p><em>Localization</em> this is setting up the desired language in CHT for the end user.</p> -<p><em>Translations</em> this is manually setting up extra translations of instance tabs texts or outgoing SMS text. See an outline of how to do that <a href="https://docs.communityhealthtoolkit.org/apps/reference/translations/#translations">here</a>.</p> -<h2 id="required-resources">Required Resources</h2> -<p>You should have a functioning <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/">CHT instance with <code>cht-conf</code> installed locally</a>, completed a <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/#3-create-and-upload-a-blank-project">project folder</a> setup, and an <a href="https://docs.communityhealthtoolkit.org/apps/reference/translations/">messages-sw.properties</a> file.</p> -<h2 id="implementation-steps">Implementation Steps</h2> -<p>Create a new file in the &rsquo;translations/&rsquo; folder called <code>messages-sw.properties</code>.</p> -<p>After an edit or addition of a translation, upload the current <code>messages-sw.properties</code> onto your local environment using the below command.</p> -<pre tabindex="0"><code>cht --url=https://medic:password@localhost --upload-custom-translations -</code></pre><h3 id="1-add-user-interface-label-translations">1. Add User Interface Label Translations</h3> -<p><em><strong>CHT Instance text</strong></em> - for non-admin users, this is the text that falls under <strong>Messages</strong>, <strong>Tasks</strong>, <strong>Reports</strong>, <strong>People</strong> and <strong>Targets</strong>. -To localize instance text to Swahili, change the default system language to <code>Swahili</code>.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="change-system-language.png"> -<img src="change-system-language.png"/> </a> -</figure> -<p>Go to App Management &gt; Display &gt; Languages &gt; Default Language(Change to Swahili)</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="click-language-name.png"> -<img src="click-language-name.png"/> </a> -</figure> -<p>To find out what the language code for Swahili is, Go to the list of language as illustrated in the screenshot, click <code>Kiswahili (Swahili)</code> to show the options dropdown and click <code>Edit Name</code> the code will be in the text box under <code>Language Code</code> on the popup.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="click-language-edit-name-popup.png"> -<img src="click-language-edit-name-popup.png"/> </a> -</figure> -<p>In our case, the language code is <code>sw</code>.</p> -<br clear="all"> -<p>Create a <code>message-sw.properties</code> file and use the instructions outlined <a href="https://docs.communityhealthtoolkit.org/apps/reference/translations/#translations">here</a> to learn the structure of a message-{language-code}.properties file.</p> -<p>Populate the <code>messages-sw.properties</code> file with the appropriate translation strigs and upload it using the below command:</p> -<pre tabindex="0"><code>cht --local upload-custom-translations -</code></pre><p>The default Swahili translations that come pre-added to CHT can be found <a href="https://github.com/medic/cht-core/blob/4.5.x/config/standard/translations/messages-sw.properties">here</a>.</p> -<p>After changing the instance language to Swahili, the various elements will behave like this:</p> -<p><em><strong>Messages</strong></em></p> -<p>In Messages, the time counter text and navigation text changes.</p> -<p>This is an example of Swahili localization.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="messages-tab-language-en.png"> -<img src="messages-tab-language-en.png"/> </a> -</figure> -<p><em>English text</em></p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="messages-tab-language-sw.png"> -<img src="messages-tab-language-sw.png"/> </a> -</figure> -<p><em>Swahili Translation</em></p> -<br clear="all"> -<p>To change the title of the tab from the default title of this <em>Messages</em> tab of <code>Jumbe</code> in Kiswahili to <code>Barua</code>, add or edit the below code in the <code>messages-sw.properties</code> file:</p> -<pre tabindex="0"><code>Messages = Jumbe -</code></pre><p>to</p> -<pre tabindex="0"><code>Messages = Barua -</code></pre><p><em><strong>Tasks</strong></em></p> -<p>Localize the task header by adding the appropriate translation in the <code>messages-sw.properties</code> file.</p> -<p>For example, to translate the below delivery task title to Swahili:</p> -<pre tabindex="0"><code>{ -name: &#39;anc-home-visit-delivery&#39;, -icon: &#39;icon-pregnancy&#39;, -title: &#39;task.anc.delivery.title&#39;, -</code></pre><p>Add the code below to the <code>messages-sw.properties</code> file</p> -<pre tabindex="0"><code>task.anc.delivery.title = Kazi ya Kujifungua -</code></pre><br clear="all"> -<figure class="right col-6 col-lg-8"><a href="localize-tasks-en.png"> -<img src="localize-tasks-en.png"/> </a> -</figure> -<p><em>Default English text</em></p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="localize-tasks-sw.png"> -<img src="localize-tasks-sw.png"/> </a> -</figure> -<p><em>Swahili translation</em></p> -<br clear="all"> -<p>To change the title of the tab from the default title of this <em>Tasks</em> tab of <code>Kazi</code> in Kiswahili to <code>Fanya Hizi</code>, add or edit the below code in the <code>messages-sw.properties</code> file:</p> -<pre tabindex="0"><code>Tasks = Kazi -</code></pre><p>to</p> -<pre tabindex="0"><code>Tasks = Fanya Hizi -</code></pre><p><em><strong>Reports</strong></em></p> -<p>Localize the report field names by adding the appropriate translation in the <code>messages-{language-code}.properties</code> file.</p> -<p>For example, to change the date of birth field to Swahili, in <code>messages-sw.properties</code> file, add this:</p> -<pre tabindex="0"><code>contact.type.date_of_birth = Siku ya Kuzaliwa -</code></pre><br clear="all"> -<figure class="right col-6 col-lg-8"><a href="localize-reports-en.png"> -<img src="localize-reports-en.png"/> </a> -</figure> -<p><em>Before</em></p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="localize-reports-sw.png"> -<img src="localize-reports-sw.png"/> </a> -</figure> -<p><em>Swahili Translation</em></p> -<br clear="all"> -<p>To change the title of the tab from the default title of this <em>Reports</em> tab of <code>Ripoti</code> in Kiswahili to <code>Ripoti hizi</code>, add or edit the below code in the <code>messages-sw.properties</code> file.</p> -<pre tabindex="0"><code>Reports = Ripoti hizi -</code></pre><p>to</p> -<pre tabindex="0"><code>Reports = Ripoti hizi -</code></pre><p>e.g</p> -<p><em><strong>People</strong></em></p> -<p>To localize the contact labels, add the appropriate translation in <code>messages-{language-code}.properties</code> file.</p> -<p>e.g to change the people name label translation from the default Swahili translation of <code>Watu</code> to <code>Watu wa hili hapa eneo</code>, in <code>messages-sw.properties</code> file, add this:</p> -<pre tabindex="0"><code>contact.type.person = Mtu wa hili hapa eneo -contact.type.person.plural = Watu wa hili hapa eneo -</code></pre><br clear="all"> -<figure class="right col-6 col-lg-8"><a href="people-translation-en.png"> -<img src="people-translation-en.png"/> </a> -</figure> -<p><em>Before</em></p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="people-translation-sw.png"> -<img src="people-translation-sw.png"/> </a> -</figure> -<p>*After</p> -<br clear="all"> -<p>To change the title of the tab from the default title of this <em>People</em> tab of <code>Wasiliani</code> in Kiswahili to <code>Watu</code>, add or edit the below code in the <code>messages-sw.properties</code> file.</p> -<pre tabindex="0"><code>People = Wasiliani -</code></pre><p>to</p> -<pre tabindex="0"><code>People = Watu -</code></pre><p><em><strong>Targets</strong></em></p> -<p>You can localize the names of the targets by adding the appropriate translation in the `messages-{language-code}.properties file.</p> -<p>For example, to add the <code>Growth Monitoring</code> target title in Swahili on the instance, add the appropriate translation in the <code>messages-sw.properties</code> file. For instance:</p> -<pre tabindex="0"><code>targets.growth_monitoring.title = Ufuatiliaji wa ukuaji -</code></pre><br clear="all"> -<figure class="right col-6 col-lg-8"><a href="growth-monitoring-before.png"> -<img src="growth-monitoring-before.png"/> </a> -</figure> -<p><em>Before adding the Swahili target label</em></p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="growth-monitoring-after.png"> -<img src="growth-monitoring-after.png"/> </a> -</figure> -<p><em>After adding the Swahili target label</em></p> -<br clear="all"> -<p>To change the title of the tab from the default title of this <em>Targets</em> tab of <code>Grafu</code> in Kiswahili to <code>Lengo</code>, add or edit the below code in the <code>messages-sw.properties</code> file.</p> -<pre tabindex="0"><code>Targets = Grafu -</code></pre><p>to</p> -<pre tabindex="0"><code>Targets = Lengo -</code></pre><h3 id="2-app-forms">2. App Forms</h3> -<p>To localize an app form to Swahili, open the appropriate xlsx of the form and add a <code>label::sw</code> column which has the translation for the text. This will work in the <code>Survey</code> sheet or the <code>choices</code> sheet.</p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-person-xls-form.png"> -<img src="new-person-xls-form.png"/> </a> -</figure> -<p><em>New Person app form XLS configuration</em></p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-person-form-english.png"> -<img src="new-person-form-english.png"/> </a> -</figure> -<p><em>Screenshot of default English translation</em></p> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="new-person-form-swahili.png"> -<img src="new-person-form-swahili.png"/> </a> -</figure> -<p><em>Screenshot of form after switching CHV language to Kiswahili</em></p> -<br clear="all"> -<p><em><strong>App Management - Admin Area</strong></em> -This will still remain in English even after changing the default language to Swahili.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -The CHV can also choose the language of their choice when they login for the first time. A popup appears where they can choose their preferred language. -</div> -<h3 id="3-outgoing-texts">3. Outgoing Texts</h3> -<p>These are the SMS notifications/replies that go to CHVs and Supervisors phones in projects that incorporate SMS workflows.</p> -<p>To change the language of outgoing texts to a particular CHV/Supervisor to Swahili:</p> -<p>First in the <code>app_settings.json</code>, when configuring replies, add Swahili(sw) under <code>locales</code>:</p> -<pre tabindex="0"><code>&#34;locales&#34;: [ -{ -&#34;code&#34;: &#34;en&#34;, -&#34;name&#34;: &#34;English&#34; -}, -{ -&#34;code&#34;: &#34;es&#34;, -&#34;name&#34;: &#34;Spanish&#34; -}, -{ -&#34;code&#34;: &#34;fr&#34;, -&#34;name&#34;: &#34;French&#34; -}, -{ -&#34;code&#34;: &#34;ne&#34;, -&#34;name&#34;: &#34;Nepali&#34; -}, -{ -&#34;code&#34;: &#34;sw&#34;, -&#34;name&#34;: &#34;Swahili&#34; -} -</code></pre><p>Set up the translation for the reply message:</p> -<pre tabindex="0"><code>&#34;messages&#34;: [{ -&#34;message&#34;: [{ -&#34;content&#34;: &#34;Thank you, visit for {{patient_name}} ({{patient_id}}) has been recorded.&#34;, -&#34;locale&#34;: &#34;en&#34; -}, -{ -&#34;content&#34;: &#34;Asante, kuhudhuria kwa {{patient_name}} ({{patient_id}} kumerekodiwa.&#34;, -&#34;locale&#34;: &#34;sw&#34; -} -], -&#34;event_type&#34;: &#34;report_accepted&#34;, -&#34;recipient&#34;: &#34;reporting_unit&#34; -} -</code></pre><br clear="all"> -<figure class="right col-6 col-lg-8"><a href="change-user-language.png"> -<img src="change-user-language.png"/> </a> -</figure> -<p>Change the CHVs language by following: App Management &gt; Users &gt; [Choose CHV username e.g chv_1] &gt; Language &gt; Pick Swahili:</p> -<br clear="all"> -<h3 id="translating-cht-to-another-language">Translating CHT to another language</h3> -<p>To translate CHT to a new language (we have English, French, Hindi, Nepali, Spanish, Swahili, and Indonesian already in CHT), follow the steps outlined <a href="https://docs.communityhealthtoolkit.org/core/overview/translations/">here</a> for reference.</p>Apps: Configuring CHT Application Graphicshttps://docs.communityhealthtoolkit.org/apps/tutorials/application-graphics/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/application-graphics/ -<div class="pageinfo pageinfo-primary"> -<p>This tutorial will take you through customising some graphical elements of CHT core.</p> -<p>You will cover site branding, partner logos, header tab icons, and app icons (used in tasks, targets, and contacts).</p> -</div> -<h2 id="required-resources">Required Resources</h2> -<p>You should have a functioning <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/">CHT instance with <code>cht-conf</code> installed locally</a> and completed a <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/#3-create-and-upload-a-blank-project">project folder</a> setup.</p> -<h2 id="implementation-steps">Implementation Steps</h2> -<h3 id="1-site-branding">1. Site branding</h3> -<p>You have the ability to modify the app title, logo, and favicon. For Progressive Web App installations you can also configure the desktop icon.</p> -<figure class="center col-6 col-lg-8"><a href="branding_elements.png"> -<img src="branding_elements.png"/> </a> -</figure> -<table> -<thead> -<tr> -<th>Variable</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td><strong>logo</strong></td> -<td>Should be less than 100KB, have a transparent background, have high contrast, and be horizontal in shape with a ratio of about 3.5:1. We recommend SVG or PNG image formats.</td> -</tr> -<tr> -<td><strong>favicon</strong></td> -<td>Ideally 32x32 pixels, simple, and very small in size. We recommend SVG or PNG image formats.</td> -</tr> -<tr> -<td><strong>icon</strong></td> -<td>Must be at least 192x192 pixels, square. We recommend SVG or PNG image formats.</td> -</tr> -</tbody> -</table> -<h4 id="using-cht-conf">Using cht-conf</h4> -<p>Create a <code>branding.json</code> file if it doesn&rsquo;t exist. (This may have already been created by the initialise-project-layout command). -Edit the file with the following content:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;title&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;My App Name&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;resources&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;logo&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;logo.png&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;favicon&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;favicon.ico&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;icon&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;pwa-icon.svg&#34;</span> <span style="color:#8f5902;font-style:italic">// required for PWA installation only -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>The folder and files structure would look like this:</p> -<pre tabindex="0"><code>./ -branding.json -/branding -logo.png -favicon.ico -pwa-icon.svg -</code></pre><p>Finally run the command: <code>cht --url=&lt;instance-url&gt; upload-branding</code></p> -<h4 id="using-the-admin-interface">Using the admin interface</h4> -<p>Log in to your instance and navigate to <code>Menu &gt; App Settings &gt; Images &gt; Icons</code></p> -<p><img src="menu.png" alt=""> -<img src="images.png" alt=""></p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -If your changes are not reflected on the browser, you will have to clear browser data to get rid of the already cached resources. -</div> -<h3 id="2-partner-logos">2. Partner logos</h3> -<p>If you would like to display a collection of logos representing all of the organizations and funders involved in a project, there is a space for these at the bottom of the About page. This page can be accessed through; <code>Menu &gt; About</code></p> -<figure class="center col-6 col-lg-8"><a href="about_page.png"> -<img src="about_page.png"/> </a> -</figure> -<h4 id="using-cht-conf-1">Using cht-conf</h4> -<p>Create a <code>partners.json</code> file if it doesn&rsquo;t exist. (This may have already been created by the initialise-project-layout command). -Edit the file with the following content:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;resources&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;partnerA&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;parnerA.png&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;partnerB&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;parnerB.png&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;partnerC&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;parnerC.png&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>The folder and files structure would look like this:</p> -<pre tabindex="0"><code>./ -partners.json -/partners -parnerA.png -parnerB.png -</code></pre><p>Finally run the command: <code>cht --url=&lt;instance-url&gt; upload-partners</code></p> -<h4 id="using-the-admin-interface-1">Using the admin interface</h4> -<p>Log in to your instance and navigate to <code>Menu &gt; App Settings &gt; Images &gt; Partners</code></p> -<p><img src="menu.png" alt=""> -<img src="partners.png" alt=""></p> -<h3 id="3-header-tab-icons">3. Header tab icons</h3> -<p>As of cht-core 3.10, app header tabs icons are configurable. CHT currently has five tabs: messages, tasks, reports, contacts, analytics.</p> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/header_tabs/">Header tabs</a></p> -<h4 id="using-cht-conf-2">Using cht-conf</h4> -<p>Create a <code>app_settings.json</code> file if it doesn&rsquo;t exist. (This may have already been created by the initialise-project-layout command).</p> -<p>We will then add a <code>header_tabs</code> key within app_settings with the following structure:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;messages&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;icon&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;fa-user&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;tasks&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;resource_icon&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;medic-health-center&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;analytics&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;icon&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;fa-flag&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;resource_icon&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;icon-treatment&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;reports&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;icon&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;fa-list-alt&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;contacts&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;icon&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;fa-user&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>The above assumes you have the following resource icons already in your instance (either uploaded or out-of-the-box): <code>medic-health-center</code>, <code>icon-treatment</code>.</p> -<p>Finally run the command: <code>cht --url=&lt;instance-url&gt; upload-app-settings</code> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/reference/resources/">Icons</a></p> -</p> -<h4 id="using-the-admin-interface-2">Using the admin interface</h4> -<p>Log in to your instance and navigate to <code>Menu &gt; App Settings &gt; Images &gt; Header Icons</code></p> -<p><img src="menu.png" alt=""> -<img src="header_icons.png" alt=""></p> -<h3 id="4-app-icons">4. App Icons</h3> -<p>Apps can be customised by defining the icons to use for tasks, targets, contacts or forms on the action bar.</p> -<p>Add icons to the <code>resources</code> folder, and include them by name in the <code>resources.json</code> file as the following example:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;icon-risk&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;icon-healthcare-warning@2x.png&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;icon-treatment&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;icon-healthcare-medicine@2x.png&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;medic-clinic&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;medic-family.svg&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;medic-district-hospital&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;medic-family.svg&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;medic-health-center&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;medic-chw-area.svg&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;medic-person&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;medic-person.svg&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/design/icons/">Icon Library</a></p> -<p>The folder and files structure would look like this:</p> -<pre tabindex="0"><code>./ -resources.json -/resources -icon-healthcare-warning@2x.png -icon-healthcare-medicine@2x.png -medic-family.svg -medic-family.svg -medic-chw-area.svg -medic-person.svg -</code></pre><p>Finally run the command: <code>cht --url=&lt;instance-url&gt; upload-resources</code></p> -<p>To modify the icon used in contacts, you will need to edit the icon subkey in app_settings.json (under contact_types). You will modify app_settings.json with the following contents:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#a40000">...</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;contact_types&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;person&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.person&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;group_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.person.plural&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;create_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.person.new&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;edit_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact.type.person.edit&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;primary_contact_key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;person.field.contact&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;icon&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;medic-person&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;create_form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;form:contact:person:create&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;edit_form&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;form:contact:person:edit&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#a40000">...</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>Finally run the command: <code>cht --local upload-app-settings</code></p> -<p>To customise the icons used in tasks or the action bar, you will need to edit a form properties file and add an icon property as outline in <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/form-properties/">form properties</a> tutorial.</p> -<p>To customise the icons used in targets, you will need to add an icon property in a target&rsquo;s definition as shown in the <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/targets/">targets</a> tutorial.</p>Apps: Application Testshttps://docs.communityhealthtoolkit.org/apps/tutorials/application-tests/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/application-tests/ -<div class="pageinfo pageinfo-primary"> -<p>This tutorial takes you through testing the various configurable components of CHT applications using <a href="http://docs.communityhealthtoolkit.org/cht-conf-test-harness/"><code>cht-conf-test-harness</code></a>.</p> -</div> -<h2 id="prerequisites">Prerequisites</h2> -<p>Complete the following tutorials:</p> -<ul> -<li><a href="https://docs.communityhealthtoolkit.org/apps/tutorials/app-forms/">Building App Forms</a></li> -<li><a href="https://docs.communityhealthtoolkit.org/apps/tutorials/tasks-1/">Building A Simple Task</a></li> -<li><a href="https://docs.communityhealthtoolkit.org/apps/tutorials/targets/">Building Target Widgets</a></li> -<li><a href="https://docs.communityhealthtoolkit.org/apps/tutorials/contact-summary/">Building Contact Summary</a></li> -</ul> -<h2 id="importance-of-testing-your-application">Importance of testing your application</h2> -<hr> -<p>Testing your CHT application is important as it ensures you are consistently maintaining your application and defining implementation requirements. Testing also helps the user make better architectural decisions, optimizes the forms, tasks and other components of the application. Although it may seem tedious, testing your application ensures that you quickly discover issues that are introduced with changes or newer commits.</p> -<h2 id="cht-application-testing">CHT Application Testing</h2> -<hr> -<p>CHT applications are greatly configurable. Depending on the number and complexity of app components, it can take a lot of time and effort to test the components manually. Some components, such as tasks, behave differently over time and are particularly challenging to test. As the project evolves, the configuration is often updated with new components and changes are made to the existing components. After each change, you need to test not only the new components, but also the old ones to make sure that the app works as expected. To facilitate the testing process, app builders are encouraged to write automated tests for their app using <a href="http://docs.communityhealthtoolkit.org/cht-conf-test-harness/">cht-conf-test-harness</a>.</p> -<p>Because it may be complicated to test with a real application, <code>cht-conf-test-harness</code> (also simply referred as <code>harness</code>), provides a platform that simulates the CHT application instance.</p> -<p>Using <code>cht-conf-test-harness</code>, you can write tests and run them with <a href="https://mochajs.org/">Mocha</a> testing framework to test the behavior of different components in a CHT application. Mocha works with a variety of assertion libraries including <a href="https://www.chaijs.com/">chai</a>, <a href="https://github.com/shouldjs/should.js">should.js</a>, <a href="https://github.com/LearnBoost/expect.js">expect.js</a>, <a href="https://github.com/visionmedia/better-assert">better-assert</a>, <a href="https://unexpected.js.org/">unexpected</a> among others.</p> -<h2 id="preparation">Preparation</h2> -<p>Writing tests for CHT apps requires a good understanding of the project workflows and requirements. To test using the harness, there are a few things you need to set up:</p> -<ol> -<li> -<p>From the previous tutorials, you should have a <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/">functioning CHT instance with <code>cht-conf</code> installed locally</a> and a <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/#3-create-and-upload-a-blank-project">project folder set up</a>. -<code>cht-conf</code> which is short for CHT app configurer, is a command-line interface tool used to manage and configure your CHT apps. It is used for backup, conversion, validation, uploading and other actions which can be found by running <code>cht --help</code>.</p> -</li> -<li> -<p>Ensure your <code>package.json</code> file has the required libraries. A <code>package.json</code> file is used to record important metadata about a project and defines functional attributes that npm uses to install dependencies and run scripts. This file should be at the root of your project folder. -If your <code>package.json</code> file does not already have them, add <code>cht-conf-test-harness</code>, <code>chai</code>, and <code>mocha</code> by running this in your command-line:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>npm install --save-dev cht-conf-test-harness chai mocha -</span></span></code></pre></div></li> -<li> -<p>Also, add the following scripts to <code>package.json</code>, if not already present:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#4e9a06">&#34;scripts&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;test&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;npm run compile-forms &amp;&amp; npm run compile-app-settings &amp;&amp; npm run unittest&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;compile-app-settings&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;npx cht --no-check compile-app-settings&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;compile-forms&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;npx cht --no-check convert-app-forms convert-contact-forms&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;unittest&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;mocha test/**/*.spec.js --timeout 20000&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>After adding these scripts, you are able to run the tests by running one of these commands from the command-line:</p> -<table> -<thead> -<tr> -<th>Command</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>npm run test</code></td> -<td>Compiles the app settings, converts the forms,then runs the tests (preferred solution).</td> -</tr> -<tr> -<td><code>npm run unittest</code></td> -<td>Only runs the unit tests.</td> -</tr> -</tbody> -</table> -<p>You can also run the test on a specific form by defining a test under the scripts section.</p> -<p>For example, to run the test on the <code>death.spec.js</code> file, define the test as <code>death-form-unit-test: &quot;mocha test/forms/death.spec.js --timeout 20000&quot;</code>. Then run:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span> npm run death-form-unit-test -</span></span></code></pre></div><p>npm also supports a shorter alias as <code>npm test</code>. There are recommended conventions on how to arrange tests by splitting into unit tests and integration tests so that each of them can be run independently. This can be best illustrated by <a href="https://github.com/medic/cht-pih-malawi-app">this project code</a>.</p> -</li> -<li> -<p>Create a folder in the project root where you keep the tests. You can name the folder yourself. In this case it is named &rsquo;test&rsquo;. -<figure class="col-9 col-lg-12"><a href="test-folder-placement.png"> -<img src="test-folder-placement.png"/> </a> -</figure> -</p> -</li> -<li> -<p>Create a file <code>harness.defaults.json</code> at the root of your project. This is the default configuration file for the harness. Here you can specify the default user, preloaded contacts and reports, and add other settings. Here&rsquo;s an <a href="https://github.com/medic/cht-conf-test-harness/blob/master/harness.defaults.json.example">example</a> file to get you started. You can read more about it <a href="https://docs.communityhealthtoolkit.org/cht-conf-test-harness/global.html#HarnessInputs">here</a>. -<figure class="col-9 col-lg-12"><a href="harness-json-file-placement.png"> -<img src="harness-json-file-placement.png"/> </a> -</figure> -</p> -</li> -</ol> -<h2 id="writing-a-test-for-cht-app">Writing a test for CHT App</h2> -<p>Start by adding a file where a group of related tests are written. For example, in the default config, all tests for the pregnancy form are in the <a href="https://github.com/medic/cht-core/blob/master/config/default/test/forms/pregnancy.spec.js">pregnancy.spec.js</a> file. The common naming pattern for CHT applications test files is <code>filename.spec.js</code>. In JavaScript testing, <code>spec</code> is short for specifications which refers to technical details of a given application, that must be fulfilled.</p> -<p>Let&rsquo;s introduce some important sections from a typical test file:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">TestRunner</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">require</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;cht-conf-test-harness&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">harness</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">new</span> <span style="color:#000">TestRunner</span><span style="color:#000;font-weight:bold">();</span> -</span></span></code></pre></div><p>In the example above, we define variables <code>TestRunner</code> and <code>harness</code>. <code>TestRunner</code> imports the Harness class and <code>harness</code> creates an instance of the class that is used throughout the test file.</p> -<p>You can also pass parameters to the <code>TestRunner()</code> when instantiating:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">// For detailed console logs -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">harness</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">new</span> <span style="color:#000">TestRunner</span><span style="color:#000;font-weight:bold">({</span> <span style="color:#000">verbose</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> <span style="color:#000;font-weight:bold">});</span> -</span></span></code></pre></div><p>Other useful options are:</p> -<ol> -<li><code>{ headless: false }</code> - Passed to <a href="https://developers.google.com/web/tools/puppeteer/get-started#default_runtime_settings">puppeteer</a>, launches browser with GUI. Helpful to see how the form is getting filled.</li> -<li><code>{ harnessDataPath: 'harness.clinic.json'}</code>: Specify different harness configuration</li> -</ol> -<p>You can find more harness options and examples <a href="https://docs.communityhealthtoolkit.org/cht-conf-test-harness/Harness.html">here</a>.</p> -<p>You need to start the harness before running tests and stop it after the use. To handle start and stop of harness, place following statements in <code>before()</code> and <code>after()</code> hooks of the test suite.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#000">describe</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;PNC form tests&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">before</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">async</span> <span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">start</span><span style="color:#000;font-weight:bold">();</span> <span style="color:#000;font-weight:bold">});</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">after</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">async</span> <span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">stop</span><span style="color:#000;font-weight:bold">();</span> <span style="color:#000;font-weight:bold">});</span> -</span></span></code></pre></div><p>Any logic that you want to execute before and after each test can be placed in the <code>beforeEach()</code> and <code>afterEach()</code> hooks. This section ensures that the harness is reset to default before or after each run.</p> -<p>In the example below, the logic is to clear the state of the harness and set the date to &lsquo;2000-01-01&rsquo; before each test run:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span> <span style="color:#000">beforeEach</span><span style="color:#000;font-weight:bold">(</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">async</span> <span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">clear</span><span style="color:#000;font-weight:bold">();</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">setNow</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;2000-01-01&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">});</span> -</span></span></code></pre></div><p>After each test run, assert that there are no errors in the console.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span> <span style="color:#000">afterEach</span><span style="color:#000;font-weight:bold">(()</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">consoleErrors</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">be</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">empty</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">});</span> -</span></span></code></pre></div><p>If you want to learn more about these hooks, refer to this <a href="https://mochajs.org/#hooks">Mocha resource</a>. Feel free to customize the hooks as you see fit.</p> -<p>Let&rsquo;s look at a more detailed example. <a name="assessment-form-test">Here</a> is a test case for the Assessment form that was covered in the <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/app-forms/">previous tutorial</a>:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span> <span style="color:#000">it</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;unit test confirming assessment with cough since 7 days&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">async</span> <span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// Load the assessment form and fill &#39;yes&#39; on the first page and &#39;7&#39; on the second page -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">result</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">fillForm</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;assessment&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;yes&#39;</span><span style="color:#000;font-weight:bold">],</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;7&#39;</span><span style="color:#000;font-weight:bold">]);</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// Verify that the form successfully got submitted -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">result</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">errors</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">be</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">empty</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// Verify some attributes on the resulting report -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">result</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">report</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">fields</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">deep</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">include</span><span style="color:#000;font-weight:bold">({</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">patient_name</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;Patient Name&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">group_assessment</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">cough</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;yes&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">cough_duration</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;7&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">});</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">});</span> -</span></span></code></pre></div><p>In <a href="#assessment-form-test-3">line 4</a> above, the method <a href="https://docs.communityhealthtoolkit.org/cht-conf-test-harness/Harness.html#fillForm">harness.fillForm()</a> fills the specified form with the given input (answers).</p> -<p>Depending on the form design, the number of inputs to be filled can be large. The inputs are often repeated within a single test or across multiple tests with little or no variation. It is a good idea to keep them in a separate file and refer them from the tests as required. You can also introduce some variations in the inputs using function parameters.</p> -<p>Example: <code>form-inputs.js</code></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#000">module</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">exports</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">assessments</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">no_cough</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;no&#39;</span><span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">cough</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">days</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;yes&#39;</span><span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">[</span> <span style="color:#000">days</span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">};</span> -</span></span></code></pre></div><p>In the test below, the inputs are used from the <code>form-inputs.js</code> file above:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">assessments</span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">require</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;../form-inputs&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">...</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">result</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">fillForm</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;assessment&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000;font-weight:bold">...</span><span style="color:#000">assessments</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">cough</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;3&#39;</span><span style="color:#000;font-weight:bold">));</span> -</span></span></code></pre></div><p>The test files are usually grouped in folders to read and run them easily. One way of grouping them is by creating folders for each of the components that can be tested: forms, contact summary, tasks, and targets.</p> -<p><img src="tests-dir.png" alt="alt_text" title="Test directory structure"></p> -<h3 id="testing-forms">Testing Forms</h3> -<table> -<thead> -<tr> -<th>What do you test?</th> -<th></th> -</tr> -</thead> -<tbody> -<tr> -<td>Minimum:</td> -<td>Filling a form with the most common options should not result in any error.</td> -</tr> -<tr> -<td>Ideal:</td> -<td>All input combinations and constraints are tested with the report fields.</td> -</tr> -</tbody> -</table> -<p><a href="#assessment-form-test">Previous example</a> demonstrates test for the app form. More test cases can be added by changing the inputs.</p> -<p>You can also test contact forms using test harness. To fill a contact form, use <code><a href="https://docs.communityhealthtoolkit.org/cht-conf-test-harness/Harness.html#fillContactForm">fillContactForm(contactType, …answers)</a></code>.</p> -<p>Example:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">result</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">fillContactForm</span><span style="color:#000;font-weight:bold">(</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#39;district_hospital&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;new_person&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;William Mars&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;1990-08-06&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;+1234567891&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;female&#39;</span><span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;yes&#39;</span><span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span><span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">result</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">errors</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">be</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">empty</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">result</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contacts</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">excluding</span><span style="color:#000;font-weight:bold">([</span><span style="color:#4e9a06">&#39;_id&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;meta&#39;</span><span style="color:#000;font-weight:bold">]).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">deep</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">eq</span><span style="color:#000;font-weight:bold">([</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">...</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">]);</span> -</span></span></code></pre></div><p>When filling a form, you can also test the field constraints. The example below asserts that a form does not accept birth date in the future:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#000">it</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">`Throws validation error when birth date is in future`</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">async</span> <span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">result</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">fillForm</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;delivery&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;yes&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;2090-01-02&#39;</span><span style="color:#000;font-weight:bold">]);</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">result</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">errors</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">length</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">equal</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">result</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">errors</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">]).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">include</span><span style="color:#000;font-weight:bold">({</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">msg</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;Date cannot be in the future!&#39;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">});</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">});</span> -</span></span></code></pre></div><blockquote> -<p>Note: If a form triggers a task, some use cases of the form can be tested when testing the task later.</p> -</blockquote> -<hr> -<h3 id="testing-contact-summary">Testing Contact Summary</h3> -<table> -<thead> -<tr> -<th>What do you test?</th> -<th></th> -</tr> -</thead> -<tbody> -<tr> -<td>Minimum:</td> -<td>Fill a contact form and count the number of fields in the contact summary</td> -</tr> -<tr> -<td>Ideal:</td> -<td>Targeted tests for calculations of context, fields, cards, etc.</td> -</tr> -</tbody> -</table> -<p>Contact summary consists of visible components such as <a href="https://docs.communityhealthtoolkit.org/apps/reference/contact-page/#condition-cards">cards</a>, <a href="https://docs.communityhealthtoolkit.org/apps/reference/contact-page/#contact-summary">fields</a> and a hidden component: <a href="https://docs.communityhealthtoolkit.org/apps/reference/contact-page/#care-guides">context</a>. All these can be tested with the test harness.</p> -<p>Use <a href="https://docs.communityhealthtoolkit.org/cht-conf-test-harness/Harness.html#getContactSummary">harness.getContactSummary()</a> method to get the <a href="https://docs.communityhealthtoolkit.org/cht-conf-test-harness/global.html#ContactSummary">ContactSummary </a>object, which has these properties: <code>fields</code>, <code>cards</code>, and <code>context</code>.</p> -<p>To test the contact summary fields added in the <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/contact-summary/#3-export-fields">previous tutorial</a>, use the following test case:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">contactSummary</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getContactSummary</span><span style="color:#000;font-weight:bold">();</span> -</span></span><span style="display:flex;"><span><span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">contactSummary</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">fields</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">have</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">property</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;length&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">5</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span><span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">contactSummary</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">fields</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">filter</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">f</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000">f</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">filter</span> <span style="color:#ce5c00;font-weight:bold">!==</span> <span style="color:#4e9a06">&#39;lineage&#39;</span><span style="color:#000;font-weight:bold">)).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">deep</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">equal</span><span style="color:#000;font-weight:bold">(</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;patient_id&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;patient_id&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">width</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">4</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.age&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;1970-07-09&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">width</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">filter</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;age&#39;</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.sex&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.sex.female&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">translate</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">width</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">4</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;person.field.phone&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">undefined</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">width</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">4</span><span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">);</span> -</span></span></code></pre></div><p>Here, the contact summary being tested represents the contact that is being &ldquo;acted on&rdquo; or the &ldquo;subject of the test&rdquo;. To learn more about this, look at the <code>subject</code> property <a href="https://docs.communityhealthtoolkit.org/cht-conf-test-harness/global.html#HarnessInputs">here</a>.</p> -<p>Similarly, you can test the condition cards too. Here is an example for testing the assessment condition card added in this <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/condition-cards/#2-define-cards-and-add-a-condition-card-object">tutorial</a>:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">// Load the assessment form and fill in &#39;yes&#39; on the first page and &#39;7&#39; on the second page -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">result</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">fillForm</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;assessment&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;yes&#39;</span><span style="color:#000;font-weight:bold">],</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;7&#39;</span><span style="color:#000;font-weight:bold">]);</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">// Verify that the form successfully got submitted -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span><span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">result</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">errors</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">be</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">empty</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">contactSummary</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getContactSummary</span><span style="color:#000;font-weight:bold">();</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">// Verify that there is one card -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span><span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">contactSummary</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">cards</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">have</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">property</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;length&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">// Verify the fields on the card -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span><span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">contactSummary</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">cards</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">].</span><span style="color:#000">fields</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">deep</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">equal</span><span style="color:#000;font-weight:bold">(</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.profile.most_recent_assessment.date&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">result</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">report</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reported_date</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">width</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">6</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">filter</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;simpleDate&#39;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">label</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contact.profile.cough&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">value</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;yes&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">width</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">6</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">);</span> -</span></span></code></pre></div><p>If you follow <a href="https://docs.communityhealthtoolkit.org/apps/reference/contact-page/#code-samples">this code sample</a> to create the pregnancy condition card, the pregnancy context can be tested this way:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">summaryContext</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getContactSummary</span><span style="color:#000;font-weight:bold">().</span><span style="color:#000">context</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">summaryContext</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">include</span><span style="color:#000;font-weight:bold">({</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">pregnant</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">});</span> -</span></span></code></pre></div><hr> -<h3 id="testing-tasks">Testing Tasks</h3> -<table> -<thead> -<tr> -<th>What do you test?</th> -<th></th> -</tr> -</thead> -<tbody> -<tr> -<td>Minimum:</td> -<td>Trigger and resolve the task</td> -</tr> -<tr> -<td>Ideal:</td> -<td>One test for each use scenario<br/>Code coverage for any arc with an external dependency<br/>Negative cases - confirm tasks don’t trigger</td> -</tr> -</tbody> -</table> -<p>When testing the tasks <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/tasks-1/#3-testing-the-task">manually</a>, you need to fill a form. Then to see the task, you need to either change the system date to move forward in time or change the reported date of the document accordingly. These are very tedious and unreliable methods. Using the test harness, you can quickly test the tasks under different scenarios and at different simulated dates.</p> -<p>Every task has a source document (contact or report). When testing, you can mock the source document. Yet, the recommended approach is to fill the source form and then proceed with checking the associated tasks.</p> -<p>Commonly used harness methods when testing tasks are:</p> -<ul> -<li> -<p><a href="https://docs.communityhealthtoolkit.org/cht-conf-test-harness/Harness.html#setNow"><code>harness.setNow()</code></a>: Set the current mock-time of the harness.</p> -</li> -<li> -<p><a href="https://docs.communityhealthtoolkit.org/cht-conf-test-harness/Harness.html#flush"><code>harness.flush()</code></a>: Increment the current time by an amount</p> -</li> -<li> -<p><a href="https://docs.communityhealthtoolkit.org/cht-conf-test-harness/Harness.html#getTasks"><code>harness.getTasks()</code></a>: Check which tasks are visible</p> -</li> -<li> -<p><a href="https://docs.communityhealthtoolkit.org/cht-conf-test-harness/Harness.html#loadAction"><code>harness.loadAction()</code></a>: Simulates the user clicking on an action</p> -</li> -</ul> -<p>With <code>getTasks()</code>, you get an array of <a href="https://docs.communityhealthtoolkit.org/cht-conf-test-harness/global.html#Task"><code>Task</code></a> objects which corresponds to the <a href="https://docs.communityhealthtoolkit.org/core/overview/db-schema/#tasks">tasks schema</a>.</p> -<p>Let&rsquo;s look back at the simple task from this tutorial: <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/tasks-1/">Building A Simple Task</a>.</p> -<p>According to the task configuration, these conditions need to be met for the assessment task to be visible:</p> -<ol> -<li>The <code>contact_type</code> of the subject (patient) is: <code>patient</code></li> -<li>The <code>contact_type</code> of the user&rsquo;s parent is: <code>chw_area</code></li> -<li>The current time is between the start and end dates of the task event.</li> -</ol> -<p>When testing with harness, the conditions 1 and 2 above can be set in the <code>harness.defaults.json</code> file. See the lines <a href="#harness-defaults-json-31">31</a> and <a href="#harness-defaults-json-26">26</a> respectively in the sample file further below.</p> -<p>For the condition 3, let&rsquo;s look at the task event window in the task definition. As mentioned in the earlier tutorial, the task event is:</p> -<ul> -<li>Due 7 days after the contact’s creation date.</li> -<li>Should appear 7 days before the due date, or immediately when the contact is created.</li> -<li>Should disappear the day after the due date.</li> -</ul> -<p>So, to see the task, the contact should be created within the last 7 days. You can easily set the contact&rsquo;s reported date so that it falls within the last 7 days (see line <a href="#harness-defaults-json-36">36</a> below).</p> -<p><strong>harness.defaults.json</strong>: -<div class="highlight"><div style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"> -<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"> -<pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-1"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-1"> 1</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-2"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-2"> 2</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-3"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-3"> 3</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-4"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-4"> 4</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-5"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-5"> 5</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-6"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-6"> 6</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-7"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-7"> 7</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-8"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-8"> 8</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-9"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-9"> 9</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-10"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-10">10</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-11"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-11">11</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-12"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-12">12</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-13"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-13">13</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-14"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-14">14</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-15"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-15">15</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-16"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-16">16</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-17"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-17">17</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-18"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-18">18</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-19"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-19">19</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-20"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-20">20</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-21"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-21">21</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-22"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-22">22</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-23"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-23">23</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-24"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-24">24</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-25"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-25">25</a> -</span><span style="background-color:#dfdfdf"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-26"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-26">26</a> -</span></span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-27"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-27">27</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-28"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-28">28</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-29"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-29">29</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-30"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-30">30</a> -</span><span style="background-color:#dfdfdf"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-31"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-31">31</a> -</span></span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-32"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-32">32</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-33"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-33">33</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-34"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-34">34</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-35"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-35">35</a> -</span><span style="background-color:#dfdfdf"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-36"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-36">36</a> -</span></span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-37"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-37">37</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-38"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-38">38</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-39"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-39">39</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-40"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-40">40</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-41"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-41">41</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-42"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-42">42</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-43"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-43">43</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-44"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-44">44</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-45"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-45">45</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-46"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-46">46</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-47"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-47">47</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-48"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-48">48</a> -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f" id="harness-defaults-json-49"><a style="outline:none;text-decoration:none;color:inherit" href="#harness-defaults-json-49">49</a> -</span></code></pre></td> -<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"> -<pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;coreVersion&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;3.14.0&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;user&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;chw_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;subject&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;patient_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;ownedBySubject&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;docs&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;chw_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;CHW&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;date_of_birth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;1971-03-10&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;phone&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;+17782475555&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;reported_date&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1550559625153</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;person&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;chw_area_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;district_id&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;chw_area_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex; background-color:#dfdfdf"><span> <span style="color:#204a87;font-weight:bold">&#34;contact_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;chw_area&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;patient_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;contact&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex; background-color:#dfdfdf"><span> <span style="color:#204a87;font-weight:bold">&#34;contact_type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;patient&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Patient Name&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;role&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;patient&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;date_of_birth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;1970-07-09&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;sex&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;female&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex; background-color:#dfdfdf"><span> <span style="color:#204a87;font-weight:bold">&#34;reported_date&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1652868491000</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;patient_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;patient_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;patient_parent_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;chw_area_id&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parent&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;district_id&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span></span></span></code></pre></td></tr></table> -</div> -</div></p> -<p>Note that the <code>reported_date</code> above stores the epoch timestamp in milliseconds when the document was first created. You can use external website <a href="https://www.epochconverter.com/">epochconverter</a> to convert the timestamp to and from a human readable date.</p> -<p>After setting the harness defaults, you can now test the task:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">// Get the task by title -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">tasks</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getTasks</span><span style="color:#000;font-weight:bold">({</span><span style="color:#000">title</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;First Assessment&#39;</span><span style="color:#000;font-weight:bold">});</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">//Make sure that you have the task -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span><span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">tasks</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">have</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">property</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;length&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">// Load the task action and fill it. -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">result</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">loadAction</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">tasks</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">],</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;yes&#39;</span><span style="color:#000;font-weight:bold">],</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;7&#39;</span><span style="color:#000;font-weight:bold">]);</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">// Verify that the form successfully got submitted -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span><span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">result</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">errors</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">be</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">empty</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">// Verify that the task is no longer visible -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span><span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getTasks</span><span style="color:#000;font-weight:bold">()).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">be</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">empty</span><span style="color:#000;font-weight:bold">;</span> -</span></span></code></pre></div><p>If a task is triggered by a report, then fill in the app form to create report before test:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#000">it</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;followup schedule&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">async</span> <span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">result</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">fillForm</span><span style="color:#000;font-weight:bold">(...</span><span style="color:#000">pncFollowups</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">registerBirth</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;2000-01-01&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;2000-02-01&#39;</span><span style="color:#000;font-weight:bold">));</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">result</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">errors</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">be</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">empty</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">/* Task should appear 7 days after birth for follow up */</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">flush</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">7</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">tasks</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getTasks</span><span style="color:#000;font-weight:bold">();</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">tasks</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">have</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">property</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;length&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">/* Complete the task, task should disappear */</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">loadAction</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">tasks</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">].</span><span style="color:#000">emission</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">actions</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">]);</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">followupResult</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">fillForm</span><span style="color:#000;font-weight:bold">(...</span><span style="color:#000">pncFollowups</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">followup</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">healthy</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">followupResult</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">errors</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">be</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">empty</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getTasks</span><span style="color:#000;font-weight:bold">()).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">be</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">empty</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">/* Confirm a task appears again on the schedule */</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">flush</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">21</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getTasks</span><span style="color:#000;font-weight:bold">()).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">have</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">property</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;length&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">});</span> -</span></span></code></pre></div><p>You may <a href="https://docs.communityhealthtoolkit.org/apps/guides/tasks/pass-data-to-form/">pass other data</a> from a task to the action form using <a href="https://docs.communityhealthtoolkit.org/apps/guides/tasks/pass-data-to-form/#modifycontent">modifyContent </a>attribute of a task. You can also verify that these data are present in the task.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span> <span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">tasks</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">].</span><span style="color:#000">emission</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">actions</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">].</span><span style="color:#000">content</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">include</span><span style="color:#000;font-weight:bold">({</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">t_follow_up_count</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;5&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">t_delivery_date</span><span style="color:#ce5c00;font-weight:bold">:</span><span style="color:#4e9a06">&#39;2000-01-01&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">});</span> -</span></span></code></pre></div><hr> -<h3 id="testing-targets">Testing Targets</h3> -<p>Testing a target is relatively straightforward. Add a report or contact that increments a target, then check the target values.</p> -<table> -<thead> -<tr> -<th>What do you test?</th> -<th></th> -</tr> -</thead> -<tbody> -<tr> -<td>Minimum:</td> -<td>Trigger incrementing of the target<br/>Ensure target doesn’t increment when it shouldn’t</td> -</tr> -<tr> -<td>Ideal:</td> -<td>One test for each user scenario<br/>Ensure proper deduplication (particularly for those with emitCustom)</td> -</tr> -</tbody> -</table> -<p>Use <a href="https://docs.communityhealthtoolkit.org/cht-conf-test-harness/Harness.html#getTargets"><code>harness.getTargets</code></a> to check the state of targets. It returns a <a href="https://docs.communityhealthtoolkit.org/cht-conf-test-harness/global.html#Target"><code>Target</code></a> object which corresponds to the <a href="https://docs.communityhealthtoolkit.org/core/overview/db-schema/#targets">targets schema</a>.</p> -<p>To test the first two targets created in the <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/targets/">targets tutorial</a>, use this code:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#000">it</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;assessment this month and all time assessments should show correct counts&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">async</span> <span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">//set the current date -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">setNow</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;2000-01-30&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">//assessment form filled -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">result</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">fillForm</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;assessment&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;yes&#39;</span><span style="color:#000;font-weight:bold">],</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;7&#39;</span><span style="color:#000;font-weight:bold">]);</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">result</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">errors</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">be</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">empty</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">thisMonth</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getTargets</span><span style="color:#000;font-weight:bold">({</span> <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;assessments-this-month&#39;</span> <span style="color:#000;font-weight:bold">});</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">thisMonth</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">have</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">property</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;length&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">//The number of assessments in this month should be 1 -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">thisMonth</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">]).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">nested</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">include</span><span style="color:#000;font-weight:bold">({</span> <span style="color:#4e9a06">&#39;value.pass&#39;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;value.total&#39;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span> <span style="color:#000;font-weight:bold">});</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">let</span> <span style="color:#000">allTime</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getTargets</span><span style="color:#000;font-weight:bold">({</span> <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;assessments-all-time&#39;</span> <span style="color:#000;font-weight:bold">});</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">thisMonth</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">have</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">property</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;length&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">//The number of all time assessments should be 1 -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">thisMonth</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">]).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">nested</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">include</span><span style="color:#000;font-weight:bold">({</span> <span style="color:#4e9a06">&#39;value.pass&#39;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;value.total&#39;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span> <span style="color:#000;font-weight:bold">});</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">//Go to next month by adding 2 days (Jan 30 to Feb 1) -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">flush</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">2</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">nextMonth</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getTargets</span><span style="color:#000;font-weight:bold">({</span> <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;assessments-this-month&#39;</span> <span style="color:#000;font-weight:bold">});</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">nextMonth</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">have</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">property</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;length&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">//The number of assessments this month now should be reset to 0 -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">nextMonth</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">]).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">nested</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">include</span><span style="color:#000;font-weight:bold">({</span> <span style="color:#4e9a06">&#39;value.pass&#39;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;value.total&#39;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">0</span> <span style="color:#000;font-weight:bold">});</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#000">allTime</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">harness</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getTargets</span><span style="color:#000;font-weight:bold">({</span> <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;assessments-all-time&#39;</span> <span style="color:#000;font-weight:bold">});</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">allTime</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">have</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">property</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;length&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">//The number of all time assessments should still be 1 -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">allTime</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">]).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">nested</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">include</span><span style="color:#000;font-weight:bold">({</span> <span style="color:#4e9a06">&#39;value.pass&#39;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;value.total&#39;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span> <span style="color:#000;font-weight:bold">});</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">});</span> -</span></span></code></pre></div><p>For targets with <code>type: 'percent'</code>, you might want to check for more properties:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span> <span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">targets</span><span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">]).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">nested</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">include</span><span style="color:#000;font-weight:bold">({</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#39;value.pass&#39;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#39;value.total&#39;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#39;value.percent&#39;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">100</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">});</span> -</span></span></code></pre></div><hr> -<h3 id="tests-for-helper-functions">Tests for helper functions</h3> -<p>If you have added <a href="https://docs.communityhealthtoolkit.org/apps/reference/tasks/#tasks-with-functions">helper functions</a>, they can be tested separately.</p> -<p>Example:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span> <span style="color:#000">describe</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;toISODateString&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">toDateString</span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">noolsExtras</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#000">it</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;parses a valid date&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">actual</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">toISODateString</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;01/01/2000&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">expect</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">actual</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">to</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">eq</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;2000-01-01&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">});</span> -</span></span></code></pre></div><h3 id="tests-for-bug-fixes">Tests for bug fixes</h3> -<p>When fixing a bug, write a test which captures the specific scenario being fixed.</p> -<ul> -<li>At least one test should fail before the fix</li> -<li>It should pass after the fix</li> -</ul> -<p>This prevents the regression and helps ensure that you&rsquo;re fixing what you expect.</p> -<h3 id="tips-for-writing-tests">Tips for writing tests</h3> -<ul> -<li>Every test needs to add value</li> -<li>Use the scientific method: your test is a hypothesis and you’re running an experiment</li> -<li>Follow these steps: -<ol> -<li>Arrange - Prepare the experiment</li> -<li>Act - Run the experiment. Take a measurement</li> -<li>Assert - Assert the hypothesis</li> -</ol> -</li> -<li>Structure testing inputs into &ldquo;classes&rdquo;: if one member in a class works well then the class is considered to work</li> -<li>Run mocha with <code>--grep</code> to only run tests that match a given string</li> -<li>Debug tests and CHT components using breakpoints and debug console.</li> -</ul> -<p>For debugging the unit tests in VS Code, here is a sample configuration file (<code>launch.json</code>):</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;version&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;0.2.0&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;configurations&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;node&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;request&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;launch&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Run Mocha Tests&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;program&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;${workspaceFolder}/node_modules/mocha/bin/_mocha&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;args&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;--timeout&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;999999&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;--colors&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;${workspaceFolder}/test/tasks/assessment.spec.js&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;--grep&#34;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#34;should show&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;--dev&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;internalConsoleOptions&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;openOnSessionStart&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div>Apps: CHT User Management toolhttps://docs.communityhealthtoolkit.org/apps/tutorials/user-management-tool/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/user-management-tool/ -<p>The Community Health Toolkit (CHT) is highly configurable and can be customized to support multiple hierarchies and users in the health care system. The CHT user management tool is a user friendly web application that works with the CHT to decentralize the user management process to the subnational levels, increasing efficiency and accuracy. This guide highlights steps for setting up and configuring the user management tool. -The guide has been tailored for specific CHT-supported national community health information systems. Partners and ministries of health are advised to customize the tool to fit their specific needs. -The tool provides templates that can be downloaded and populated.</p> -<p>The user management tool has various features and fuctionalities including:</p> -<ul> -<li>Creation of single or bulk (many) users.</li> -<li>Replacing of existing users</li> -<li>Moving of an existing place -<ul> -<li>Users can access bulk user templates that they can populate.</li> -<li>One can validate the entries before the upload.</li> -<li>Download the user credentials upon upload.</li> -</ul> -</li> -<li>Validation of input for completeness and accuracy. The tool identifies errors that the can be corrected.</li> -</ul> -<h1 id="configuring-the-cht-user-management-tool">Configuring the CHT User Management tool</h1> -<h2 id="implementation-steps">Implementation Steps</h2> -<ol> -<li> -<p>Clone the <code>cht-user-management</code> repository.</p> -</li> -<li> -<p>Create a new folder under <code>src/config</code> with your required instance eg. chis-znz</p> -</li> -<li> -<p>Within that new folder create a <code>config.json</code> file with the following parameters</p> -<table> -<thead> -<tr> -<th>Property</th> -<th>Type</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>domains</code></td> -<td>Array</td> -<td>Controls the list of instances which the user can login to</td> -</tr> -<tr> -<td><code>domains.friendly</code></td> -<td>string</td> -<td>Friendly name for the instance (eg. &ldquo;Migori&rdquo;)</td> -</tr> -<tr> -<td><code>domains.domain</code></td> -<td>string</td> -<td>Hostname for the instance (eg. &ldquo;migori-echis.health.go.ke&rdquo;)</td> -</tr> -<tr> -<td><code>domains.useHttp</code></td> -<td>boolean</td> -<td>Whether to make an insecure connection (http) to the hostname (defaults to false)</td> -</tr> -<tr> -<td><code>contact_types</code></td> -<td>Array</td> -<td>One element for each type of user which can be created by the system</td> -</tr> -<tr> -<td><code>contact_types.name</code></td> -<td>string</td> -<td>The name of the contact_type as it <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/hierarchy/">appears in the app&rsquo;s base_settings.json</a></td> -</tr> -<tr> -<td><code>contact_types.friendly</code></td> -<td>string</td> -<td>Friendly name of the contact type</td> -</tr> -<tr> -<td><code>contact_types.contact_type</code></td> -<td>string</td> -<td>The contact_type of the primary contact. <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/hierarchy/">As defined in base_settings.json</a></td> -</tr> -<tr> -<td><code>contact_types.contact_friendly</code></td> -<td>string</td> -<td>Friendly name of the primary contact type</td> -</tr> -<tr> -<td><code>contact_types.user_role</code></td> -<td>string[]</td> -<td>A list of allowed <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-roles/">user roles</a>. If only one is provided, it will be used by default.</td> -</tr> -<tr> -<td><code>contact_types.username_from_place</code></td> -<td>boolean</td> -<td>When true, the username is generated from the place&rsquo;s name. When false, the username is generated from the primary contact&rsquo;s name. Default is false.</td> -</tr> -<tr> -<td><code>contact_types.hierarchy</code></td> -<td>Array<ConfigProperty></td> -<td>Defines how this <code>contact_type</code> is connected into the hierarchy. An element with <code>level:1</code> (parent) is required and additional elements can be provided to support disambiguation. See <a href="#ConfigProperty">ConfigProperty</a>.</td> -</tr> -<tr> -<td><code>contact_types.hierarchy.level</code></td> -<td>integer</td> -<td>The hierarchy element with <code>level:1</code> is the parent, <code>level:3</code> is the great grandparent.</td> -</tr> -<tr> -<td><code>contact_types.replacement_property</code></td> -<td>Property</td> -<td>Defines how this <code>contact_type</code> is described when being replaced. The <code>property_name</code> is always <code>replacement</code>. See <a href="#ConfigProperty">ConfigProperty</a>.</td> -</tr> -<tr> -<td><code>contact_types.place_properties</code></td> -<td>Array<ConfigProperty></td> -<td>Defines the attributes which are collected and set on the user&rsquo;s created place. See <a href="#ConfigProperty">ConfigProperty</a>.</td> -</tr> -<tr> -<td><code>contact_types.contact_properties</code></td> -<td>Array<ConfigProperty></td> -<td>Defines the attributes which are collected and set on the user&rsquo;s primary contact doc. See <a href="#ConfigProperty">ConfigProperty</a>.</td> -</tr> -<tr> -<td><code>contact_types.deactivate_users_on_replace</code></td> -<td>boolean</td> -<td>Controls what should happen to the defunct contact and user documents when a user is replaced. When <code>false</code>, the contact and user account will be deleted. When <code>true</code>, the contact will be unaltered and the user account will be assigned the role <code>deactivated</code>. This allows for account restoration.</td> -</tr> -<tr> -<td><code>logoBase64</code></td> -<td>Image in base64</td> -<td>Logo image for your project</td> -</tr> -</tbody> -</table> -</li> -<li> -<p>Add reference to your configuration folder(that you have just added above) in <code>src/config/config-factory.ts</code>. For example <code>import znzConfig from './chis-znz'</code></p> -</li> -<li> -<p>Configure the <code>config.json</code> file.</p> -</li> -</ol> -<h4 id="configproperty">ConfigProperty</h4> -<p>The ConfigProperty is a data structure used several times within the <code>config.json</code> file and defines a property on an object. The ConfigProperty include:</p> -<table> -<thead> -<tr> -<th>Property</th> -<th>Type</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>friendly_name</td> -<td>string</td> -<td>Defines how this data will be labeled in CSV files and throughout the user experience.</td> -</tr> -<tr> -<td>property_name</td> -<td>string</td> -<td>Defines how the value will be stored on the object.</td> -</tr> -<tr> -<td>type</td> -<td>ConfigPropertyType</td> -<td>Defines the validation rules, and auto-formatting rules. See <a href="#configpropertytype">ConfigPropertyType</a>.</td> -</tr> -<tr> -<td>parameter</td> -<td>any</td> -<td>See <a href="#ConfigPropertyType">ConfigPropertyType</a>.</td> -</tr> -<tr> -<td>required</td> -<td>boolean</td> -<td>True if the object should not exist without this information.</td> -</tr> -</tbody> -</table> -<h4 id="configpropertytype">ConfigPropertyType</h4> -<p>The <code>ConfigPropertyType</code> defines a property&rsquo;s validation rules and auto-formatting rules. The optional <code>parameter</code> information alters the behavior of the <code>ConfigPropertyType</code>.</p> -<table> -<thead> -<tr> -<th>Type</th> -<th>Validation Rules</th> -<th>Auto Formatting Rules</th> -<th>Validator</th> -<th>Parameter</th> -</tr> -</thead> -<tbody> -<tr> -<td>string</td> -<td>Must be defined</td> -<td>Removes double whitespaces, leading or trailing whitespaces, and any character which is not alphanumeric or <code> ()\-'</code></td> -<td>None</td> -<td></td> -</tr> -<tr> -<td>name</td> -<td>Must be defined</td> -<td>Same as string + title case + <code>parameter</code> behavior</td> -<td>One or more regexes which are removed from the value when matched (eg. <code>&quot;parameter&quot;: [&quot;\\sCHU&quot;]</code> will format <code>this CHU</code> into <code>This</code>)</td> -<td></td> -</tr> -<tr> -<td>regex</td> -<td>Must match the <code>regex</code> captured by <code>parameter</code></td> -<td>Same as <code>string</code></td> -<td>A regex which must be matched to pass validation (eg. <code>&quot;parameter&quot;: &quot;^\\d{6}$&quot;</code> will accept only 6 digit numbers)</td> -<td></td> -</tr> -<tr> -<td>phone</td> -<td>A valid phone number for the specified locality</td> -<td>Auto formatting provided by <a href="https://github.com/google/libphonenumber">libphonenumber</a></td> -<td>Two letter country code specifying the locality of phone number (eg. <code>&quot;parameter&quot;: &quot;KE&quot;</code>)</td> -<td></td> -</tr> -<tr> -<td>generated</td> -<td>None. No user inputs.</td> -<td>Uses <a href="https://liquidjs.com">LiquidJS</a> templates to generate data</td> -<td>None</td> -<td><a href="#The-Generated-ConfigPropertyType">Details</a></td> -</tr> -<tr> -<td>select_one</td> -<td>Single choice from a list of options</td> -<td>Same as <code>string</code></td> -<td>None</td> -<td>Dictionary where the keys are the option values and the values are the corresponding labels</td> -</tr> -<tr> -<td>select_multiple</td> -<td>Multiple choice from a list of options</td> -<td>Same as <code>string</code></td> -<td>None</td> -<td>Same as <code>select_one</code></td> -</tr> -<tr> -<td>none</td> -<td>None</td> -<td>None</td> -<td>None</td> -<td></td> -</tr> -</tbody> -</table> -<h4 id="the-generated-configpropertytype">The Generated ConfigPropertyType</h4> -<p>ContactProperties with <code>type: &quot;generated&quot;</code> use the <a href="https://liquidjs.com">LiquidJS</a> template engine to populate a property with data. Here is an example of some configuration properties which use <code>&quot;type&quot;: &quot;generated&quot;</code>:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;place_properties&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;friendly_name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;CHP Area Name&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;property_name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;name&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;generated&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;parameter&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;{{ contact.name }}&#39;s Area&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;required&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;contact_properties&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;friendly_name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;CHP Name&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;property_name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;name&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;name&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;required&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>The user will be prompted to input the contact&rsquo;s name (CHP Name). The user is <em>not</em> prompted to input the place&rsquo;s name (CHP Area Name) because the place&rsquo;s name will automatically be assigned a value. In this example, if the user puts <code>john</code> as the contact&rsquo;s name, then the place will be named <code>John's Area</code>.</p> -<p>The data that is passed to the template is consistent with the properties defined in your configuration.</p> -<table> -<thead> -<tr> -<th>Variable</th> -<th>Value</th> -</tr> -</thead> -<tbody> -<tr> -<td>place</td> -<td>Has the attributes from <code>place_properties.property_name</code></td> -</tr> -<tr> -<td>contact</td> -<td>Has the attributes from <code>contact_properties.property_name</code></td> -</tr> -<tr> -<td>lineage</td> -<td>Has the attributes from <code>hierarchy.property_name</code></td> -</tr> -</tbody> -</table> -<h3 id="deployment">Deployment</h3> -<p>This tool is available via Docker by running <code>docker compose up</code>. Set the <a href="#environment-variables">Environment Variables</a>.</p> -<h2 id="development">Development</h2> -<p>Create an environment file by <code>cp env.example .env</code>. Change <code>INTERFACE</code> to <code>127.0.0.1</code> and otherwise see <a href="#environment-variables">Environment Variables</a> for more info.</p> -<p>Then run:</p> -<pre tabindex="0"><code>npm run dev -</code></pre><p>or</p> -<pre tabindex="0"><code>npm run build -npm start -</code></pre><h2 id="environment-variables">Environment Variables</h2> -<p>The <code>env.example</code> file has example values. Here&rsquo;s what they mean:</p> -<table> -<thead> -<tr> -<th>Variable</th> -<th>Description</th> -<th>Sample</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>CONFIG_NAME</code></td> -<td>Name of the configuration to use</td> -<td><code>chis-ke</code></td> -</tr> -<tr> -<td><code>EXTERNAL_PORT</code></td> -<td>Port to use in docker compose when starting the web server</td> -<td><code>3000</code></td> -</tr> -<tr> -<td><code>PORT</code></td> -<td>For localhost development environment</td> -<td><code>3000</code></td> -</tr> -<tr> -<td><code>COOKIE_PRIVATE_KEY</code></td> -<td>A string used to two-way encryption of cookies. Production values need to be a secret. Suggest <code>uuidgen</code> to generate</td> -<td><code>589a7f23-5bb2-4b77-ac78-f202b9b6d5e3</code></td> -</tr> -<tr> -<td><code>INTERFACE</code></td> -<td>Interface to bind to. Leave as &lsquo;0.0.0.0&rsquo; for prod, suggest &lsquo;127.0.0.1&rsquo; for development</td> -<td><code>127.0.0.1</code></td> -</tr> -<tr> -<td><code>CHT_DEV_URL_PORT</code></td> -<td>CHT instance when in <code>NODE_ENV===dev</code>. Needs URL and port</td> -<td><code>192-168-1-26.local-ip.medicmobile.org:10463</code></td> -</tr> -<tr> -<td><code>CHT_DEV_HTTP</code></td> -<td>&lsquo;false&rsquo; for http &rsquo;true&rsquo; for https</td> -<td><code>false</code></td> -</tr> -</tbody> -</table> -<h2 id="publishing-new-docker-images">Publishing new docker images</h2> -<p>Docker images are hosted on <a href="https://gallery.ecr.aws/medic/cht-user-management">AWS&rsquo;s Elastic Container Registry (ECR)</a>. To publish a new version:</p> -<ol> -<li>Update the <a href="https://github.com/medic/cht-user-management/blob/d992d5d6a911cdc21f610fa48a0ffb3e275bae0d/package.json#L3">version string</a> in the <code>package.json</code>.</li> -<li>Submit a PR and have it merged to <code>main</code>.</li> -<li><a href="https://github.com/medic/cht-user-management/blob/main/.github/workflows/docker-build.yml">Existing CI</a> will push an image to ECR.</li> -</ol>Apps: Building A Complex Task (Optional)https://docs.communityhealthtoolkit.org/apps/tutorials/tasks-2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/tasks-2/ -<div class="pageinfo pageinfo-primary"> -<p>Tasks prompt users to complete activities on a programmatic schedule. This tutorial will guide you through the development of an advanced task. This is an <em>optional</em> tutorial and is not required to <em>get started</em> with CHT Application development.</p> -<ul> -<li>Create a task with a complex follow-up schedule</li> -<li>Use a 3rd party JavaScript library <code>luxon</code> to make Date/Time calculations easier</li> -<li>Pass information from the task into the action <em>app form</em></li> -<li>Custom logic for resolving a task</li> -</ul> -</div> -<h2 id="prerequisites">Prerequisites</h2> -<ul> -<li><a href="https://docs.communityhealthtoolkit.org/apps/tutorials/tasks-1/">Building a simple task</a></li> -<li><a href="https://docs.communityhealthtoolkit.org/apps/examples/anc/">Maternal and Newborn Health Reference App</a></li> -</ul> -<h2 id="scenario">Scenario</h2> -<p>This scenario is loosely based on the <em>Pregnancy Visit Task</em> from the <a href="https://docs.communityhealthtoolkit.org/apps/examples/anc/">Maternal and Newborn Health Reference App</a>.</p> -<p>We expectations for the task are:</p> -<ol> -<li>Only CHW users should be prompted to complete pregnancy visits</li> -<li>Pregnancy visits should appear for pregnant patients after their pregnancy registration</li> -<li>Pregnancy visits should be scheduled eight times between the last mentrual period (LMP) and delivery.</li> -<li>A pregnancy visit should be skipped if an <em>assessment followup</em> is completed within the scheduled window for the pregancy visit.</li> -<li>The first pregnancy visit should prompt the CHW to ask some additional questions</li> -</ol> -<h2 id="developing-the-task">Developing the task</h2> -<p>This solution relies on the library <a href="https://moment.github.io/luxon">luxon</a> for parsing and manipulating dates and times. Let&rsquo;s start by installing it locally:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>npm install --save-dev luxon -</span></span></code></pre></div><p>And then in <code>tasks.js</code> we can analyse this solution:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">DateTime</span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">require</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;luxon&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#000">module</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">exports</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">name</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;pnc-after-pregnancy&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">icon</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;icon-follow-up&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">title</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;task.pnc_followup&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesTo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;contacts&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesToType</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;patient&#39;</span><span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">appliesIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">userIsChw</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">user</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">user</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">parent</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact_type</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#4e9a06">&#39;chw_area&#39;</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">isDead</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">date_of_death</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">isMuted</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">muted</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">userIsChw</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#000">isDead</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#000">isMuted</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">mostRecentPregnancy</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">Utils</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getMostRecentReport</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reports</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;pregnancy&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">calculatedLmp</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">mostRecentPregnancy</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">Utils</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">getField</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">mostRecentPregnancy</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;g_details.estimated_lmp&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">this</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">lmp</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">calculatedLmp</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">DateTime</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">fromFormat</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">calculatedLmp</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;yyyy-MM-dd&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#204a87;font-weight:bold">this</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">lmp</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#204a87;font-weight:bold">this</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">lmp</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">isValid</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">events</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#0000cf;font-weight:bold">12</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">20</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">26</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">30</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">34</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">36</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">38</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#0000cf;font-weight:bold">40</span><span style="color:#000;font-weight:bold">]</span> <span style="color:#8f5902;font-style:italic">// follow-up weeks after LMP -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">map</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">weekAfterLmp</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">({</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">`pnc-week-</span><span style="color:#4e9a06">${</span><span style="color:#000">weekAfterLmp</span><span style="color:#4e9a06">}</span><span style="color:#4e9a06">`</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">start</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">weekAfterLmp</span> <span style="color:#ce5c00;font-weight:bold">&gt;</span> <span style="color:#0000cf;font-weight:bold">30</span> <span style="color:#ce5c00;font-weight:bold">?</span> <span style="color:#0000cf;font-weight:bold">6</span> <span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">7</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">end</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">weekAfterLmp</span> <span style="color:#ce5c00;font-weight:bold">&gt;</span> <span style="color:#0000cf;font-weight:bold">30</span> <span style="color:#ce5c00;font-weight:bold">?</span> <span style="color:#0000cf;font-weight:bold">7</span> <span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">14</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">dueDate</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#204a87;font-weight:bold">this</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">lmp</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">plus</span><span style="color:#000;font-weight:bold">({</span> <span style="color:#000">weeks</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000">weekAfterLmp</span> <span style="color:#000;font-weight:bold">}).</span><span style="color:#000">toJsDate</span><span style="color:#000;font-weight:bold">();</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">})),</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">resolvedIf</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">report</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">event</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">dueDate</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">start</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">Utils</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">addDate</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">dueDate</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#000">event</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">start</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">getTime</span><span style="color:#000;font-weight:bold">();</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">end</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">Utils</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">addDate</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">dueDate</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">event</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">end</span> <span style="color:#ce5c00;font-weight:bold">+</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">).</span><span style="color:#000">getTime</span><span style="color:#000;font-weight:bold">();</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">pncInWindow</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">Utils</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">isFormSubmittedInWindow</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reports</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;pnc_followup&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">start</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">end</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">assessmentInWindow</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">Utils</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">isFormSubmittedInWindow</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">contact</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reports</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;assessment_followup&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">start</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">end</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">pncInWindow</span> <span style="color:#ce5c00;font-weight:bold">||</span> <span style="color:#000">assessmentInWindow</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">actions</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">form</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;pnc_followup&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">modifyContent</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">content</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">contact</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">report</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">event</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">followupCount</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">this</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">definition</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">events</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">findIndex</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">e</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000">event</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">id</span> <span style="color:#ce5c00;font-weight:bold">===</span> <span style="color:#000">e</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">id</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">+</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">content</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">t_followup_count</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">followupCount</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">toString</span><span style="color:#000;font-weight:bold">();</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">};</span> -</span></span></code></pre></div><h2 id="what-is-this-code-doing">What is this code doing?</h2> -<h3 id="appliesif">AppliesIf</h3> -<pre tabindex="0"><code>1. Only CHW users should be prompted to complete pregnancy visits -2. Pregnancy visits should appear for pregnant patients after their pregnancy registration -</code></pre><p>This function starts with the standard stuff from the <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/tasks-1/">Configuring Tasks Tutorial</a>. We want to confirm the user is a CHW, and the patient is alive and unmuted.</p> -<p>Then, the code searches through each contact&rsquo;s reports to find the most recent (<em>newest</em>) pregnancy registration using the <a href="https://docs.communityhealthtoolkit.org/apps/reference/_partial_utils/">Utils helper library</a>. It gets that report&rsquo;s value for <code>report.fields.g_details.estimated_lmp</code>, which is a date string calculated by the app form. It parses that string using the luxon library and returns true if it is a valid date.</p> -<p>Defining <code>lmp</code> as a property of <code>this</code>, <em>stores</em> the LMP DateTime and we will use that value later.</p> -<h3 id="events">Events</h3> -<pre tabindex="0"><code>3. Pregnancy visits should be scheduled eight times between the last mentrual period (LMP) and delivery. -</code></pre><p>This is a recurring task, unlike the one-time task we wrote in <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/tasks-1/">Configuring Tasks Tutorial</a>. This task is going to appear once 12 weeks after the estimated LMP date, again after 20 weeks, and again 6 more times. The <code>dueDate</code> on the event says that the task event is due <em>a variable number of weeks after the estimated LMP</em>. The time the CHW has to complete the visit is shorter toward the end of the schedule.</p> -<p>We&rsquo;re using the <code>this.lmp</code> value which was calculated and saved in the <code>appliesIf</code> function.</p> -<h2 id="resolvedif">ResolvedIf</h2> -<pre tabindex="0"><code>4. A pregnancy visit should be skipped if an _assessment followup_ is completed within the scheduled window for the pregancy visit. -</code></pre><p><code>resolvedIf</code> captures the conditions when the task event should disappear because it has been <em>completed</em>. Since we want the task schedule to appear if the user completes an <em>assessment followup</em> or the <em>pnc followup</em>, we can&rsquo;t use the <a href="https://docs.communityhealthtoolkit.org/apps/reference/tasks/#default-resolvedif-method">default resolvedIf definition</a>.</p> -<p>This function calculates timestamps for the start and end of each event. Then, it uses the <a href="https://docs.communityhealthtoolkit.org/apps/reference/_partial_utils/">Utils helper library</a> to see if <em>either</em> a <em>pnc</em> or an <em>assessment</em> followup is present within those timestamps.</p> -<p>The concept of <em>task completion</em> is covered in more depth in <a href="https://docs.communityhealthtoolkit.org/apps/guides/tasks/query-task-data/#completion-vs-cancellation">Task Completion vs Cancellation</a>.</p> -<h2 id="actions">Actions</h2> -<pre tabindex="0"><code>5. The first pregnancy visit should prompt the CHW to ask some additional questions -</code></pre><p>The <code>modifyContent</code> attribute allows the task to pass data from the task (in JavaScript) into the action app form (xlsx). Check out the guide for <a href="https://docs.communityhealthtoolkit.org/apps/guides/tasks/pass-data-to-form/">Passing data from a task into the app form</a>.</p>Apps: Local Docker Setuphttps://docs.communityhealthtoolkit.org/apps/tutorials/_partial_docker_setup/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/_partial_docker_setup/ -<ul class="nav nav-tabs" id="tabs-0" role="tablist"> -<li class="nav-item"> -<button class="nav-link active" -id="tabs-00-00-tab" data-bs-toggle="tab" data-bs-target="#tabs-00-00" role="tab" -aria-controls="tabs-00-00" aria-selected="true"> -Linux (Ubuntu) -</button> -</li><li class="nav-item"> -<button class="nav-link" -id="tabs-00-01-tab" data-bs-toggle="tab" data-bs-target="#tabs-00-01" role="tab" -aria-controls="tabs-00-01" aria-selected="false"> -macOS -</button> -</li><li class="nav-item"> -<button class="nav-link" -id="tabs-00-02-tab" data-bs-toggle="tab" data-bs-target="#tabs-00-02" role="tab" -aria-controls="tabs-00-02" aria-selected="false"> -Windows (WSL2) -</button> -</li> -</ul> -<div class="tab-content" id="tabs-0-content"> -<div class="tab-body tab-pane fade show active" -id="tabs-00-00" role="tabpanel" aria-labelled-by="tabs-00-00-tab" tabindex="0"> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>curl -fsSL get.docker.com -o get-docker.sh <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> sh get-docker.sh -</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"># OPTIONAL: Allow user to run Docker without sudo</span> -</span></span><span style="display:flex;"><span>dockerd-rootless-setuptool.sh install -</span></span><span style="display:flex;"><span><span style="color:#204a87">echo</span> <span style="color:#4e9a06">&#34;export PATH=/usr/bin:</span><span style="color:#000">$PATH</span><span style="color:#4e9a06">&#34;</span> &gt;&gt; ~/.<span style="color:#204a87;font-weight:bold">$(</span>basename <span style="color:#000">$SHELL</span><span style="color:#204a87;font-weight:bold">)</span>rc -</span></span><span style="display:flex;"><span><span style="color:#204a87">echo</span> <span style="color:#4e9a06">&#34;export DOCKER_HOST=unix:///run/user/1000/docker.sock&#34;</span> &gt;&gt; ~/.<span style="color:#204a87;font-weight:bold">$(</span>basename <span style="color:#000">$SHELL</span><span style="color:#204a87;font-weight:bold">)</span>rc -</span></span><span style="display:flex;"><span>. ~/.<span style="color:#204a87;font-weight:bold">$(</span>basename <span style="color:#000">$SHELL</span><span style="color:#204a87;font-weight:bold">)</span>rc -</span></span></code></pre></div> -</div> -<div class="tab-body tab-pane fade" -id="tabs-00-01" role="tabpanel" aria-labelled-by="tabs-00-01-tab" tabindex="0"> -<p>Download and install <a href="https://www.docker.com/products/docker-desktop">Docker Desktop</a> or <a href="https://github.com/abiosoft/colima#readme">Colima</a>.</p> -</div> -<div class="tab-body tab-pane fade" -id="tabs-00-02" role="tabpanel" aria-labelled-by="tabs-00-02-tab" tabindex="0"> -<p>Download and install <a href="https://www.docker.com/products/docker-desktop">Docker Desktop</a>.</p> -</div> -</div> -<p>Restart your entire machine to finish initializing Docker.</p> -<p>After restarting, verify Docker is running as expected. Run the simple <code>hello-world</code> Docker container. This should output &ldquo;Hello from Docker!&rdquo; as well as some other intro text:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>docker run hello-world -</span></span></code></pre></div> \ No newline at end of file +Step-by-Step Tutorials on Community Health Toolkithttps://docs.communityhealthtoolkit.org/apps/tutorials/Recent content in Step-by-Step Tutorials on Community Health ToolkitHugo -- gohugo.ioenGetting started building a CHT apphttps://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/This tutorial will take you through setting up a local environment to build and test CHT applications on CHT version 4.x. This includes setting up the necessary tools to download and run the CHT public docker image as well as a command line interface tool to manage and build CHT apps. +By the end of the tutorial you should be able to: +View the login page to CHT webapp on localhost Upload default settings to localhost Note This guide will only work with CHT 4.Local Couch2pg Setuphttps://docs.communityhealthtoolkit.org/apps/tutorials/couch2pg-setup/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/couch2pg-setup/This tutorial will take you through setting up a Couch2pg service. +By the end of the tutorial you should be able to: +Set up a Couch2pg service Run the Couch2pg service CHT Couch2pg is a background process that moves data from Couchdb to Postgres through one way replication. It therefore, needs to have full read and write access to both the Postgres Database and Couchdb upstream. It is built in nodejs and can be set up as a background process using systemd.Contact and User Management - Part 1https://docs.communityhealthtoolkit.org/apps/tutorials/contact-and-users-1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/contact-and-users-1/In this tutorial you will learn how to create and edit contacts and their associated users in and application built with the CHT using the default contact creation forms. This will help you get familiar with the UI of the webapp as well as some features and functionality. If you are already comfortable with this, you can skip to part 2, which covers manipulating contacts and their associated documents using cht-conf.Contact and User Management - Part 2https://docs.communityhealthtoolkit.org/apps/tutorials/contact-and-users-2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/contact-and-users-2/In this tutorial you will learn how to create and edit contacts and their associated users in the CHT application using cht-conf. If you haven&rsquo;t already, have a look at part 1 of this tutorial for a useful overview of key concepts. +Brief Overview of Key Concepts cht-conf is a command-line interface tool to manage and configure your apps built using the Core Framework of the Community Health Toolkit.Building SMS Formshttps://docs.communityhealthtoolkit.org/apps/tutorials/sms-forms/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/sms-forms/SMS forms allow users to submit reports from any device including feature phones without internet access. SMS forms are ideal in scenarios where targeted users have no way of accessing internet or where they are restricted to using feature phones. +This tutorial will take you through how to build SMS forms for CHT applications, including: +Defining SMS forms Setting validation rules for SMS forms Setting automatic responses to SMS reports You will be building a pregnancy registration workflow that allows Community Health Workers to register households, register household members, and register new pregnancies for the household members.CHT Application Settingshttps://docs.communityhealthtoolkit.org/apps/tutorials/application-settings/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/application-settings/This tutorial will take you through how to manage the CHT application settings, including; +Setting user roles and permissions Enabling and disabling transitions Configuring contact hierarchy and configuring replication. App settings allow you to both persist information that is critical to the application outside the code, and to create profiles that store the preferences for project deployments. +Brief Overview of Key Concepts The settings which control CHT apps are defined in the app_settings.Setting up Multi-facility usershttps://docs.communityhealthtoolkit.org/apps/tutorials/multi-facility-users/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/multi-facility-users/This tutorial will take you through how to create and assign users to multiple places in the CHT UI. Assigning users to multiple places is only available from CHT 4.9.0. +This tutorial covers; +Creating contacts and their associated users Creating places and assign contacts to those places Assigning users to multiple places The CHT application settings allows you to both persist information that is critical to the application outside the code, and to create profiles that store the preferences for project deployments.Building SMS Scheduleshttps://docs.communityhealthtoolkit.org/apps/tutorials/sms-schedules/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/sms-schedules/SMS schedules allow you to send reminder messages at predetermined times. These reminders serve as useful prompts for end-users to take specific actions. +This tutorial takes you through how to set up SMS schedules for CHT applications. It uses a pregnancy registration workflow and follow-up reminders for a Community Health Worker as an example. The same methodology can be applied to other workflows and reminders as needed. +Brief Overview of Key Concepts SMS schedules are a series of SMS messages that are to be sent to specific contacts at future dates and times.Building App Formshttps://docs.communityhealthtoolkit.org/apps/tutorials/app-forms/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/app-forms/App forms allow users to submit reports from Android devices +This tutorial will take you through how to build App forms for CHT applications, including: +Authoring forms in Excel, Google sheets or other spreadsheet applications. Converting XLSForms to XForms Uploading XForms to CHT You will be building assessment workflow that allows Community Health Workers to conduct a health assessment for children under the age of 5. +Brief Overview of Key Concepts App forms serve as actions within the app.Setting Form Propertieshttps://docs.communityhealthtoolkit.org/apps/tutorials/form-properties/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/form-properties/This tutorial will take you through how to write the &lt;form_name&gt;.properties.json file. +The &lt;form_name&gt;.properties.json file allows you to add logic that ensures that the right action appears for the right contacts (people and places). For instance, an assessment form for children under-5 will only appear for person contacts on the CHT whose age is less than 5. +You will be adding meta-data and context to an assessment workflow that allows Community Health Workers to conduct a health assessment for children under the age of 5.Building A Simple Taskhttps://docs.communityhealthtoolkit.org/apps/tutorials/tasks-1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/tasks-1/Tasks prompt users to complete activities on a programmatic schedule. This guide will explain how to write a task which prompts CHW users to complete an assessment app form for new patients within 7 days of registration. +Creating a straight-forward task Running and testing that task Prerequisites Complete the App Forms Tutorial - Tasks prompt users to complete activities by opening an app form. The app forms tutorial produces an assessment app form which we will use here.Building Target Widgetshttps://docs.communityhealthtoolkit.org/apps/tutorials/targets/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/targets/This tutorial will take you through how to build target widgets. +Target widgets provide a summary or analysis of the data in submitted reports. +You will be adding target widgets that will allow Community Health Workers (CHWs) to track various metrics based on assessment reports submitted. +Brief Overview of Key Concepts Targets is the user dashboard or analytics tab. +Target widgets provide a summary or analysis of the data in submitted reports.Building Contact Summaryhttps://docs.communityhealthtoolkit.org/apps/tutorials/contact-summary/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/contact-summary/This tutorial will take you through building a contact summary for CHT applications. +Contact summaries display basic information about the contact. +You will be adding a contact summary that displays information about a person&rsquo;s patient id, age, sex, phone number, and information about the place they belong to ie. parent. +Brief Overview of Key Concepts Each field that can be shown on a contact’s profile is defined as an object in the fields array of contact-summary.Building Death Report Workflowshttps://docs.communityhealthtoolkit.org/apps/tutorials/death-reporting/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/death-reporting/Death Reporting Guide for setting up a comprehensive death report workflow In this tutorial you will learn how to set up a death report workflow. This includes laying out a death report form as well as handling all the configurations needed for wiring it up in the CHT. By the end of the tutorial you should be able to: +Mark select contacts as deceased Make relevant app updates for dead contacts Brief Overview of Key Concepts When a contact is marked as deceased within the CHT, the contact will be hidden by default on the contacts tab.Building Condition Cardshttps://docs.communityhealthtoolkit.org/apps/tutorials/condition-cards/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/condition-cards/This tutorial will take you through building a condition card for CHT applications. +Condition cards, like contact summaries display information about the contact. The data displayed in condition cards can be pulled from submitted reports. +In this tutorial,you will be adding a condition card that displays information about a person&rsquo;s most recent assessment, including: the date of the most recent assessment, and whether or not they had a cough.Localizationhttps://docs.communityhealthtoolkit.org/apps/tutorials/localizing-cht/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/localizing-cht/Given that CHT apps are used around the world, the Core Framework was designed with localization in mind. The Core Framework itself is available in English, French, Hindi, Nepali, Spanish, Swahili, and Indonesian. +This tutorial will take you through localizing the CHT to a custom language (Swahili). This will include setting up the user interface labels as well as outgoing text messages. +By the end of the tutorial you should be able to:Configuring CHT Application Graphicshttps://docs.communityhealthtoolkit.org/apps/tutorials/application-graphics/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/application-graphics/This tutorial will take you through customising some graphical elements of CHT core. +You will cover site branding, partner logos, header tab icons, and app icons (used in tasks, targets, and contacts). +Required Resources You should have a functioning CHT instance with cht-conf installed locally and completed a project folder setup. +Implementation Steps 1. Site branding You have the ability to modify the app title, logo, and favicon. For Progressive Web App installations you can also configure the desktop icon.Application Testshttps://docs.communityhealthtoolkit.org/apps/tutorials/application-tests/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/application-tests/This tutorial takes you through testing the various configurable components of CHT applications using cht-conf-test-harness. +Prerequisites Complete the following tutorials: +Building App Forms Building A Simple Task Building Target Widgets Building Contact Summary Importance of testing your application Testing your CHT application is important as it ensures you are consistently maintaining your application and defining implementation requirements. Testing also helps the user make better architectural decisions, optimizes the forms, tasks and other components of the application.CHT User Management toolhttps://docs.communityhealthtoolkit.org/apps/tutorials/user-management-tool/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/user-management-tool/The Community Health Toolkit (CHT) is highly configurable and can be customized to support multiple hierarchies and users in the health care system. The CHT user management tool is a user friendly web application that works with the CHT to decentralize the user management process to the subnational levels, increasing efficiency and accuracy. This guide highlights steps for setting up and configuring the user management tool. The guide has been tailored for specific CHT-supported national community health information systems.Building A Complex Task (Optional)https://docs.communityhealthtoolkit.org/apps/tutorials/tasks-2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/tasks-2/Tasks prompt users to complete activities on a programmatic schedule. This tutorial will guide you through the development of an advanced task. This is an optional tutorial and is not required to get started with CHT Application development. +Create a task with a complex follow-up schedule Use a 3rd party JavaScript library luxon to make Date/Time calculations easier Pass information from the task into the action app form Custom logic for resolving a task Prerequisites Building a simple task Maternal and Newborn Health Reference App Scenario This scenario is loosely based on the Pregnancy Visit Task from the Maternal and Newborn Health Reference App.Local Docker Setuphttps://docs.communityhealthtoolkit.org/apps/tutorials/_partial_docker_setup/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/_partial_docker_setup/Linux (Ubuntu) macOS Windows (WSL2) curl -fsSL get.docker.com -o get-docker.sh &amp;&amp; sh get-docker.sh # OPTIONAL: Allow user to run Docker without sudo dockerd-rootless-setuptool.sh install echo &#34;export PATH=/usr/bin:$PATH&#34; &gt;&gt; ~/.$(basename $SHELL)rc echo &#34;export DOCKER_HOST=unix:///run/user/1000/docker.sock&#34; &gt;&gt; ~/.$(basename $SHELL)rc . ~/.$(basename $SHELL)rc Download and install Docker Desktop or Colima. +Download and install Docker Desktop. +Restart your entire machine to finish initializing Docker. +After restarting, verify Docker is running as expected. Run the simple hello-world Docker container. \ No newline at end of file diff --git a/apps/tutorials/local-setup/index.html b/apps/tutorials/local-setup/index.html index 3dddba893e..6e502c5823 100644 --- a/apps/tutorials/local-setup/index.html +++ b/apps/tutorials/local-setup/index.html @@ -1,9 +1,9 @@ -Getting started building a CHT app | Community Health Toolkit +Getting started building a CHT app | Community Health Toolkit

    Getting started building a CHT app

    Setting up a local environment to build and test CHT 4.x applications

    This tutorial will take you through setting up a local environment to build and test CHT applications on CHT version 4.x. This includes setting up the necessary tools to download and run the CHT public docker image as well as a command line interface tool to manage and build CHT apps.

    By the end of the tutorial you should be able to:

    • View the login page to CHT webapp on localhost
    • Upload default settings to localhost

    Brief Overview of Key Concepts

    The CHT Core Framework makes it faster to build full-featured, scalable digital health apps by providing a foundation developers can build on. These apps can support most languages, are Offline-First, and work on basic phones (via SMS), smartphones, tablets, and computers.

    CHT Project Configurer also known as cht-conf is a command-line interface tool to manage and configure CHT apps.

    Docker is a tool designed to make it easier to create, deploy, and run applications by using containers.

    Containers allow a developer to package up an application with all of the parts it needs, such as libraries and other dependencies, and deploy it as one package.

    Setup environment

    CHT app development can be done on Linux, macOS, or Windows (using the Windows Subsystem for Linux (WSL2)).

    CHT apps can be built on your local system (with the necessary libraries installed and configured) or they can be built from within VS Code Dev Containers.

    Before you begin, ensure you have the following tools:

    Installing Docker

    Getting started building a CHT app

    Setting up a local environment to build and test CHT 4.x applications

    This tutorial will take you through setting up a local environment to build and test CHT applications on CHT version 4.x. This includes setting up the necessary tools to download and run the CHT public docker image as well as a command line interface tool to manage and build CHT apps.

    By the end of the tutorial you should be able to:

    • View the login page to CHT webapp on localhost
    • Upload default settings to localhost

    Brief Overview of Key Concepts

    The CHT Core Framework makes it faster to build full-featured, scalable digital health apps by providing a foundation developers can build on. These apps can support most languages, are Offline-First, and work on basic phones (via SMS), smartphones, tablets, and computers.

    CHT Project Configurer also known as cht-conf is a command-line interface tool to manage and configure CHT apps.

    Docker is a tool designed to make it easier to create, deploy, and run applications by using containers.

    Containers allow a developer to package up an application with all of the parts it needs, such as libraries and other dependencies, and deploy it as one package.

    Setup environment

    CHT app development can be done on Linux, macOS, or Windows (using the Windows Subsystem for Linux (WSL2)).

    CHT apps can be built on your local system (with the necessary libraries installed and configured) or they can be built from within VS Code Dev Containers.

    Before you begin, ensure you have the following tools:

    Installing Docker

    curl -fsSL get.docker.com -o get-docker.sh && sh get-docker.sh
     # OPTIONAL: Allow user to run Docker without sudo
     dockerd-rootless-setuptool.sh install
    @@ -362,7 +362,8 @@
     Self Hosting

    Hosting the CHT on self run infrastructure

    Hosting > 3.x > AWS Hosting

    Hosting the CHT on Amazon EC2

    -

    \ No newline at end of file +

    \ No newline at end of file diff --git a/apps/tutorials/localizing-cht/index.html b/apps/tutorials/localizing-cht/index.html index d0decbeb7f..3e200005a4 100644 --- a/apps/tutorials/localizing-cht/index.html +++ b/apps/tutorials/localizing-cht/index.html @@ -1,9 +1,9 @@ -Localization | Community Health Toolkit +Localization | Community Health Toolkit

    Localization

    Localizing language in the CHT

    Given that CHT apps are used around the world, the Core Framework was designed with localization in mind. The Core Framework itself is available in English, French, Hindi, Nepali, Spanish, Swahili, and Indonesian.

    This tutorial will take you through localizing the CHT to a custom language (Swahili). This will include setting up the user interface labels as well as outgoing text messages.

    By the end of the tutorial you should be able to:

    • Change the CHT user interface labels to a custom language.
    • Change outgoing text messages to a custom language(Swahili will be used in the guide).

    Brief Overview of Key Concepts

    Localization this is setting up the desired language in CHT for the end user.

    Translations this is manually setting up extra translations of instance tabs texts or outgoing SMS text. See an outline of how to do that here.

    Required Resources

    You should have a functioning CHT instance with cht-conf installed locally, completed a project folder setup, and an messages-sw.properties file.

    Implementation Steps

    Create a new file in the ’translations/’ folder called messages-sw.properties.

    After an edit or addition of a translation, upload the current messages-sw.properties onto your local environment using the below command.

    cht --url=https://medic:password@localhost --upload-custom-translations
    + Create project issue

    Localization

    Localizing language in the CHT

    Given that CHT apps are used around the world, the Core Framework was designed with localization in mind. The Core Framework itself is available in English, French, Hindi, Nepali, Spanish, Swahili, and Indonesian.

    This tutorial will take you through localizing the CHT to a custom language (Swahili). This will include setting up the user interface labels as well as outgoing text messages.

    By the end of the tutorial you should be able to:

    • Change the CHT user interface labels to a custom language.
    • Change outgoing text messages to a custom language(Swahili will be used in the guide).

    Brief Overview of Key Concepts

    Localization this is setting up the desired language in CHT for the end user.

    Translations this is manually setting up extra translations of instance tabs texts or outgoing SMS text. See an outline of how to do that here.

    Required Resources

    You should have a functioning CHT instance with cht-conf installed locally, completed a project folder setup, and an messages-sw.properties file.

    Implementation Steps

    Create a new file in the ’translations/’ folder called messages-sw.properties.

    After an edit or addition of a translation, upload the current messages-sw.properties onto your local environment using the below command.

    cht --url=https://medic:password@localhost --upload-custom-translations
     

    1. Add User Interface Label Translations

    CHT Instance text - for non-admin users, this is the text that falls under Messages, Tasks, Reports, People and Targets. To localize instance text to Swahili, change the default system language to Swahili.


    Go to App Management > Display > Languages > Default Language(Change to Swahili)


    To find out what the language code for Swahili is, Go to the list of language as illustrated in the screenshot, click Kiswahili (Swahili) to show the options dropdown and click Edit Name the code will be in the text box under Language Code on the popup.


    In our case, the language code is sw.


    Create a message-sw.properties file and use the instructions outlined here to learn the structure of a message-{language-code}.properties file.

    Populate the messages-sw.properties file with the appropriate translation strigs and upload it using the below command:

    cht --local upload-custom-translations
     

    The default Swahili translations that come pre-added to CHT can be found here.

    After changing the instance language to Swahili, the various elements will behave like this:

    Messages

    In Messages, the time counter text and navigation text changes.

    This is an example of Swahili localization.


    English text


    Swahili Translation


    To change the title of the tab from the default title of this Messages tab of Jumbe in Kiswahili to Barua, add or edit the below code in the messages-sw.properties file:

    Messages = Jumbe
    @@ -362,7 +362,8 @@
     Translations

    Process for managing translations in CHT Core

    CHT Applications > Reference > translations/

    Localization: Localized labels for CHT applications

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/tutorials/multi-facility-users/index.html b/apps/tutorials/multi-facility-users/index.html index 54b04bb189..11e0e9b7ef 100644 --- a/apps/tutorials/multi-facility-users/index.html +++ b/apps/tutorials/multi-facility-users/index.html @@ -1,9 +1,9 @@ -Setting up Multi-facility users | Community Health Toolkit +Setting up Multi-facility users | Community Health Toolkit

    Setting up Multi-facility users

    Creating and assigning users to multiple places in the CHT UI

    This tutorial will take you through how to create and assign users to multiple places in the CHT UI. Assigning users to multiple places is only available from CHT 4.9.0.

    This tutorial covers;

    • Creating contacts and their associated users
    • Creating places and assign contacts to those places
    • Assigning users to multiple places

    The CHT application settings allows you to both persist information that is critical to the application outside the code, and to create profiles that store the preferences for project deployments.

    Brief Overview of Key Concepts

    Permissions are settings that control access to specific app features and functionality.

    Roles define permissions for users to access a group of app features and functionality.

    Contacts are people or places that are created in the CHT application.

    People are both patients in the system and users of the system, such as CHWs or Nurses.

    Places represent either an actual physical location such as a health facility, clinic, or a grouping such as a household or CHW Area.

    Pre-requisites

    Implementation Steps

    In this tutorial, you will work with the default contact forms and the default hierarchy.

    The default app_settings.json in the CHT found in the /config/default folder has an existing chw_supervisor role and the "can_have_multiple_places": [] permission.

    While logged in as an admin user, you will create two Health Facilities, CHW Supervisor, and CHW Area. You will then create the user for the CHW Supervisor so that they can log in and see the facilities, and the CHW Areas they supervise.

    Creating multi-facility user using the CHT default config.

    1. Create New Health Facility.


    While logged into the CHT application, go to the People tab and select New Health Facility


    Select the option that lets you create a new person within the form. This person will automatically become the primary contact for the created place.



    When filling in the required fields select CHW Supervisor as the role.

    Once you have filled the required fields, click Next to go to the next section.


    Select the option that lets you name the facility manually. Enter the Name of the Health Facility and submit the form.


    You should see the newly created Health Facility appear on the left-hand side and when you select it, you should see the Health Facility name and the primary contact of the Health Facility.



    2. Create CHW Area and CHW

    We will now create a CHW Area within the Health Facility that we previously created.


    Select the Health Facility on the left-hand side. Click on the Blue Action Button and select New Area.


    Select the option that lets you create a new person within the form. This person will automatically become the primary contact for the created place.

    When filling in the required fields select CHW as the role.

    Once you have filled the required fields, click Next to go to the next section.


    You will get an option to name the Place after the created contact person or name it yourself. If you select Yes, the new place will be named <contact-name>'s Area. For example Jane Doe's Area.


    Once you submit, a new CHW Area will be created. On the right-hand you should see the CHW Area name, the primary contact of the CHW Area, and the Health Facility that the CHW Area belongs to.



    3. Create Second Health Facility.


    For the second Health Facility, we will skip creating or assigning a primary contact and focus on creating the second Health Facility.


    Enter the details of the Health Facility and submit the form.


    You should see the two Health Facilities appear on the left-hand side.


    4. Create the CHW Supervisor User

    Let’s create a CHW Supervisor user who’s linked to the CHW Supervisor contact we created earlier.


    Go to the hamburger menu and select App Management.


    When you are on the App Management page, select Users on the left-hand side and then select Add User on the right-hand side.


    You should now see an Add User Form. Fill in the user name, then select the role as CHW Supervisor.

    In the Place field, search for the first Health Facility by typing the first few letters of the Health Facility name. Once you have selected the first Health Facility, type the name of the Second Facility and select it as well.

    Once that is done, under the Associate Contact field select the name of the CHW Supervisor whose user you are creating. Finally, input a password and hit Submit.


    Once this is done, you have created a supervisor who is assigned to multiple Health Facilities. You can logout and log into the app using the username and password that you just created.

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/tutorials/sms-forms/index.html b/apps/tutorials/sms-forms/index.html index a1399236fc..3e26a8c53a 100644 --- a/apps/tutorials/sms-forms/index.html +++ b/apps/tutorials/sms-forms/index.html @@ -1,9 +1,9 @@ -Building SMS Forms | Community Health Toolkit +Building SMS Forms | Community Health Toolkit

    Building SMS Forms

    Building CHT application SMS forms

    SMS forms allow users to submit reports from any device including feature phones without internet access. SMS forms are ideal in scenarios where targeted users have no way of accessing internet or where they are restricted to using feature phones.

    This tutorial will take you through how to build SMS forms for CHT applications, including:

    • Defining SMS forms
    • Setting validation rules for SMS forms
    • Setting automatic responses to SMS reports

    You will be building a pregnancy registration workflow that allows Community Health Workers to register households, register household members, and register new pregnancies for the household members.

    Brief Overview of Key Concepts

    SMS forms are structured text messages that contain a form code representing a report type and some information associated with the report.

    SMS forms are defined in either the base_settings.json or the app_settings/forms.json file and compiled into the app_settings.json file with the compile-app-settings action in the cht-conf tool, then stored in the settings doc in the database.

    SMS gateways allow the CHT core framework to send and receive SMS transmission to or from a mobile network operator.

    SMS aggregators act as intermediaries between the mobile network operators and the CHT core framework. They allow for greater customization of SMS workflows in the CHT.

    Interoperability: CHT apps can receive data from other software when it is submitted as reports using the same JSON/SMS form notation.

    Required Resources

    You should have a functioning CHT instance with cht-conf installed locally.

    You also need to have some prior knowledge on app_settings.json.

    Implementation Steps

    SMS forms are defined using JSON in the app_settings.json file.

    1. Enable Transitions

    To ensure SMS forms function as expected, you will first need to enable the following transitions in app_settings.json.

      "update_clinics": true,
    + Create project issue

    Building SMS Forms

    Building CHT application SMS forms

    SMS forms allow users to submit reports from any device including feature phones without internet access. SMS forms are ideal in scenarios where targeted users have no way of accessing internet or where they are restricted to using feature phones.

    This tutorial will take you through how to build SMS forms for CHT applications, including:

    • Defining SMS forms
    • Setting validation rules for SMS forms
    • Setting automatic responses to SMS reports

    You will be building a pregnancy registration workflow that allows Community Health Workers to register households, register household members, and register new pregnancies for the household members.

    Brief Overview of Key Concepts

    SMS forms are structured text messages that contain a form code representing a report type and some information associated with the report.

    SMS forms are defined in either the base_settings.json or the app_settings/forms.json file and compiled into the app_settings.json file with the compile-app-settings action in the cht-conf tool, then stored in the settings doc in the database.

    SMS gateways allow the CHT core framework to send and receive SMS transmission to or from a mobile network operator.

    SMS aggregators act as intermediaries between the mobile network operators and the CHT core framework. They allow for greater customization of SMS workflows in the CHT.

    Interoperability: CHT apps can receive data from other software when it is submitted as reports using the same JSON/SMS form notation.

    Required Resources

    You should have a functioning CHT instance with cht-conf installed locally.

    You also need to have some prior knowledge on app_settings.json.

    Implementation Steps

    SMS forms are defined using JSON in the app_settings.json file.

    1. Enable Transitions

    To ensure SMS forms function as expected, you will first need to enable the following transitions in app_settings.json.

      "update_clinics": true,
       "registration": true,
       "accept_patient_reports": true,
       "generate_shortcode_on_contacts": true,
    @@ -606,7 +606,8 @@
     : Validations

    Settings: The primary location of settings for CHT applications

    CHT Applications > Quick Guides > Messaging

    Building and troubleshooting messaging

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/tutorials/sms-schedules/index.html b/apps/tutorials/sms-schedules/index.html index fd4ce373de..75bbfb8a84 100644 --- a/apps/tutorials/sms-schedules/index.html +++ b/apps/tutorials/sms-schedules/index.html @@ -1,9 +1,9 @@ -Building SMS Schedules | Community Health Toolkit +Building SMS Schedules | Community Health Toolkit

    Building SMS Schedules

    Building CHT application SMS schedules

    SMS schedules allow you to send reminder messages at predetermined times. These reminders serve as useful prompts for end-users to take specific actions.

    This tutorial takes you through how to set up SMS schedules for CHT applications. It uses a pregnancy registration workflow and follow-up reminders for a Community Health Worker as an example. The same methodology can be applied to other workflows and reminders as needed.

    Brief Overview of Key Concepts

    SMS schedules are a series of SMS messages that are to be sent to specific contacts at future dates and times. They are defined in either the base_settings.json or the app_settings/schedules.json file and compiled into the app_settings.json file with the compile-app-settings action in the cht-conf tool.

    SMS schedules can be triggered by SMS forms or App forms.

    Required Resources

    You should have built a pregnancy SMS form.

    Implementation Steps

    SMS schedules are defined using JSON in the app_settings.json file.

    1. Define a Pregnancy Follow Up Schedule

    To set the pregnancy follow up schedule, edit the array corresponding to the schedules key in app_settings.json. Add an object within the array as shown. This will set a schedule of reminders that will be sent after a time interval (offset) relative to a base date (start_from).

    schedules: [
    + Create project issue

    Building SMS Schedules

    Building CHT application SMS schedules

    SMS schedules allow you to send reminder messages at predetermined times. These reminders serve as useful prompts for end-users to take specific actions.

    This tutorial takes you through how to set up SMS schedules for CHT applications. It uses a pregnancy registration workflow and follow-up reminders for a Community Health Worker as an example. The same methodology can be applied to other workflows and reminders as needed.

    Brief Overview of Key Concepts

    SMS schedules are a series of SMS messages that are to be sent to specific contacts at future dates and times. They are defined in either the base_settings.json or the app_settings/schedules.json file and compiled into the app_settings.json file with the compile-app-settings action in the cht-conf tool.

    SMS schedules can be triggered by SMS forms or App forms.

    Required Resources

    You should have built a pregnancy SMS form.

    Implementation Steps

    SMS schedules are defined using JSON in the app_settings.json file.

    1. Define a Pregnancy Follow Up Schedule

    To set the pregnancy follow up schedule, edit the array corresponding to the schedules key in app_settings.json. Add an object within the array as shown. This will set a schedule of reminders that will be sent after a time interval (offset) relative to a base date (start_from).

    schedules: [
       {
         "name": "Pregnancy Follow Up Reminders",
         "summary": "",
    @@ -425,7 +425,8 @@
     Messaging

    Messaging for Care Coordination, Alerts, and Notifications

    CHT Applications > Quick Guides > Messaging

    Building and troubleshooting messaging

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/tutorials/targets/index.html b/apps/tutorials/targets/index.html index 41bace2f15..a3b56d20ae 100644 --- a/apps/tutorials/targets/index.html +++ b/apps/tutorials/targets/index.html @@ -1,9 +1,9 @@ -Building Target Widgets | Community Health Toolkit +Building Target Widgets | Community Health Toolkit

    Building Target Widgets

    How to build CHT monthly and all time target widgets

    This tutorial will take you through how to build target widgets.

    Target widgets provide a summary or analysis of the data in submitted reports.

    You will be adding target widgets that will allow Community Health Workers (CHWs) to track various metrics based on assessment reports submitted.

    Brief Overview of Key Concepts

    Targets is the user dashboard or analytics tab.

    Target widgets provide a summary or analysis of the data in submitted reports.

    Count widgets show a tally of a particular report that has been submitted or data within a report that matches a set of criteria.

    Percent widgets display a ratio, which helps to provide insight into the proportion that matches a defined criteria.

    Target schema details a set of properties for targets.

    Target instance is an object emitted and counted or aggregated based on target configuration.

    Required Resources

    You should have a functioning CHT instance with cht-conf installed locally, completed a project folder setup, and an assessment form.

    Implementation Steps

    It is good practice to set up a reference document outlining the specifications of the target widgets similar to the one below. Other formats may also be used.

    SourceUI LabelDefinitionTypeReporting PeriodGoal
    Assessment formTotal assessmentsTotal number of assessment reports submittedCountAll time_
    Assessment formTotal assessmentsTotal number of assessment reports submitted this monthCountThis month2
    Assessment formTotal population with coughTotal number of household members with coughCountThis month_
    Assessment form% Population with coughTotal number of contacts with cough/Total number of contacts assessedPercentThis month_
    Assessment formTotal households with assessmentsTotal number of households with at least one submitted assessment formCountThis month2
    Assessment form% Household with >=2 assessmentsTotal number of households with at least two patients assessed/Total number of householdsPercentAll time60

    Create a targets.js file (this may have already been created by the initialise-project-layout command).

    1. Define an All-Time Target Widget

    This widget counts the total number of assessment reports that have been submitted by the user from the time that they started reporting. + Create project issue

    Building Target Widgets

    How to build CHT monthly and all time target widgets

    This tutorial will take you through how to build target widgets.

    Target widgets provide a summary or analysis of the data in submitted reports.

    You will be adding target widgets that will allow Community Health Workers (CHWs) to track various metrics based on assessment reports submitted.

    Brief Overview of Key Concepts

    Targets is the user dashboard or analytics tab.

    Target widgets provide a summary or analysis of the data in submitted reports.

    Count widgets show a tally of a particular report that has been submitted or data within a report that matches a set of criteria.

    Percent widgets display a ratio, which helps to provide insight into the proportion that matches a defined criteria.

    Target schema details a set of properties for targets.

    Target instance is an object emitted and counted or aggregated based on target configuration.

    Required Resources

    You should have a functioning CHT instance with cht-conf installed locally, completed a project folder setup, and an assessment form.

    Implementation Steps

    It is good practice to set up a reference document outlining the specifications of the target widgets similar to the one below. Other formats may also be used.

    SourceUI LabelDefinitionTypeReporting PeriodGoal
    Assessment formTotal assessmentsTotal number of assessment reports submittedCountAll time_
    Assessment formTotal assessmentsTotal number of assessment reports submitted this monthCountThis month2
    Assessment formTotal population with coughTotal number of household members with coughCountThis month_
    Assessment form% Population with coughTotal number of contacts with cough/Total number of contacts assessedPercentThis month_
    Assessment formTotal households with assessmentsTotal number of households with at least one submitted assessment formCountThis month2
    Assessment form% Household with >=2 assessmentsTotal number of households with at least two patients assessed/Total number of householdsPercentAll time60

    Create a targets.js file (this may have already been created by the initialise-project-layout command).

    1. Define an All-Time Target Widget

    This widget counts the total number of assessment reports that have been submitted by the user from the time that they started reporting. Edit the targets.js file to define the total assessments all-time widget as shown below:

      {
         id: 'assessments-all-time',
         type: 'count',
    @@ -549,7 +549,8 @@
     targets.js

    Targets: Definition of target widgets calculated and seen in the app

    Design System > Best Practices : Anatomy of a Task

    This document covers the configuration best practices of forms, tasks, targets, and contact profiles when building your own community health app.

    -

    Last modified 08.02.2022: Targets tutorials (#574) (3fb53ed4)
    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/apps/tutorials/tasks-1/index.html b/apps/tutorials/tasks-1/index.html index 4ceaadcb52..857e526020 100644 --- a/apps/tutorials/tasks-1/index.html +++ b/apps/tutorials/tasks-1/index.html @@ -1,9 +1,9 @@ -Building A Simple Task | Community Health Toolkit +Building A Simple Task | Community Health Toolkit

    Building A Simple Task

    Writing and testing a simple task

    Tasks prompt users to complete activities on a programmatic schedule. This guide will explain how to write a task which prompts CHW users to complete an assessment app form for new patients within 7 days of registration.

    • Creating a straight-forward task
    • Running and testing that task

    Prerequisites

    Implementation Steps

    Create a tasks.js file (this may have already been created by the initialise-project-layout command).

    1. Define a Simple Task

    The appearance, behaviour, and schedule of tasks are all controlled through the JavaScript in the tasks.js file. Let’s start in that file with a simple first task:

    module.exports = [{
    + Create project issue

    Building A Simple Task

    Writing and testing a simple task

    Tasks prompt users to complete activities on a programmatic schedule. This guide will explain how to write a task which prompts CHW users to complete an assessment app form for new patients within 7 days of registration.

    • Creating a straight-forward task
    • Running and testing that task

    Prerequisites

    Implementation Steps

    Create a tasks.js file (this may have already been created by the initialise-project-layout command).

    1. Define a Simple Task

    The appearance, behaviour, and schedule of tasks are all controlled through the JavaScript in the tasks.js file. Let’s start in that file with a simple first task:

    module.exports = [{
       name: 'assessment-after-registration',
       title: 'First Assessment',
       icon: 'icon-healthcare',
    @@ -325,7 +325,8 @@
     Workflows

    Building connections between people, actions, and data systems

    Design System > Best Practices : Anatomy of a Task

    This document covers the configuration best practices of forms, tasks, targets, and contact profiles when building your own community health app.

    -

    Last modified 25.04.2022: Fix broken image and link (fab814a2)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/tutorials/tasks-2/index.html b/apps/tutorials/tasks-2/index.html index 2d8c7174bb..4b4aba2026 100644 --- a/apps/tutorials/tasks-2/index.html +++ b/apps/tutorials/tasks-2/index.html @@ -1,9 +1,9 @@ -Building A Complex Task (Optional) | Community Health Toolkit +Building A Complex Task (Optional) | Community Health Toolkit

    Building A Complex Task (Optional)

    Building a more complex task

    Tasks prompt users to complete activities on a programmatic schedule. This tutorial will guide you through the development of an advanced task. This is an optional tutorial and is not required to get started with CHT Application development.

    • Create a task with a complex follow-up schedule
    • Use a 3rd party JavaScript library luxon to make Date/Time calculations easier
    • Pass information from the task into the action app form
    • Custom logic for resolving a task

    Prerequisites

    Scenario

    This scenario is loosely based on the Pregnancy Visit Task from the Maternal and Newborn Health Reference App.

    We expectations for the task are:

    1. Only CHW users should be prompted to complete pregnancy visits
    2. Pregnancy visits should appear for pregnant patients after their pregnancy registration
    3. Pregnancy visits should be scheduled eight times between the last mentrual period (LMP) and delivery.
    4. A pregnancy visit should be skipped if an assessment followup is completed within the scheduled window for the pregancy visit.
    5. The first pregnancy visit should prompt the CHW to ask some additional questions

    Developing the task

    This solution relies on the library luxon for parsing and manipulating dates and times. Let’s start by installing it locally:

    npm install --save-dev luxon
    + Create project issue

    Building A Complex Task (Optional)

    Building a more complex task

    Tasks prompt users to complete activities on a programmatic schedule. This tutorial will guide you through the development of an advanced task. This is an optional tutorial and is not required to get started with CHT Application development.

    • Create a task with a complex follow-up schedule
    • Use a 3rd party JavaScript library luxon to make Date/Time calculations easier
    • Pass information from the task into the action app form
    • Custom logic for resolving a task

    Prerequisites

    Scenario

    This scenario is loosely based on the Pregnancy Visit Task from the Maternal and Newborn Health Reference App.

    We expectations for the task are:

    1. Only CHW users should be prompted to complete pregnancy visits
    2. Pregnancy visits should appear for pregnant patients after their pregnancy registration
    3. Pregnancy visits should be scheduled eight times between the last mentrual period (LMP) and delivery.
    4. A pregnancy visit should be skipped if an assessment followup is completed within the scheduled window for the pregancy visit.
    5. The first pregnancy visit should prompt the CHW to ask some additional questions

    Developing the task

    This solution relies on the library luxon for parsing and manipulating dates and times. Let’s start by installing it locally:

    npm install --save-dev luxon
     

    And then in tasks.js we can analyse this solution:

    const { DateTime } = require('luxon');
     
     module.exports = {
    @@ -358,7 +358,8 @@
     tasks.js

    Tasks: Definition of tasks shown to app users

    CHT Applications > Examples > Maternal & Newborn Health

    Reference application for maternal and newborn care for CHW’s using a mobile app

    -

    Last modified 06.04.2023: 1008 - Fix Partials (#1009) (4938b3e0)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/apps/tutorials/user-management-tool/index.html b/apps/tutorials/user-management-tool/index.html index 9c2dbb55b3..e084cb1726 100644 --- a/apps/tutorials/user-management-tool/index.html +++ b/apps/tutorials/user-management-tool/index.html @@ -1,9 +1,9 @@ -CHT User Management tool | Community Health Toolkit +CHT User Management tool | Community Health Toolkit

    CHT User Management tool

    How to use and configure a user management tool for your CHT project.

    The Community Health Toolkit (CHT) is highly configurable and can be customized to support multiple hierarchies and users in the health care system. The CHT user management tool is a user friendly web application that works with the CHT to decentralize the user management process to the subnational levels, increasing efficiency and accuracy. This guide highlights steps for setting up and configuring the user management tool. + Create project issue

    CHT User Management tool

    How to use and configure a user management tool for your CHT project.

    The Community Health Toolkit (CHT) is highly configurable and can be customized to support multiple hierarchies and users in the health care system. The CHT user management tool is a user friendly web application that works with the CHT to decentralize the user management process to the subnational levels, increasing efficiency and accuracy. This guide highlights steps for setting up and configuring the user management tool. The guide has been tailored for specific CHT-supported national community health information systems. Partners and ministries of health are advised to customize the tool to fit their specific needs. The tool provides templates that can be downloaded and populated.

    The user management tool has various features and fuctionalities including:

    • Creation of single or bulk (many) users.
    • Replacing of existing users
    • Moving of an existing place
      • Users can access bulk user templates that they can populate.
      • One can validate the entries before the upload.
      • Download the user credentials upon upload.
    • Validation of input for completeness and accuracy. The tool identifies errors that the can be corrected.

    Configuring the CHT User Management tool

    Implementation Steps

    1. Clone the cht-user-management repository.

    2. Create a new folder under src/config with your required instance eg. chis-znz

    3. Within that new folder create a config.json file with the following parameters

      PropertyTypeDescription
      domainsArrayControls the list of instances which the user can login to
      domains.friendlystringFriendly name for the instance (eg. “Migori”)
      domains.domainstringHostname for the instance (eg. “migori-echis.health.go.ke”)
      domains.useHttpbooleanWhether to make an insecure connection (http) to the hostname (defaults to false)
      contact_typesArrayOne element for each type of user which can be created by the system
      contact_types.namestringThe name of the contact_type as it appears in the app’s base_settings.json
      contact_types.friendlystringFriendly name of the contact type
      contact_types.contact_typestringThe contact_type of the primary contact. As defined in base_settings.json
      contact_types.contact_friendlystringFriendly name of the primary contact type
      contact_types.user_rolestring[]A list of allowed user roles. If only one is provided, it will be used by default.
      contact_types.username_from_placebooleanWhen true, the username is generated from the place’s name. When false, the username is generated from the primary contact’s name. Default is false.
      contact_types.hierarchyArrayDefines how this contact_type is connected into the hierarchy. An element with level:1 (parent) is required and additional elements can be provided to support disambiguation. See ConfigProperty.
      contact_types.hierarchy.levelintegerThe hierarchy element with level:1 is the parent, level:3 is the great grandparent.
      contact_types.replacement_propertyPropertyDefines how this contact_type is described when being replaced. The property_name is always replacement. See ConfigProperty.
      contact_types.place_propertiesArrayDefines the attributes which are collected and set on the user’s created place. See ConfigProperty.
      contact_types.contact_propertiesArrayDefines the attributes which are collected and set on the user’s primary contact doc. See ConfigProperty.
      contact_types.deactivate_users_on_replacebooleanControls what should happen to the defunct contact and user documents when a user is replaced. When false, the contact and user account will be deleted. When true, the contact will be unaltered and the user account will be assigned the role deactivated. This allows for account restoration.
      logoBase64Image in base64Logo image for your project
    4. Add reference to your configuration folder(that you have just added above) in src/config/config-factory.ts. For example import znzConfig from './chis-znz'

    5. Configure the config.json file.

    ConfigProperty

    The ConfigProperty is a data structure used several times within the config.json file and defines a property on an object. The ConfigProperty include:

    PropertyTypeDescription
    friendly_namestringDefines how this data will be labeled in CSV files and throughout the user experience.
    property_namestringDefines how the value will be stored on the object.
    typeConfigPropertyTypeDefines the validation rules, and auto-formatting rules. See ConfigPropertyType.
    parameteranySee ConfigPropertyType.
    requiredbooleanTrue if the object should not exist without this information.

    ConfigPropertyType

    The ConfigPropertyType defines a property’s validation rules and auto-formatting rules. The optional parameter information alters the behavior of the ConfigPropertyType.

    TypeValidation RulesAuto Formatting RulesValidatorParameter
    stringMust be definedRemoves double whitespaces, leading or trailing whitespaces, and any character which is not alphanumeric or ()\-'None
    nameMust be definedSame as string + title case + parameter behaviorOne or more regexes which are removed from the value when matched (eg. "parameter": ["\\sCHU"] will format this CHU into This)
    regexMust match the regex captured by parameterSame as stringA regex which must be matched to pass validation (eg. "parameter": "^\\d{6}$" will accept only 6 digit numbers)
    phoneA valid phone number for the specified localityAuto formatting provided by libphonenumberTwo letter country code specifying the locality of phone number (eg. "parameter": "KE")
    generatedNone. No user inputs.Uses LiquidJS templates to generate dataNoneDetails
    select_oneSingle choice from a list of optionsSame as stringNoneDictionary where the keys are the option values and the values are the corresponding labels
    select_multipleMultiple choice from a list of optionsSame as stringNoneSame as select_one
    noneNoneNoneNone

    The Generated ConfigPropertyType

    ContactProperties with type: "generated" use the LiquidJS template engine to populate a property with data. Here is an example of some configuration properties which use "type": "generated":

    {
       "place_properties": [
    @@ -330,7 +330,8 @@
     Quick Guides >
     Data >
     Bulk Load Users

    How to create users in bulk

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/contribute/code-of-conduct/index.html b/contribute/code-of-conduct/index.html index 3c415e535e..129fbfbba1 100644 --- a/contribute/code-of-conduct/index.html +++ b/contribute/code-of-conduct/index.html @@ -1,9 +1,9 @@ -Code of Conduct | Community Health Toolkit +Code of Conduct | Community Health Toolkit

    Code of Conduct

    The Code of Conduct for the CHT community

    All maintainers and contributors in this community are required to act according to the following Code of Conduct. These guidelines help steer our interactions and help us provide and ensure a safe environment for everyone.

    Our Standards

    Examples of behavior that contributes to creating a positive environment + Create project issue

    Code of Conduct

    The Code of Conduct for the CHT community

    All maintainers and contributors in this community are required to act according to the following Code of Conduct. These guidelines help steer our interactions and help us provide and ensure a safe environment for everyone.

    Our Standards

    Examples of behavior that contributes to creating a positive environment include:

    • Using welcoming and inclusive language
    • Being respectful of differing viewpoints and experiences
    • Gracefully accepting constructive criticism
    • Focusing on what is best for the community
    • Showing empathy towards other community members

    Examples of unacceptable behavior by participants include:

    • The use of sexualized language or imagery and unwelcome sexual attention or advances
    • Trolling, insulting/derogatory comments, and personal or political attacks
    • Public or private harassment
    • Publishing others’ private information, such as a physical or electronic address, without explicit permission
    • Other conduct which could reasonably be considered inappropriate in a @@ -318,7 +318,8 @@ Further details of specific enforcement policies may be posted separately.

      Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project’s leadership.

      Attribution

      This Code of Conduct is adapted from the Contributor Covenant, version 1.4. For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq

      -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/contribute/code/android/development-setup/index.html b/contribute/code/android/development-setup/index.html index 0717eb845c..4d751e7ffa 100644 --- a/contribute/code/android/development-setup/index.html +++ b/contribute/code/android/development-setup/index.html @@ -1,9 +1,9 @@ -Android Dev Environment | Community Health Toolkit +Android Dev Environment | Community Health Toolkit

    Android Dev Environment

    Instructions for setting up the development environment

    The following instructions allows you to setup a development environment for the CHT Android apps, and the CHT Gateway app as well.

    Finally, you will learn how to assemble the app, run the tests, and how to choose the right artifacts when installing or publishing the apps.

    Requirements

    • Java 17+ (OpenJDK versions work).
    • Android SDK, and optionally Android Studio.
    • The adb command for debugging and get the logs.
    • The source code. To run all the tests in the CHT Android app you need to clone also the submodules: git clone --recurse-submodules https://github.com/medic/cht-android.git.
    • The make command.
    • If you are going to build a new flavor (CHT Android), you also need to have installed: head, xxd, openssl and apksigner.

    Gradle is also used but it’s downloaded and installed in the user space the first time make is executed. You can also build and launch the app with Android Studio.

    Below are the instructions of how to install and setup some of the tools required.

    Install

    Java

    Java 17+ needs to be installed. The bin/ folder of the JDK must added into the $PATH environment variable, and it’s recommended to have $JAVA_HOME pointing to the JDK folder as well.

    To install different versions of Java and without the need to have root permissions, checkout Sdkman!, if you are familiar with tools like nvm or rvm, this tool is pretty much the same for Java, and the command takes care of adding the selected JDK to the $PATH variable and to set the $JAVA_HOME variable when switching across different versions.

    Android Studio and the SDK

    Android Studio is the full package: the IDE based on IntelliJ IDEA, the Android SDK and the SDK Manager with the UI to manage different packages visually, while the SDK alone only includes the command line tools like the sdkmanager CLI.

    You don’t need the IDE to build the app or to install a “debug” version in a device, or get the logs, but it’s recommended if you also want to debug the app or modify the code.

    If you install Android Studio it’s still recommended to download and setup the command line tools separately to be able to use them without the IDE.

    The binary folder of the command also need to be added to the $PATH, and $ANDROID_HOME pointing to the root of the SDK. Moreover, the SDK requires to be stored in the $ANDROID_HOME and inside a folder called latest.

    Here are the steps from the command line you can follow to install the CLI tools once downloaded the zip file:

    mkdir -p Android/Sdk/cmdline-tools
    + Create project issue

    Android Dev Environment

    Instructions for setting up the development environment

    The following instructions allows you to setup a development environment for the CHT Android apps, and the CHT Gateway app as well.

    Finally, you will learn how to assemble the app, run the tests, and how to choose the right artifacts when installing or publishing the apps.

    Requirements

    • Java 17+ (OpenJDK versions work).
    • Android SDK, and optionally Android Studio.
    • The adb command for debugging and get the logs.
    • The source code. To run all the tests in the CHT Android app you need to clone also the submodules: git clone --recurse-submodules https://github.com/medic/cht-android.git.
    • The make command.
    • If you are going to build a new flavor (CHT Android), you also need to have installed: head, xxd, openssl and apksigner.

    Gradle is also used but it’s downloaded and installed in the user space the first time make is executed. You can also build and launch the app with Android Studio.

    Below are the instructions of how to install and setup some of the tools required.

    Install

    Java

    Java 17+ needs to be installed. The bin/ folder of the JDK must added into the $PATH environment variable, and it’s recommended to have $JAVA_HOME pointing to the JDK folder as well.

    To install different versions of Java and without the need to have root permissions, checkout Sdkman!, if you are familiar with tools like nvm or rvm, this tool is pretty much the same for Java, and the command takes care of adding the selected JDK to the $PATH variable and to set the $JAVA_HOME variable when switching across different versions.

    Android Studio and the SDK

    Android Studio is the full package: the IDE based on IntelliJ IDEA, the Android SDK and the SDK Manager with the UI to manage different packages visually, while the SDK alone only includes the command line tools like the sdkmanager CLI.

    You don’t need the IDE to build the app or to install a “debug” version in a device, or get the logs, but it’s recommended if you also want to debug the app or modify the code.

    If you install Android Studio it’s still recommended to download and setup the command line tools separately to be able to use them without the IDE.

    The binary folder of the command also need to be added to the $PATH, and $ANDROID_HOME pointing to the root of the SDK. Moreover, the SDK requires to be stored in the $ANDROID_HOME and inside a folder called latest.

    Here are the steps from the command line you can follow to install the CLI tools once downloaded the zip file:

    mkdir -p Android/Sdk/cmdline-tools
     unzip commandlinetools-linux-7583922_latest.zip
     mv cmdline-tools/ Android/Sdk/cmdline-tools/latest/
     

    Then, to add the environment variables required, you can add the following to your ~/.bashrc file:

    export ANDROID_HOME="$HOME/Android/Sdk"
    @@ -314,7 +314,8 @@
     Code >
     Android Code >
     Releasing

    Instructions for releasing Android Apps

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/code/android/index.html b/contribute/code/android/index.html index 38c4dfadad..5c2efc87f0 100644 --- a/contribute/code/android/index.html +++ b/contribute/code/android/index.html @@ -1,9 +1,9 @@ -Contributing Android Code | Community Health Toolkit +Contributing Android Code | Community Health Toolkit

    Contributing Android Code

    How to contribute to code to the CHT Android app

    Android Dev Environment

    Instructions for setting up the development environment

    Releasing

    Instructions for releasing Android Apps


    Contributor Handbook > + Create project issue

    Contributing Android Code

    How to contribute to code to the CHT Android app

    Android Dev Environment

    Instructions for setting up the development environment

    Releasing

    Instructions for releasing Android Apps


    Contributor Handbook > Code > CHT Core Code > CHT Core dev environment setup

    Getting your local machine ready to do development work on CHT Core.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/code/android/index.xml b/contribute/code/android/index.xml index 5dbd437e07..6ae2662148 100644 --- a/contribute/code/android/index.xml +++ b/contribute/code/android/index.xml @@ -1,162 +1,4 @@ -Community Health Toolkit – Contributing Android Codehttps://docs.communityhealthtoolkit.org/contribute/code/android/Recent content in Contributing Android Code on Community Health ToolkitHugo -- gohugo.ioenContribute: Android Dev Environmenthttps://docs.communityhealthtoolkit.org/contribute/code/android/development-setup/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/android/development-setup/ -<div class="pageinfo pageinfo-primary"> -<p>The following instructions allows you to setup a development environment for the <strong><a href="https://github.com/medic/cht-android">CHT Android</a></strong> apps, and the <strong><a href="https://github.com/medic/cht-gateway">CHT Gateway</a></strong> app as well.</p> -<p>Finally, you will learn how to assemble the app, run the tests, and how to choose the right artifacts when installing or publishing the apps.</p> -</div> -<h2 id="requirements">Requirements</h2> -<ul> -<li>Java 17+ (OpenJDK versions work).</li> -<li>Android SDK, and optionally Android Studio.</li> -<li>The <code>adb</code> command for debugging and get the logs.</li> -<li>The source code. To run all the tests in the CHT Android app you need to clone also the submodules: <code>git clone --recurse-submodules https://github.com/medic/cht-android.git</code>.</li> -<li>The <code>make</code> command.</li> -<li>If you are going to build a new flavor (CHT Android), you also need to have installed: <code>head</code>, <code>xxd</code>, <code>openssl</code> and <code>apksigner</code>.</li> -</ul> -<p><strong>Gradle</strong> is also used but it&rsquo;s downloaded and installed in the user space the first time <code>make</code> is executed. You can also build and launch the app with <a href="#android-studio">Android Studio</a>.</p> -<p>Below are the instructions of how to install and setup some of the tools required.</p> -<h2 id="install">Install</h2> -<h3 id="java">Java</h3> -<p>Java 17+ needs to be installed. The <code>bin/</code> folder of the JDK must added into the <code>$PATH</code> environment variable, and it&rsquo;s recommended to have <code>$JAVA_HOME</code> pointing to the JDK folder as well.</p> -<p>To install different versions of Java and without the need to have root permissions, checkout <a href="https://sdkman.io/">Sdkman!</a>, if you are familiar with tools like <code>nvm</code> or <code>rvm</code>, this tool is pretty much the same for Java, and the command takes care of adding the selected JDK to the <code>$PATH</code> variable and to set the <code>$JAVA_HOME</code> variable when switching across different versions.</p> -<h3 id="android-studio-and-the-sdk">Android Studio and the SDK</h3> -<p>Android Studio is the full package: the IDE based on IntelliJ IDEA, the Android SDK and the SDK Manager with the UI to manage different packages visually, while the SDK alone only includes the command line tools like the <code>sdkmanager</code> CLI.</p> -<p>You don&rsquo;t need the IDE to build the app or to install a &ldquo;debug&rdquo; version in a device, or get the logs, but it&rsquo;s recommended if you also want to debug the app or modify the code.</p> -<ul> -<li>Android Studio: download from <a href="https://developer.android.com/studio">https://developer.android.com/studio</a></li> -<li>Command line tools only: download from <a href="https://developer.android.com/studio">https://developer.android.com/studio</a>. Scroll down to the &lsquo;Command Line Tools Only&rsquo; section of the page.</li> -</ul> -<p>If you install Android Studio it&rsquo;s still recommended to download and setup the command line tools separately to be able to use them without the IDE.</p> -<p>The binary folder of the command also need to be added to the <code>$PATH</code>, and <code>$ANDROID_HOME</code> pointing to the root of the SDK. Moreover, the SDK requires to be stored in the <code>$ANDROID_HOME</code> and inside a folder called <code>latest</code>.</p> -<p>Here are the steps from the command line you can follow to install the CLI tools once downloaded the zip file:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mkdir -p Android/Sdk/cmdline-tools -</span></span><span style="display:flex;"><span>unzip commandlinetools-linux-7583922_latest.zip -</span></span><span style="display:flex;"><span>mv cmdline-tools/ Android/Sdk/cmdline-tools/latest/ -</span></span></code></pre></div><p>Then, to add the environment variables required, you can add the following to your <code>~/.bashrc</code> file:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#204a87">export</span> <span style="color:#000">ANDROID_HOME</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;</span><span style="color:#000">$HOME</span><span style="color:#4e9a06">/Android/Sdk&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87">export</span> <span style="color:#000">PATH</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;</span><span style="color:#000">$PATH</span><span style="color:#4e9a06">:</span><span style="color:#000">$ANDROID_HOME</span><span style="color:#4e9a06">/cmdline-tools/latest/bin&#34;</span> -</span></span></code></pre></div><h3 id="debug-tool-adb">Debug tool <code>adb</code></h3> -<p>Old SDK distributions used to have it pre-packaged, now you have to install it separately. Once installed SDK following the steps above, you can install the latest version of <code>adb</code> with:</p> -<pre tabindex="0"><code>sdkmanager --install platform-tools -</code></pre><p>If you also installed Android Studio you can use the <a href="https://developer.android.com/studio/intro/update#sdk-manager">SDK Manager</a> instead.</p> -<p>Finally edit again the <code>$PATH</code> environment variable to add the adb path: <code>$ANDROID_HOME/platform-tools</code>.</p> -<h3 id="apksigner"><code>apksigner</code></h3> -<p>This tool is used automatically by the Android SDK for signing APKs, and to check the certificate of a given APK, so chances are that after installing the SDK following the steps above you already have it installed, but not configured in the <code>$PATH</code> that is needed to manually check APKs signature.</p> -<p>The CLI is part of other CLI tolls under the <code>build-tools</code> package, and multiple build tools package can be installed, so check what versions you have under the <code>$ANDROID_HOME/build-tools</code> folder, and add the most up to date to the <code>$PATH</code> folder. E.g. if you have the version 30.0.3 installed in you computer, apksigner should be installed at <code>$ANDROID_HOME/build-tools/30.0.3/apksigner</code>, so add the <code>$ANDROID_HOME/build-tools/30.0.3</code> folder to the <code>$PATH</code> variable.</p> -<p>In case you don&rsquo;t have it installed or want to install a newer version, checkout the versions available with <code>sdkmanager --list</code>. You will see a table with a list of installed and available packages, not just the build tools.</p> -<p>To install the version 31.0.0: <code>sdkmanager --install 'build-tools;31.0.0'</code>. Then update or add it to the <code>$PATH</code> variable.</p> -<h2 id="development">Development</h2> -<h3 id="flavor-selection">Flavor selection</h3> -<p><em>Only CHT Android</em></p> -<p>Some <code>make</code> targets support the flavor as <code>make flavor=[Flavor] [task]</code>, where <code>[Flavor]</code> is the branded version with the first letter capitalized. The <code>[task]</code> is the action to execute: <code>deploy</code>, <code>assemble</code>, <code>lint</code>, etc.</p> -<p>The default value for <code>flavor</code> is <code>Unbranded</code>, e.g. executing <code>make deploy</code> will assemble and install that flavor, while executing <code>make flavor=Medicmobilegamma deploy</code> will do the same for the <em>Medicmobilegamma</em> brand.</p> -<p>See the <a href="https://github.com/medic/cht-android/blob/master/Makefile">Makefile</a> for more details.</p> -<h3 id="build-and-assemble">Build and assemble</h3> -<p>To build and assemble the apps within the console use:</p> -<pre><code>make assemble -</code></pre> -<p>The command above builds and assembles the <em>debug</em> and <em>release</em> APKs of the apps, and for the CHT-Android project the Unbranded flavor is built and assembled by default.</p> -<p>Each APK will be generated and stored in <code>build/outputs/apk/[flavor]/[debug|release]/</code>, for example after assembling the <em>Medicmobilegamma</em> flavor with <code>make flavor=Medicmobilegamma assemble</code>, the <em>release</em> versions of the APKs generated are stored in <code>build/outputs/apk/medicmobilegamma/release/</code>.</p> -<p>To assemble other flavors, use the following command: <code>make flavour=[Flavor] assemble</code>. See the <a href="#flavor-selection">Flavor selection</a> section for more details about <code>make</code> commands.</p> -<p>To create the <code>.aab</code> bundle file, use <code>make bundle</code>, although signed versions are generated when <a href="https://docs.communityhealthtoolkit.org/contribute/code/android/releasing/">releasing</a>, and the Play Store requires the AAB to be signed with the right key.</p> -<p>To clean the APKs and compiled resources: <code>make clean</code>.</p> -<h3 id="testing">Testing</h3> -<p>To execute unit tests and static analysis, run: <code>make test</code>.</p> -<p>To generate a unit test coverage report, run: <code>make test-coverage</code>.</p> -<p>Find the generated report in: -<code>build/reports/jacoco/makeUnbrandedDebugUnitTestCoverageReport/html/index.html</code></p> -<h4 id="static-checks">Static checks</h4> -<p><em>Only CHT Android</em></p> -<p>To only execute the <strong>linter checks</strong>, run: <code>make lint</code>.</p> -<h4 id="instrumentation-tests">Instrumentation Tests</h4> -<p><em>Only CHT Android</em></p> -<p>The UI tests run on a device.</p> -<ol> -<li>Uninstall previous versions of the app, otherwise an <code>InstallException: INSTALL_FAILED_VERSION_DOWNGRADE</code> can cause tests to fail.</li> -<li>Select English as default language in the device.</li> -<li>Ensure you meet all the <a href="#requirements">Requirements</a>.</li> -<li>Execute: <code>make test-ui-all</code>.</li> -</ol> -<h4 id="shell-tests">Shell tests</h4> -<p><em>Only CHT Android</em></p> -<p>The project has bash tests that verify the Make targets used to create and manage the keystores used to sign the apps. Use <code>make test-bash-keystore</code> to run them. In CI they are executed in Linux and MacOS VMs.</p> -<p>If you get an error like <code>make: ./src/test/bash/bats/bin/bats: Command not found</code>, it&rsquo;s because you cloned the project without the <code>--recurse-submodules</code> git argument. Execute first <code>git submodule update --init</code> to clone -the submodules within the cht-android folder.</p> -<h4 id="connecting-to-the-server-locally">Connecting to the server locally</h4> -<p><em>Only CHT Android</em></p> -<p>Refer to the <a href="https://docs.communityhealthtoolkit.org/contribute/code/core/dev-environment/#nginx-local-ip">CHT Core Developer Guide</a>.</p> -<h4 id="manually-testing-with-older-android-versions">Manually testing with older Android versions</h4> -<p><em>Only CHT Android</em></p> -<p>Later versions of the CHT, only support <a href="https://docs.communityhealthtoolkit.org/core/releases/#dependencies">running on Chrome/Webview 90+</a>. Some older versions of Android will not have a new enough Android System Webview to be able to run the CHT webapp. This is particularly true of emulated Android devices, which typically are configured to not receive updates. To test the CHT functionality on these devices you must manually upgrade the version of the Chrome/Webview apk used as the Android System Webview.</p> -<p>To upgrade the necessary apk on an emulated Android device:</p> -<ul> -<li>Start by using the <code>Google APIs</code> variant of the Android image. The base AOSP image bakes in the <code>com.android.webview</code> package, making it difficult to update.</li> -<li>Depending on your version of Android, you will need to manually download a new version of the apk (make sure to get the correct variant for your architecture): -<ul> -<li>For Android 7-9, you should download a new version of <code>com.android.chrome</code> (e.g. from <a href="https://www.apkmirror.com/apk/google-inc/chrome/">apkMirror</a>).</li> -<li>For Android 10+, you should download a new version of <code>com.google.android.webview</code> (e.g. from <a href="https://www.apkmirror.com/apk/google-inc/android-system-webview/">apkMirror</a>).</li> -</ul> -</li> -<li>Then, you can use adb to install the apk into the device: <code>adb install -r *your_apk*.apk</code></li> -</ul> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -<p>Testing using an Android 5-6 device can be more challenging. The <code>com.android.webview</code> package seems to be baked into the system partition regardless of the image variant. It is possible use a custom ROM (e.g. CyanogenMod) with an upgraded WebView version on a physical device. Another approach to testing with older versions of Android is to simply test against an older version of the CHT (e.g. CHT <code>3.x.</code> only requires Chrome/Webview 53). The functionality of cht-android can still be validated by interacting with this old CHT version.</p> -<p>If using the docker-helper to deploy CHT instances for testing with old Android versions, be aware that the <code>local-ip.medicmobile.org</code> SSL certificates may not work with the device (because the root certificate is not recognized). On Android 5-6 you can manually install the root certificate via Settings &gt; Security &gt; Install from SD Card.</p> -</div> -<h3 id="android-studio">Android Studio</h3> -<p>The <a href="https://developer.android.com/studio">Android Studio</a> can be used to build and launch the app instead. Be sure to select the right flavor from the <em>Build Variants</em> dialog (see <a href="https://developer.android.com/studio/run#changing-variant">Change the build variant</a>). To launch the app in an emulator, you need to uncomment the code that has the strings for the <code>x86</code> or the <code>x86_64</code> architecture in the <code>android</code> / <code>splits</code> / <code>include</code> sections of the <code>build.gradle</code> file.</p> -<h3 id="artifact-formats">Artifact formats</h3> -<p>When building the app there are two output formats you can use: Android App Bundle or APK.</p> -<h4 id="android-app-bundles">Android App Bundles</h4> -<p><em>Only CHT Android</em></p> -<p>The <a href="https://github.com/medic/cht-android/blob/master/.github/workflows/publish.yml">publish</a> script in CI produces multiple AABs for publishing to the <strong>Google Play Store</strong>, so the generated <code>.aab</code> files need to be uploaded instead of the <code>.apk</code> files if the Play Store require so. Old apps published for the first time before Aug 1, 2021 can be updated with the APK format. -If distributing AABs via the Play Store, upload all AABs and it will automatically choose the right one for the target device. The AABs are named as follows: <code>cht-android-{version}-{brand}-release.aab</code>.</p> -<h4 id="apks">APKs</h4> -<p>For compatibility with a wide range of devices, the <a href="https://github.com/medic/cht-android/blob/master/.github/workflows/publish.yml">publish</a> script in CI produces multiple APKs. The two variables are the instruction set used by the device&rsquo;s CPU, and the supported Android version. When sideloading the application, it is essential to pick the correct APK or the application may crash.</p> -<p>If distributing APKs via the Play Store, upload all APKs and it will automatically choose the right one for the target device.</p> -<p>To help you pick which APK to install, you can find information about the version of Android and the CPU in the About section of the phone&rsquo;s settings menu.</p> -<p>The APKs are named as follows: <code>cht-android-{version}-{brand}-{instruction-set}-release.apk</code>.</p> -<table> -<thead> -<tr> -<th>Instruction set</th> -<th>Android version</th> -<th>Notes</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>arm64-v8a</code></td> -<td>5+</td> -<td>Preferred. Use this APK if possible.</td> -</tr> -<tr> -<td><code>armeabi-v7a</code></td> -<td>5+</td> -<td>Built as support for older devices, ignore if possible.</td> -</tr> -</tbody> -</table>Contribute: Releasinghttps://docs.communityhealthtoolkit.org/contribute/code/android/releasing/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/android/releasing/ -<p>All Medic&rsquo;s Android projects automatically build, sign, and release builds via GitHub Actions. The following guide applies to any of these apps, although the last 2 are in maintenance mode (links pointing to the release sections):</p> -<ul> -<li><a href="https://github.com/medic/cht-android/releases">cht-android</a></li> -<li><a href="https://github.com/medic/cht-gateway/releases">cht-gateway</a></li> -<li><a href="https://github.com/medic/medic-collect/releases">medic-collect</a></li> -<li><a href="https://github.com/medic/rdt-capture/releases">rdt-capture</a></li> -</ul> -<h2 id="alpha-for-release-testing">Alpha for release testing</h2> -<ol> -<li>Ensure all issues for this release have passed AT and been merged into <code>master</code>. You can also create an alpha release from a feature branch, to provide the needed <code>.apk</code> files to the QA team.</li> -<li>Create a git tag starting with <code>v</code> and ending with the alpha version, e.g. <code>git tag v1.2.3-alpha.1</code>, and push the tag to GitHub (<code>git push --tags</code>). For features branches, you can add the name of the branch or whatever keyword helps to identify the release, e.g. <code>v1.2.3-alpha.dark-theme.4</code>.</li> -<li>Creating the tag will trigger the building and signing of the app in CI. The release-ready APKs are available for side-loading from the Releases section in the project (e.g. <a href="https://github.com/medic/cht-android/releases">CHT-Android Releases</a>), along with the AABs that the Google Play Store may require. Note that the created release on GitHub with the generated artifacts will not be explicitly linked to the tag because the release is in a draft state.</li> -<li><strong>Side-Load</strong>: for testing internally, or apps that are not published in the Play Store like Collect and Gateway: Navigate to the GitHub Releases page (linked above) and download the relevant APKs for distribution.</li> -<li>Announce the release in <em>#quality-assurance</em>.</li> -</ol> -<h2 id="production-release">Production release</h2> -<ol> -<li>Update the CHANGELOG with the details of what&rsquo;s in this release.</li> -<li>Repeat steps 2-3 from the above <a href="#alpha-for-release-testing">alpha release section</a> with the naming convention <code>v&lt;major&gt;.&lt;minor&gt;.&lt;patch&gt;</code>.</li> -<li>The CI build for the tag will create a new draft release on GitHub. Include the release notes from the CHANGELOG in the description of the release and publish the release on GitHub.</li> -<li><a href="https://docs.communityhealthtoolkit.org/apps/guides/android/publishing/">Publish</a> in the Play Store. For the CHT-Android app, the &ldquo;reference&rdquo; apps (<code>medicmobilegamma</code> and <code>unbranded</code>) must be published in the Play Store. Other channels such as F-Droid can also be used to publish the app.</li> -<li>Announce the release on the <a href="https://forum.communityhealthtoolkit.org">CHT forum</a>, under the &ldquo;Product - Releases&rdquo; category.</li> -</ol> \ No newline at end of file +Contributing Android Code on Community Health Toolkithttps://docs.communityhealthtoolkit.org/contribute/code/android/Recent content in Contributing Android Code on Community Health ToolkitHugo -- gohugo.ioenAndroid Dev Environmenthttps://docs.communityhealthtoolkit.org/contribute/code/android/development-setup/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/android/development-setup/The following instructions allows you to setup a development environment for the CHT Android apps, and the CHT Gateway app as well. +Finally, you will learn how to assemble the app, run the tests, and how to choose the right artifacts when installing or publishing the apps. +Requirements Java 17+ (OpenJDK versions work). Android SDK, and optionally Android Studio. The adb command for debugging and get the logs. The source code.Releasinghttps://docs.communityhealthtoolkit.org/contribute/code/android/releasing/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/android/releasing/All Medic&rsquo;s Android projects automatically build, sign, and release builds via GitHub Actions. The following guide applies to any of these apps, although the last 2 are in maintenance mode (links pointing to the release sections): +cht-android cht-gateway medic-collect rdt-capture Alpha for release testing Ensure all issues for this release have passed AT and been merged into master. You can also create an alpha release from a feature branch, to provide the needed . \ No newline at end of file diff --git a/contribute/code/android/releasing/index.html b/contribute/code/android/releasing/index.html index c0b2081ced..e1a4a43068 100644 --- a/contribute/code/android/releasing/index.html +++ b/contribute/code/android/releasing/index.html @@ -1,9 +1,9 @@ -Releasing | Community Health Toolkit +Releasing | Community Health Toolkit

    Releasing

    Instructions for releasing Android Apps

    All Medic’s Android projects automatically build, sign, and release builds via GitHub Actions. The following guide applies to any of these apps, although the last 2 are in maintenance mode (links pointing to the release sections):

    Alpha for release testing

    1. Ensure all issues for this release have passed AT and been merged into master. You can also create an alpha release from a feature branch, to provide the needed .apk files to the QA team.
    2. Create a git tag starting with v and ending with the alpha version, e.g. git tag v1.2.3-alpha.1, and push the tag to GitHub (git push --tags). For features branches, you can add the name of the branch or whatever keyword helps to identify the release, e.g. v1.2.3-alpha.dark-theme.4.
    3. Creating the tag will trigger the building and signing of the app in CI. The release-ready APKs are available for side-loading from the Releases section in the project (e.g. CHT-Android Releases), along with the AABs that the Google Play Store may require. Note that the created release on GitHub with the generated artifacts will not be explicitly linked to the tag because the release is in a draft state.
    4. Side-Load: for testing internally, or apps that are not published in the Play Store like Collect and Gateway: Navigate to the GitHub Releases page (linked above) and download the relevant APKs for distribution.
    5. Announce the release in #quality-assurance.

    Production release

    1. Update the CHANGELOG with the details of what’s in this release.
    2. Repeat steps 2-3 from the above alpha release section with the naming convention v<major>.<minor>.<patch>.
    3. The CI build for the tag will create a new draft release on GitHub. Include the release notes from the CHANGELOG in the description of the release and publish the release on GitHub.
    4. Publish in the Play Store. For the CHT-Android app, the “reference” apps (medicmobilegamma and unbranded) must be published in the Play Store. Other channels such as F-Droid can also be used to publish the app.
    5. Announce the release on the CHT forum, under the “Product - Releases” category.

    CHT Applications > + Create project issue

    Releasing

    Instructions for releasing Android Apps

    All Medic’s Android projects automatically build, sign, and release builds via GitHub Actions. The following guide applies to any of these apps, although the last 2 are in maintenance mode (links pointing to the release sections):

    Alpha for release testing

    1. Ensure all issues for this release have passed AT and been merged into master. You can also create an alpha release from a feature branch, to provide the needed .apk files to the QA team.
    2. Create a git tag starting with v and ending with the alpha version, e.g. git tag v1.2.3-alpha.1, and push the tag to GitHub (git push --tags). For features branches, you can add the name of the branch or whatever keyword helps to identify the release, e.g. v1.2.3-alpha.dark-theme.4.
    3. Creating the tag will trigger the building and signing of the app in CI. The release-ready APKs are available for side-loading from the Releases section in the project (e.g. CHT-Android Releases), along with the AABs that the Google Play Store may require. Note that the created release on GitHub with the generated artifacts will not be explicitly linked to the tag because the release is in a draft state.
    4. Side-Load: for testing internally, or apps that are not published in the Play Store like Collect and Gateway: Navigate to the GitHub Releases page (linked above) and download the relevant APKs for distribution.
    5. Announce the release in #quality-assurance.

    Production release

    1. Update the CHANGELOG with the details of what’s in this release.
    2. Repeat steps 2-3 from the above alpha release section with the naming convention v<major>.<minor>.<patch>.
    3. The CI build for the tag will create a new draft release on GitHub. Include the release notes from the CHANGELOG in the description of the release and publish the release on GitHub.
    4. Publish in the Play Store. For the CHT-Android app, the “reference” apps (medicmobilegamma and unbranded) must be published in the Play Store. Other channels such as F-Droid can also be used to publish the app.
    5. Announce the release on the CHT forum, under the “Product - Releases” category.
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/code/cht-conf/index.html b/contribute/code/cht-conf/index.html index 14ef7c9795..90a8c4dc82 100644 --- a/contribute/code/cht-conf/index.html +++ b/contribute/code/cht-conf/index.html @@ -1,9 +1,9 @@ -CHT App Configurer | Community Health Toolkit +CHT App Configurer | Community Health Toolkit

    CHT App Configurer

    CHT Conf is a command-line interface tool to manage and configure apps built using the Core Framework of the Community Health Toolkit.

    Requirements

    • nodejs 18 or later
    • python 3
    • Docker(optional)

    Installation

    Operating System Specific

    CHT App Configurer

    CHT Conf is a command-line interface tool to manage and configure apps built using the Core Framework of the Community Health Toolkit.

    Requirements

    • nodejs 18 or later
    • python 3
    • Docker(optional)

    Installation

    Operating System Specific

    npm install -g cht-conf
    @@ -321,7 +321,8 @@
     

    The list of available actions can be seen via cht --help.

    Perform actions for specific forms

    cht <--local|--instance=instance-name|--url=url> <...action> -- <...form>
     

    Protecting against configuration overwriting

    Added in v3.2.0 In order to avoid overwriting someone else’s configuration cht-conf records the last uploaded configuration snapshot in the .snapshots directory. The remote.json file should be committed to your repository along with the associated configuration change. When uploading future configuration if cht-conf detects the snapshot doesn’t match the configuration on the server you will be prompted to overwrite or cancel.

    Development

    To develop a new command that is part of cht-conf, or improve an existing one. For more information check “Actions” doc.

    Testing

    Unit tests

    Execute npm test to run static analysis checks and the test suite. Requires Docker to run integration tests against a CouchDB instance.

    End-to-end tests

    Run npm run test-e2e to run the end-to-end test suite against an actual CHT instance locally. These tests rely on CHT Docker Helper to spin up and tear down an instance locally.

    The code interfacing with CHT Docker Helper lives in test/e2e/cht-docker-utils.js. You should rely on the API exposed by this file to orchestrate CHT instances for testing purposes. It is preferable to keep the number of CHT instances orchestrated in E2E tests low as it takes a non-negligible amount of time to spin up an instance and can quickly lead to timeouts.

    Executing your local branch

    1. Clone the project locally
    2. Make changes to cht-conf or checkout a branch for testing
    3. Test changes
      1. To test CLI changes locally you can run node <project_dir>/src/bin/index.js. This will run as if you installed via npm.
      2. To test changes that are imported in code run npm install <project_dir> to use the local version of cht-conf.

    Releasing

    1. Create a pull request with prep for the new release.
    2. Get the pull request reviewed and approved.
    3. When doing the squash and merge, make sure that your commit message is clear and readable and follows the strict format described in the commit format section below. If the commit message does not comply, automatic release will fail.
    4. In case you are planning to merge the pull request with a merge commit, make sure that every commit in your branch respects the format.

    Commit format

    The commit format should follow this conventional-changelog angular preset. Examples are provided below.

    TypeExample commit messageRelease type
    Bug fixesfix(#123): infinite spinner when clicking contacts tab twicepatch
    Performanceperf(#789): lazily loaded angular modulespatch
    Featuresfeat(#456): add home tabminor
    Non-codechore(#123): update READMEnone
    Breakingperf(#2): remove reporting rates feature
    BREAKING CHANGE: reporting rates no longer supported
    major

    Releasing betas

    1. Checkout the default branch, for example main
    2. Run npm version --no-git-tag-version <major>.<minor>.<patch>-beta.1. This will only update the versions in package.json and package-lock.json. It will not create a git tag and not create an associated commit.
    3. Run npm publish --tag beta. This will publish your beta tag to npm’s beta channel.

    To install from the beta channel, run npm install cht-conf@beta.

    Build status

    Builds brought to you courtesy of GitHub actions.

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/contribute/code/core/automated-tests/index.html b/contribute/code/core/automated-tests/index.html index 7cae7b9116..7b58bfe026 100644 --- a/contribute/code/core/automated-tests/index.html +++ b/contribute/code/core/automated-tests/index.html @@ -1,9 +1,9 @@ -Automated Tests | Community Health Toolkit +Automated Tests | Community Health Toolkit

    Automated Tests

    Automated testing for cht-core

    The goal of automated testing

    Developers should be able to make changes in the codebase quickly and confidently. A big part of this means knowing that new changes have not impacted other functionality in the system and everything continues to work as expected.

    Of course any new functionality itself may or may not work as expected and it is up to the developer to write the appropriate tests to ensure it works correctly in both expected and unexpected scenarios. Tests should give a developer confidence in their own work, and prior tests should give future developers similar confidence.

    Automation of testing should speed up development in two significant areas:

    1. While making changes, new automated tests can be run regularly to ensure (without lots of manual effort) that the changes continue to work as expected
    2. Avoid large amounts of time spent manually performing regression testing of the whole application to ensure existing functionality keeps working

    Test types and expectations

    We seek to have a quality codebase that developers can work on with speed. This means balancing test strategies, quantity, and coverage.

    When looking at a well-factored codebase there are three common ways to automate tests (ordered by levels low to high):

    1. Unit tests
    2. Integration tests
      • Backend integration tests
      • Frontend integration tests
    3. End-to-end tests

    Note: All the commands to execute the different tests can be found in package.json file.

    Unit Tests

    Description

    Small tests of specific behavior. Each unit test is only intended to validate an isolated piece (unit) of functionality separated from the rest of the system. Any dependencies are often mocked.

    Expectations

    High coverage of functionality. If measured in branch coverage percentage, aim for 100%. This is the place to guarantee confidence in the system. If a higher-level test spots and error and there’s no lower-level test failing, you need to evaluate if a lower test should be written.

    Execution SpeedComplexityFragility
    Extremely fastExtremely lowExtremely stable

    Implementation

    In cht-core unit tests are located in the tests directories of each app (e.g. in webapp/tests you can find unit test for the webapp). Run them locally with: npm run unit.

    Integration Tests

    Description

    Tests to exercise how multiple components interact with each other. With a dynamic language like JavaScript these are especially important to verify expectations of interface points. These may mock some parts, but often use the “real” components since the point is to exercise those components together. As a result, these tests likely involve more setup, potentially involving data scenarios.

    Expectations

    Dramatically fewer than unit tests. The goal is not to verify all branches; it is to gain confidence in interface points.

    Execution SpeedComplexityFragility
    Fast execution, but slower startup when working with a DBMid-to-high. Things can get complex fast when combining parts!Mostly stable. Fragility risks tend to come from DB setup.

    Implementation

    For us, backend integration testing means testing through the entire stack of our application connected to other applications within our system. In the image below, it means that we test each application (box) and its interaction with other applications within our system. + Create project issue

    Automated Tests

    Automated testing for cht-core

    The goal of automated testing

    Developers should be able to make changes in the codebase quickly and confidently. A big part of this means knowing that new changes have not impacted other functionality in the system and everything continues to work as expected.

    Of course any new functionality itself may or may not work as expected and it is up to the developer to write the appropriate tests to ensure it works correctly in both expected and unexpected scenarios. Tests should give a developer confidence in their own work, and prior tests should give future developers similar confidence.

    Automation of testing should speed up development in two significant areas:

    1. While making changes, new automated tests can be run regularly to ensure (without lots of manual effort) that the changes continue to work as expected
    2. Avoid large amounts of time spent manually performing regression testing of the whole application to ensure existing functionality keeps working

    Test types and expectations

    We seek to have a quality codebase that developers can work on with speed. This means balancing test strategies, quantity, and coverage.

    When looking at a well-factored codebase there are three common ways to automate tests (ordered by levels low to high):

    1. Unit tests
    2. Integration tests
      • Backend integration tests
      • Frontend integration tests
    3. End-to-end tests

    Note: All the commands to execute the different tests can be found in package.json file.

    Unit Tests

    Description

    Small tests of specific behavior. Each unit test is only intended to validate an isolated piece (unit) of functionality separated from the rest of the system. Any dependencies are often mocked.

    Expectations

    High coverage of functionality. If measured in branch coverage percentage, aim for 100%. This is the place to guarantee confidence in the system. If a higher-level test spots and error and there’s no lower-level test failing, you need to evaluate if a lower test should be written.

    Execution SpeedComplexityFragility
    Extremely fastExtremely lowExtremely stable

    Implementation

    In cht-core unit tests are located in the tests directories of each app (e.g. in webapp/tests you can find unit test for the webapp). Run them locally with: npm run unit.

    Integration Tests

    Description

    Tests to exercise how multiple components interact with each other. With a dynamic language like JavaScript these are especially important to verify expectations of interface points. These may mock some parts, but often use the “real” components since the point is to exercise those components together. As a result, these tests likely involve more setup, potentially involving data scenarios.

    Expectations

    Dramatically fewer than unit tests. The goal is not to verify all branches; it is to gain confidence in interface points.

    Execution SpeedComplexityFragility
    Fast execution, but slower startup when working with a DBMid-to-high. Things can get complex fast when combining parts!Mostly stable. Fragility risks tend to come from DB setup.

    Implementation

    For us, backend integration testing means testing through the entire stack of our application connected to other applications within our system. In the image below, it means that we test each application (box) and its interaction with other applications within our system. We isolate the tests from the webapp and make the necessary shortcuts to make the test more straightforward and faster. We do not mock any part of the system.

    Backend integration tests are located in tests/integration. Run them locally with npm run integration-all-local and npm run integration-sentinel-local.

    flowchart LR
         subgraph cht-e2e [Docker: cht-e2e]
             api
    @@ -348,7 +348,63 @@
     Either run the test multiple times until you load all images, download images manually or increase this timeout.
     

    Try the following:

    • Manually downloaded the images. To download images manually, you can use either docker-compose or docker:
      • With docker, you’d do a docker pull for every image you want to download.
      • With docker-compose, you’d save all docker-compose files in a folder, do a docker-compose pull, and point to your files as a source. Read more on docker compose pull

    Test Architecture

    Our GitHub actions spin up an ubuntu-22.04 machine. They install software and then launch couchdb and horticulturalist in a docker container. This is needed to run our applications in the specific node versions we support, while allowing our test code to run in versions of node it supports. This creates a paradigm to keep in mind when writing tests. Tests run on the ubuntu machine. Any test code that starts a server or runs an executable is running outside of the horti container. The ports are exposed for all our services and horti has access to the cht-core root via a volume. Horti can also talk to the host by getting the gateway of the docker network.

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/contribute/code/core/build-commands/index.html b/contribute/code/core/build-commands/index.html index ae218b5912..54a819c53e 100644 --- a/contribute/code/core/build-commands/index.html +++ b/contribute/code/core/build-commands/index.html @@ -1,9 +1,9 @@ -Build commands | Community Health Toolkit +Build commands | Community Health Toolkit

    Build commands

    All commands available for executing to build, test, and deploy CHT Core Framework

    CHT Core build commands

    These commands are defined in the package.json and can be executed with npm run <command> from the cht-core repository directory.

    Development build commands

    For developers (humans) to execute to build cht-core.

    CommandDescription
    build-ddocsCompiles all the DDocs and outputs them into /api/build/ddocs ready for deployment.
    build-devUpdates dependencies and builds all the applications.
    build-dev-watchSame as build-dev, but keeps watching for any code changes and automatically deploys on change.
    build-documentationExecutes jsdoc on all the applications.
    build-webapp-devCompiles the /webapp application.
    build-cht-formCompiles the cht-form web component.
    copy-api-resourcesCopies the static api files into the api build directory ready for deployment.
    dev-apiSets up and runs the api server, and automatically deploys source changes.
    dev-sentinelSets up and runs the sentinel server, and automatically deploys source changes.
    local-imagesBuilds the docker images and updates the docker compose files.
    update-service-workerUpdates the service worker file for deployment.

    Development test commands

    For developers to execute to test cht-core.

    CommandDescription
    integration-all-localCompiles the app and executes the integration test suite except for the sentinel tests.
    integration-apiCompiles the app and executes the api integration test suite.
    integration-sentinel-localCompiles the app and executes the sentinel integration test suite.
    lintPerforms static analysis checks on the codebase.
    testSame as running lint, unit, and integration-api.
    unitExecutes unit test suites for all applications.
    unit-adminExecutes the unit test suite on admin.
    unit-apiExecutes the unit test suite on api.
    unit-sentinelExecutes the unit test suite on sentinel.
    unit-shared-libExecutes the unit test suite on all shared-lib modules.
    unit-webappExecutes the unit test suite on webapp.
    unit-webapp-continuousExecutes the unit test suite on webapp, and re-runs on code change.
    wdio-default-mobile-localCompiles the app and executes the mobile e2e test suite.
    wdio-localCompiles the app and executes the default e2e test suite.
    wdio-standard-localCompiles the app and executes the standard e2e test suite.
    wdio-cht-formExecutes the default e2e test suite on code change.

    CI commands

    For Continuous Integration (robots) to run to build and test cht-core.

    CommandDescription
    buildCompiles, minifies, bundles the code, and builds the DDocs for publishing.
    ci-compileBuilds, does static analysis, and runs unit tests for all applications.
    ci-e2e-integrationExecutes the integration e2e test suite.
    ci-webdriver-defaultExecutes the default e2e test suite.
    ci-webdriver-default-mobileExecutes the mobile e2e test suite.
    ci-webdriver-standardExecutes the standard e2e test suite.
    publish-for-testingBuilds docker images and publishes to the staging server for use in e2e test builds.
    test-config-defaultExecutes the default config test suite.
    test-config-standardExecutes the standard config test suite.
    upgrade-wdioExecutes the upgrade e2e test suite.
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/code/core/deploy-on-eks/index.html b/contribute/code/core/deploy-on-eks/index.html index d7cda9cae2..05eae0d21b 100644 --- a/contribute/code/core/deploy-on-eks/index.html +++ b/contribute/code/core/deploy-on-eks/index.html @@ -1,9 +1,9 @@ -Deploy CHT Core on Medic hosted EKS | Community Health Toolkit +Deploy CHT Core on Medic hosted EKS | Community Health Toolkit

    Deploy CHT Core on Medic hosted EKS

    Setting up a cloud hosted deployment of CHT Core on Medic’s AWS EKS infrastructure

    While not directly available to the public who might be doing CHT Core development, having Medic’s process for using our Amazon Elastic Kubernetes Service (AWS EKS) publicly documented will help Medic employees new to EKS. As well, hopefully external developers looking to re-use Medic tools and process to use EKS will find it helpful.

    While these instructions assume you work at Medic and have access to private GitHub repositories, many of the tools are fully open source.

    Prerequisites

    Command Line

    Be sure you have these tools installed and repos cloned:

    • awscli: version 2 or newer
    • kubectl: Must be within one minor version of cluster. If cluster is 1.24.x, use 1.23.x, 1.24.x or 1.25.x.
    • helm
    • jq
    • Medic Infra repo cloned

    Optional: Autocomplete

    Both helm and kubectl have autocomplete libraries. For power users and beginners alike, it adds a lot of discoverability. This code is for zsh, but bash, fish and powershell are supported as well:

    source <(kubectl completion zsh)
    + Create project issue

    Deploy CHT Core on Medic hosted EKS

    Setting up a cloud hosted deployment of CHT Core on Medic’s AWS EKS infrastructure

    While not directly available to the public who might be doing CHT Core development, having Medic’s process for using our Amazon Elastic Kubernetes Service (AWS EKS) publicly documented will help Medic employees new to EKS. As well, hopefully external developers looking to re-use Medic tools and process to use EKS will find it helpful.

    While these instructions assume you work at Medic and have access to private GitHub repositories, many of the tools are fully open source.

    Prerequisites

    Command Line

    Be sure you have these tools installed and repos cloned:

    • awscli: version 2 or newer
    • kubectl: Must be within one minor version of cluster. If cluster is 1.24.x, use 1.23.x, 1.24.x or 1.25.x.
    • helm
    • jq
    • Medic Infra repo cloned

    Optional: Autocomplete

    Both helm and kubectl have autocomplete libraries. For power users and beginners alike, it adds a lot of discoverability. This code is for zsh, but bash, fish and powershell are supported as well:

    source <(kubectl completion zsh)
     source <(helm completion zsh)
     

    See helm and kubectl docs to automatically loading these on every new session.

    Request permission

    By default, Medic teammates do not have EKS access and must file a ticket to request it:

    1. Create a ticket to get your DNS and Namespace created for EKS, which should match each other. As an example, a mrjones-dev name space would match mrjones.dev.medicmobile.org DNS. The ticket should include requesting EKS access to be granted.
    2. Once the ticket in step one is complete, follow the CLI setup guide.

    NB - Security key (e.g. Yubikey) users need to add a TOTP MFA (Time-based, One-Time Password Multi-Factor Authentication) too! CLI requires the TOTP values (6-digit number) and security keys are not supported. Security keys can only be used on web logins.

    First time setup

    These steps only need to be run once!

    After you have created a ticket per “Request permission” above, you should get a link to sign up for AWS. Click the link and:

    1. Create new password ensure it’s 10+ characters including one alpha (a-z) and one special (~!@#$%^&*_-+=`|\(){}[]:;"'<>,.?/) character.

    2. Setup MFA. In top-right corner of browser, there is a drop-down menu with your username @ medic. Click that and then on “My Security Credentials”

    3. Assign an MFA device and give it the same name as your username: In AWS web GUI, click your name in upper right:

      1. Security Credentials
      2. scroll down to “Multi-factor authentication (MFA)”
      3. click “Assign MFA device”
      4. enter a “Device name” (should match username)
      5. “Select MFA device” that you’re using
    4. Create Access Keys for Command Line Interface: In AWS web GUI, click your name in upper right -> Security Credentials -> scroll down to “Access keys” -> click “Create access key” -> for use case choose “Command Line Interface” -> click “Next” -> enter description and click “Create access key”

    5. Run aws configure and place appropriate access keys during prompts. Use eu-west-2 region. It should look like this:

      $ aws configure
       
      @@ -314,7 +314,8 @@
       
  • Deploy!:
    cd scripts/deploy;./cht-deploy -f PATH_TO/values.yaml
     
  • Delete it when you’re done:
    helm delete USERNAME-dev --namespace USERNAME-dev
     
  • References and Debugging

    More information on cht-deploy script is available in the CHT Core GitHub repository which includes specifics of the values.yaml file and more details about the debugging utilities listed below.

    Debugging

    A summary of the utilities in cht-core/scripts/deploy directory, assuming mrjones-dev namespace:

    • list all resources: ./troubleshooting/list-all-resources mrjones-dev
    • view logs, assuming cht-couchdb-1 returned from prior command: ./troubleshooting/view-logs mrjones-dev cht-couchdb-1
    • describe deployment, assuming cht-couchdb-1 returned from 1st command: ./troubleshooting/describe-deployment mrjones-dev cht-couchdb-1
    • list all deployments: ./troubleshooting/list-all-resources mrjones-dev

    Getting shell

    Sometimes you need to look at files and other key pieces of data that are not available with the current troubleshooting/view-logs script. In this case, getting an interactive shell on the pod can be helpful.

    1. First, get a list pods for your namespace: kubectl -n NAMESPACE get pods
    2. After finding the pod you’re interested, connect to the pod to get a shell: kubectl -n NAMESPACE exec -it PODNAME/CONTAINERNAME -- /bin/bash

    invalid apiVersion Error

    If you get the error:

    exec plugin: invalid apiVersion “client.authentication.k8s.io/v1alpha1” when running kubectl version

    You might be using an version of kubernetes api client.authentication.k8s.io which is not supported by your kubectl client. This can sometimes happen in EKS clusters if aws cli is an older version, in most cases you need at least version 2 of aws cli. Check version by running: aws --version and note that version 2 cannot be installed through pip (See Command Line section above for installation instructions)

    SRE Steps for granting users access to a namespace

    If you’re on the SRE/Infra team and want to grant a Medic teammate access to EKS:

    1. Tools required: aws, eksctl, kubectl
    2. Create AWS User.
      • Attach IAM policy: Force_MFA and share auto-generated password safely
      • Have user log in and finish MFA, access key setup
      • SRE adds you to mfa-required-users group
    3. Add the namespaces and users to tf/eks/dev/access/main.tf
    4. Run tofu apply in the folder tf/eks/dev/access
    5. Create identitymapping if needed:

    Reading the AWS guide for principal access may help here!

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/code/core/dev-environment/index.html b/contribute/code/core/dev-environment/index.html index fd8051bb53..40a7857e9c 100644 --- a/contribute/code/core/dev-environment/index.html +++ b/contribute/code/core/dev-environment/index.html @@ -1,9 +1,9 @@ -CHT Core dev environment setup | Community Health Toolkit +CHT Core dev environment setup | Community Health Toolkit

    CHT Core dev environment setup

    Getting your local machine ready to do development work on CHT Core.

    The Happy Path Installation

    CHT Core development can be done on Linux, macOS, or Windows (using the Windows Subsystem for Linux (WSL2)). This CHT Core developer guide will have you install NodeJS, npm, and CouchDB (via Docker) on your local workstation.

    Install NodeJS, npm, and Docker

    First, update your current packages and install some supporting tools:

    (Node 20 is the environment used to run the CHT server in production, so this is the recommended version of Node to use for development.)

    CHT Core dev environment setup

    Getting your local machine ready to do development work on CHT Core.

    The Happy Path Installation

    CHT Core development can be done on Linux, macOS, or Windows (using the Windows Subsystem for Linux (WSL2)). This CHT Core developer guide will have you install NodeJS, npm, and CouchDB (via Docker) on your local workstation.

    Install NodeJS, npm, and Docker

    First, update your current packages and install some supporting tools:

    (Node 20 is the environment used to run the CHT server in production, so this is the recommended version of Node to use for development.)

    sudo apt update && sudo apt -y dist-upgrade
    @@ -323,9 +323,9 @@
     curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/$nvm_version/install.sh | $SHELL
     . ~/.$(basename $SHELL)rc
     nvm install 20

    Now let’s ensure NodeJS 20 and npm 10 were installed. This should output version 20.x.x for NodeJS and 10.x.x for npm:

    node -v && npm -v
    -

    Install Docker:

    Install Docker:

    curl -fsSL get.docker.com -o get-docker.sh && sh get-docker.sh
     # OPTIONAL: Allow user to run Docker without sudo
     dockerd-rootless-setuptool.sh install
    @@ -367,7 +367,8 @@
     

    Tests

    Refer to the testing doc in the GitHub repo.

    nginx-local-ip

    nginx-local-ip is a local proxy that keeps all traffic local, and runs without latency or throttling. If sharing your local CHT instance is not required, it is the recommended method to add a valid SSL certificate (rather than ngrok or similar).

    1. Clone the repo: git clone https://github.com/medic/nginx-local-ip.git
    2. cd into the new directory: cd nginx-local-ip
    3. Assuming your IP is 192.168.0.3, start nginx-local-ip to connect to:
      • The CHT API running via npm run or horti, execute APP_URL=http://192.168.0.3:5988 docker compose up and then access it at https://192-168-0-3.local-ip.medicmobile.org/.
      • The CHT API running via docker, the ports are remapped, so execute HTTP=8080 HTTPS=8443 APP_URL=https://192.168.0.3 docker compose up and then access it at https://192-168-0-3.local-ip.medicmobile.org:8443/.
    4. The HTTP/HTTPS ports (80/443) need to accept traffic from the IP address of your host machine and your local webapp port (e.g. 5988) needs to accept traffic from the IP address of the nginx-local-ip container (on the Docker network). If you are using the UFW firewall (in a Linux environment) you can allow traffic on these ports with the following commands:

    (Since local IP addresses can change over time, ranges are used in these rules so that the firewall configuration does not have to be updated each time a new address is assigned.)

    sudo ufw allow proto tcp from 192.168.0.0/16 to any port 80,443
     sudo ufw allow proto tcp from  172.16.0.0/16 to any port 5988
     

    Remote Proxies

    ngrok and pagekite are remote proxies that route local traffic between your client and the CHT via a remote SSL terminator. While easy and handy, they introduce latency and are sometimes throttled. Always use nginx-local-ip when you need a TLS certificate and only use these when you need to share your dev instance.

    ngrok

    1. Create an ngrok account, download and install the binary, then link your computer to your ngrok account.
    2. Start ngrok to connect to:
      • The CHT API running via npm run or horti, execute ./ngrok http 5988
      • The CHT API running via docker, execute ./ngrok http 443
    3. Access the app using the https address shown (e.g. https://YOUR-NGROK-NAME.ngrok.io, replacing YOUR-NGROK-NAME with what you signed up with).

    Note: The service worker cache preload sometimes fails due to connection throttling (thereby causing an ngrok failure at startup).

    pagekite

    1. Create a pagekite account, download and install the python script.
    2. Start pagekite (be sure to replace YOUR-PAGEKIT-NAME with the URL you signed up for) to connect to:
    • The CHT API running via npm run or horti, execute python pagekite.py 5988 YOUR-PAGEKIT-NAME.pagekite.me
    • The CHT API running via docker, execute python pagekite.py 443 YOUR-PAGEKIT-NAME.pagekite.me
    1. Access the app using the https address shown (e.g. https://YOUR-PAGEKIT-NAME.pagekite.me).
    -

    \ No newline at end of file +

    \ No newline at end of file diff --git a/contribute/code/core/index.html b/contribute/code/core/index.html index f23ce7e17c..95f3d254fd 100644 --- a/contribute/code/core/index.html +++ b/contribute/code/core/index.html @@ -1,9 +1,9 @@ -Contributing CHT Core Code | Community Health Toolkit +Contributing CHT Core Code | Community Health Toolkit

    Contributing CHT Core Code

    How to contribute to code to the CHT Core Framework

    CHT Core dev environment setup

    Getting your local machine ready to do development work on CHT Core.

    Build commands

    All commands available for executing to build, test, and deploy CHT Core Framework

    Deploy CHT Core on Medic hosted EKS

    Setting up a cloud hosted deployment of CHT Core on Medic’s AWS EKS infrastructure

    Updating Dependencies

    Process for updating dependencies

    Automated Tests

    Automated testing for cht-core

    Style guide for automated tests

    This style guide provides editorial guidelines for anyone creating new automated test cases for the CHT-Core.

    Developing on Windows

    Notes for developing on Windows

    Running multiple Chrome versions

    How to run multiple Chrome versions on your local machine

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/code/core/index.xml b/contribute/code/core/index.xml index 3f2c9f5e38..79e7751e8d 100644 --- a/contribute/code/core/index.xml +++ b/contribute/code/core/index.xml @@ -1,1056 +1,14 @@ -Community Health Toolkit – Contributing CHT Core Codehttps://docs.communityhealthtoolkit.org/contribute/code/core/Recent content in Contributing CHT Core Code on Community Health ToolkitHugo -- gohugo.ioenContribute: CHT Core dev environment setuphttps://docs.communityhealthtoolkit.org/contribute/code/core/dev-environment/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/core/dev-environment/ -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -<p>This guide assumes you are a CHT Core developer wanting to run the CHT Core from source code to make commits to the <a href="https://github.com/medic/cht-core">public GitHub repository</a>. To set up your environment for developing apps, see the <a href="https://docs.communityhealthtoolkit.org/hosting/3.x/app-developer/">app guide</a>.</p> -<p>To deploy the CHT in production, see either <a href="https://docs.communityhealthtoolkit.org/hosting/3.x/ec2-setup-guide/">AWS hosting</a> or <a href="https://docs.communityhealthtoolkit.org/hosting/3.x/self-hosting/">Self hosting</a></p> -</div> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -These steps apply to both 3.x and 4.x CHT core development, unless stated otherwise. -</div> -<h2 id="the-happy-path-installation">The Happy Path Installation</h2> -<p>CHT Core development can be done on Linux, macOS, or Windows (using the <a href="https://learn.microsoft.com/en-us/windows/wsl/install">Windows Subsystem for Linux (WSL2)</a>). This CHT Core developer guide will have you install NodeJS, npm, and CouchDB (via Docker) on your local workstation.</p> -<h3 id="install-nodejs-npm-and-docker">Install NodeJS, npm, and Docker</h3> -<p>First, update your current packages and install some supporting tools:</p> -<p><em>(Node 20 is the environment used to run the CHT server in production, so this is the recommended version of Node to use for development.)</em></p> -<ul class="nav nav-tabs" id="tabs-3" role="tablist"> -<li class="nav-item"> -<button class="nav-link active" -id="tabs-03-00-tab" data-bs-toggle="tab" data-bs-target="#tabs-03-00" role="tab" -aria-controls="tabs-03-00" aria-selected="true"> -Linux (Ubuntu) -</button> -</li><li class="nav-item"> -<button class="nav-link" -id="tabs-03-01-tab" data-bs-toggle="tab" data-bs-target="#tabs-03-01" role="tab" -aria-controls="tabs-03-01" aria-selected="false"> -macOS -</button> -</li><li class="nav-item"> -<button class="nav-link" -id="tabs-03-02-tab" data-bs-toggle="tab" data-bs-target="#tabs-03-02" role="tab" -aria-controls="tabs-03-02" aria-selected="false"> -Windows (WSL2) -</button> -</li> -</ul> -<div class="tab-content" id="tabs-3-content"> -<div class="tab-pane fade show active" -id="tabs-03-00" role="tabpanel" aria-labelled-by="tabs-03-00-tab" tabindex="3"> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>sudo apt update <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> sudo apt -y dist-upgrade -</span></span><span style="display:flex;"><span>sudo apt -y install xsltproc curl uidmap jq python2 git make g++ -</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"># Use NVM to install NodeJS:</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87">export</span> <span style="color:#000">nvm_version</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">`</span>curl -s https://api.github.com/repos/nvm-sh/nvm/releases/latest <span style="color:#000;font-weight:bold">|</span> jq -r .name<span style="color:#4e9a06">`</span> -</span></span><span style="display:flex;"><span>curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/<span style="color:#000">$nvm_version</span>/install.sh <span style="color:#000;font-weight:bold">|</span> <span style="color:#000">$SHELL</span> -</span></span><span style="display:flex;"><span>. ~/.<span style="color:#204a87;font-weight:bold">$(</span>basename <span style="color:#000">$SHELL</span><span style="color:#204a87;font-weight:bold">)</span>rc -</span></span><span style="display:flex;"><span>nvm install <span style="color:#0000cf;font-weight:bold">20</span></span></span></code></pre></div> -</div> -<div class="tab-pane fade" -id="tabs-03-01" role="tabpanel" aria-labelled-by="tabs-03-01-tab" tabindex="3"> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"># Uses Homebrew: https://brew.sh/</span> -</span></span><span style="display:flex;"><span>brew update -</span></span><span style="display:flex;"><span>brew install curl jq pyenv git make node@20 gcc -</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"># Python no longer included by default in macOS &gt;12.3 </span> -</span></span><span style="display:flex;"><span>pyenv install 2.7.18 -</span></span><span style="display:flex;"><span>pyenv global 2.7.18 -</span></span><span style="display:flex;"><span><span style="color:#204a87">echo</span> <span style="color:#4e9a06">&#34;eval \&#34;\$(pyenv init --path)\&#34;&#34;</span> &gt;&gt; ~/.<span style="color:#204a87;font-weight:bold">$(</span>basename <span style="color:#000">$SHELL</span><span style="color:#204a87;font-weight:bold">)</span>rc -</span></span><span style="display:flex;"><span>. ~/.<span style="color:#204a87;font-weight:bold">$(</span>basename <span style="color:#000">$SHELL</span><span style="color:#204a87;font-weight:bold">)</span>rc</span></span></code></pre></div> -</div> -<div class="tab-pane fade" -id="tabs-03-02" role="tabpanel" aria-labelled-by="tabs-03-02-tab" tabindex="3"> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>sudo apt update <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> sudo apt -y dist-upgrade -</span></span><span style="display:flex;"><span>sudo apt -y install xsltproc curl uidmap jq python2 git make g++ -</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"># Use NVM to install NodeJS:</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87">export</span> <span style="color:#000">nvm_version</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">`</span>curl -s https://api.github.com/repos/nvm-sh/nvm/releases/latest <span style="color:#000;font-weight:bold">|</span> jq -r .name<span style="color:#4e9a06">`</span> -</span></span><span style="display:flex;"><span>curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/<span style="color:#000">$nvm_version</span>/install.sh <span style="color:#000;font-weight:bold">|</span> <span style="color:#000">$SHELL</span> -</span></span><span style="display:flex;"><span>. ~/.<span style="color:#204a87;font-weight:bold">$(</span>basename <span style="color:#000">$SHELL</span><span style="color:#204a87;font-weight:bold">)</span>rc -</span></span><span style="display:flex;"><span>nvm install <span style="color:#0000cf;font-weight:bold">20</span></span></span></code></pre></div> -</div> -</div> -<p>Now let&rsquo;s ensure NodeJS 20 and npm 10 were installed. This should output version 20.x.x for NodeJS and 10.x.x for <code>npm</code>:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>node -v <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> npm -v -</span></span></code></pre></div><p>Install Docker:</p> -<ul class="nav nav-tabs" id="tabs-0" role="tablist"> -<li class="nav-item"> -<button class="nav-link active" -id="tabs-00-00-tab" data-bs-toggle="tab" data-bs-target="#tabs-00-00" role="tab" -aria-controls="tabs-00-00" aria-selected="true"> -Linux (Ubuntu) -</button> -</li><li class="nav-item"> -<button class="nav-link" -id="tabs-00-01-tab" data-bs-toggle="tab" data-bs-target="#tabs-00-01" role="tab" -aria-controls="tabs-00-01" aria-selected="false"> -macOS -</button> -</li><li class="nav-item"> -<button class="nav-link" -id="tabs-00-02-tab" data-bs-toggle="tab" data-bs-target="#tabs-00-02" role="tab" -aria-controls="tabs-00-02" aria-selected="false"> -Windows (WSL2) -</button> -</li> -</ul> -<div class="tab-content" id="tabs-0-content"> -<div class="tab-body tab-pane fade show active" -id="tabs-00-00" role="tabpanel" aria-labelled-by="tabs-00-00-tab" tabindex="0"> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>curl -fsSL get.docker.com -o get-docker.sh <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> sh get-docker.sh -</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"># OPTIONAL: Allow user to run Docker without sudo</span> -</span></span><span style="display:flex;"><span>dockerd-rootless-setuptool.sh install -</span></span><span style="display:flex;"><span><span style="color:#204a87">echo</span> <span style="color:#4e9a06">&#34;export PATH=/usr/bin:</span><span style="color:#000">$PATH</span><span style="color:#4e9a06">&#34;</span> &gt;&gt; ~/.<span style="color:#204a87;font-weight:bold">$(</span>basename <span style="color:#000">$SHELL</span><span style="color:#204a87;font-weight:bold">)</span>rc -</span></span><span style="display:flex;"><span><span style="color:#204a87">echo</span> <span style="color:#4e9a06">&#34;export DOCKER_HOST=unix:///run/user/1000/docker.sock&#34;</span> &gt;&gt; ~/.<span style="color:#204a87;font-weight:bold">$(</span>basename <span style="color:#000">$SHELL</span><span style="color:#204a87;font-weight:bold">)</span>rc -</span></span><span style="display:flex;"><span>. ~/.<span style="color:#204a87;font-weight:bold">$(</span>basename <span style="color:#000">$SHELL</span><span style="color:#204a87;font-weight:bold">)</span>rc -</span></span></code></pre></div> -</div> -<div class="tab-body tab-pane fade" -id="tabs-00-01" role="tabpanel" aria-labelled-by="tabs-00-01-tab" tabindex="0"> -<p>Download and install <a href="https://www.docker.com/products/docker-desktop">Docker Desktop</a> or <a href="https://github.com/abiosoft/colima#readme">Colima</a>.</p> -</div> -<div class="tab-body tab-pane fade" -id="tabs-00-02" role="tabpanel" aria-labelled-by="tabs-00-02-tab" tabindex="0"> -<p>Download and install <a href="https://www.docker.com/products/docker-desktop">Docker Desktop</a>.</p> -</div> -</div> -<p>Restart your entire machine to finish initializing Docker.</p> -<p>After restarting, verify Docker is running as expected. Run the simple <code>hello-world</code> Docker container. This should output &ldquo;Hello from Docker!&rdquo; as well as some other intro text:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>docker run hello-world -</span></span></code></pre></div> -<h3 id="cht-core-cloning-and-setup">CHT Core Cloning and Setup</h3> -<p>Clone the main CHT Core repo from GitHub and change directories into it:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>git clone https://github.com/medic/cht-core ~/cht-core -</span></span><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/cht-core -</span></span></code></pre></div><p>Install dependencies and perform other setup tasks via an <code>npm</code> command. Note this command may take many minutes. Be patient!</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>npm ci --legacy-peer-deps -</span></span></code></pre></div><p>To finalise setting up any remaining dependencies build the project by running:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>npm run build-dev -</span></span></code></pre></div><p>Every time you run any <code>npm</code> or <code>node</code> commands, it will expect <code>COUCH_NODE_NAME</code> and <code>COUCH_URL</code> environment variables to be set:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">echo</span> <span style="color:#4e9a06">&#34;export COUCH_NODE_NAME=nonode@nohost&#34;</span>&gt;&gt; ~/.<span style="color:#204a87;font-weight:bold">$(</span>basename <span style="color:#000">$SHELL</span><span style="color:#204a87;font-weight:bold">)</span>rc -</span></span><span style="display:flex;"><span><span style="color:#204a87">echo</span> <span style="color:#4e9a06">&#34;export COUCH_URL=http://medic:password@localhost:5984/medic&#34;</span>&gt;&gt; ~/.<span style="color:#204a87;font-weight:bold">$(</span>basename <span style="color:#000">$SHELL</span><span style="color:#204a87;font-weight:bold">)</span>rc -</span></span><span style="display:flex;"><span>. ~/.<span style="color:#204a87;font-weight:bold">$(</span>basename <span style="color:#000">$SHELL</span><span style="color:#204a87;font-weight:bold">)</span>rc -</span></span></code></pre></div><p>To ensure these to exports and sourcing your rc file worked, echo the values back out. You should see <code>nonode@nohost</code> and <code>http://medic:password@localhost:5984/medic</code>:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">echo</span> <span style="color:#000">$COUCH_NODE_NAME</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#204a87">echo</span> <span style="color:#000">$COUCH_URL</span> -</span></span></code></pre></div><h3 id="couchdb">CouchDB</h3> -<p>CouchDB execution differs depending on whether you&rsquo;re running CHT 3.x or 4.x. Follow the instructions in one of the sections below.</p> -<h4 id="couchdb-setup-in-cht-3x">CouchDB Setup in CHT 3.x</h4> -<p>Note this will run in the background and store its data in <code>/home/YOUR-USER/cht-docker</code>. The login for your CHT instance will be <code>medic</code> and the password will be <code>password</code>:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>docker run -d -p 5984:5984 -p 5986:5986 --name medic-couchdb -e <span style="color:#000">COUCHDB_USER</span><span style="color:#ce5c00;font-weight:bold">=</span>medic -e <span style="color:#000">COUCHDB_PASSWORD</span><span style="color:#ce5c00;font-weight:bold">=</span>password -v ~/cht-docker/local.d:/opt/couchdb/data -v ~/cht-docker/local.d:/opt/couchdb/etc/local.d apache/couchdb:2 -</span></span></code></pre></div><p>Let&rsquo;s ensure CouchDB is set up with a test <code>curl</code> call. This should show &ldquo;nonode@nohost&rdquo; in JSON:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>curl -X GET <span style="color:#4e9a06">&#34;http://medic:password@localhost:5984/_membership&#34;</span> <span style="color:#000;font-weight:bold">|</span> jq -</span></span></code></pre></div><h4 id="couchdb-setup-in-cht-4x">CouchDB Setup in CHT 4.x</h4> -<p>Create a <code>docker-compose.yml</code> and <code>couchdb-override.yml</code> files under the <code>~/cht-docker</code> folder with this code:</p> -<pre tabindex="0"><code>mkdir -p ~/cht-docker -curl -s -o ~/cht-docker/docker-compose.yml https://staging.dev.medicmobile.org/_couch/builds_4/medic:medic:master/docker-compose/cht-couchdb.yml -cat &gt; ~/cht-docker/couchdb-override.yml &lt;&lt; EOF -version: &#39;3.9&#39; -services: -couchdb: -ports: -- &#34;5984:5984&#34; -- &#34;5986:5986&#34; -EOF -</code></pre><p>Now you can start CouchDB. The login for your CHT instance will be <code>medic</code> and the <code>password</code> will be password:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/cht-docker -</span></span><span style="display:flex;"><span><span style="color:#000">COUCHDB_USER</span><span style="color:#ce5c00;font-weight:bold">=</span>medic <span style="color:#000">COUCHDB_PASSWORD</span><span style="color:#ce5c00;font-weight:bold">=</span>password docker-compose -f docker-compose.yml -f couchdb-override.yml up -d -</span></span></code></pre></div><h3 id="developing">Developing</h3> -<p>Now you have everything installed and can begin development! You&rsquo;ll need three separate terminals when doing development.</p> -<p>In the first terminal we&rsquo;ll compile and deploy the web application by running:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/cht-core <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> npm run build-dev-watch -</span></span></code></pre></div><p>Be <strong>very</strong> patient until you see:</p> -<blockquote> -<p>&ldquo;Waiting&hellip;&rdquo;</p> -</blockquote> -<p>In the second terminal we&rsquo;ll start the API nodejs service by running:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/cht-core <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> npm run dev-api -</span></span></code></pre></div><p>Finally, in a 3rd terminal we&rsquo;ll start the Sentinel nodejs service by running:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/cht-core <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> npm run dev-sentinel -</span></span></code></pre></div><p>That&rsquo;s it! Now when you edit code in your IDE, it will automatically reload. You can see the CHT running locally here: <a href="http://localhost:5988/">http://localhost:5988/</a></p> -<p>When you&rsquo;re done with development you can <code>ctrl + c</code> in the three terminals and stop the CouchDB container with <code>docker stop medic-couchdb</code>. When you want to resume development later, run <code>docker start medic-couchdb</code> and re-run the three terminal commands.</p> -<h2 id="other-path-troubleshooting">Other Path Troubleshooting</h2> -<p>If you weren&rsquo;t able to follow <a href="#the-happy-path-installation">the happy path above</a>, here are some details about the developer install that may help you troubleshoot what went wrong.</p> -<h3 id="prerequisites">Prerequisites</h3> -<p>If you had issues with following the above steps, check out these links for how to install the prerequisites on your specific platform:</p> -<ul> -<li><a href="https://nodejs.org/">Node.js 20.x</a> &amp; <a href="https://npmjs.com/">npm 10.x.x</a> - Both of which we recommend installing <a href="https://github.com/nvm-sh/nvm#installing-and-updating">via <code>nvm</code></a></li> -<li><a href="http://www.sagehill.net/docbookxsl/InstallingAProcessor.html">xsltproc</a></li> -<li><a href="https://www.python.org/downloads/">python 2.7</a></li> -<li><a href="https://docs.docker.com/engine/install/">Docker</a></li> -<li><a href="https://docs.couchdb.org/en/2.3.1/install/index.html">CouchDB</a> - OS package instead of in Docker - you <strong>MUST</strong> use CouchDB 2.x! We still strongly recommend using Docker.</li> -</ul> -<h3 id="ubuntu-1804">Ubuntu 18.04</h3> -<p>Ubuntu 18.04&rsquo;s default <code>apt</code> repositories do not know about <code>python2</code>. This means when you go to install run the first <code>apt install</code> command above, you see an error:</p> -<pre tabindex="0"><code>E: Unable to locate package python2 -</code></pre><p>To fix this, change the <code>apt install</code> call to this:</p> -<pre tabindex="0"><code>sudo apt -y install xsltproc curl uidmap jq python git make g++ -</code></pre><p>As well, after you install docker, and go to run the rootless script <code>dockerd-rootless-setuptool.sh</code>, you might see this error:</p> -<pre tabindex="0"><code>[ERROR] Failed to start docker.service. Run `journalctl -n 20 --no-pager --user --unit docker.service` to show the error log. -</code></pre><p>The workaround, unfortunately, is to just start your CouchDB Docker container with sudo: <code>sudo docker run...</code>.</p> -<h3 id="couchdb-on-docker-details">CouchDB on Docker Details</h3> -<p>Breaking down the command from <a href="#couchdb">the above section</a>, here&rsquo;s a generic version that doesn&rsquo;t include hard coded paths:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>docker run -d -p 5984:5984 -p 5986:5986 --name medic-couchdb -e <span style="color:#000">COUCHDB_USER</span><span style="color:#ce5c00;font-weight:bold">=</span>medic -e <span style="color:#000">COUCHDB_PASSWORD</span><span style="color:#ce5c00;font-weight:bold">=</span>password -v &lt;data path&gt;:/opt/couchdb/data -v &lt;config path&gt;:/opt/couchdb/etc/local.d apache/couchdb:2 -</span></span></code></pre></div><p>Parts of the command:</p> -<ul> -<li><code>--name</code> creates a container called <code>medic-couchdb</code>. You can name it whatever you want, but this is how you refer to it later</li> -<li><code>-e</code> sets an environment variable inside the container. Two are set here, for a user and password for the initial admin user.</li> -<li><code>-v</code> maps where couchdb stores data to your local file system to ensure persistence without depending on the container, using the path <em>before</em> the <code>:</code> (the path after the colon is the internal path inside the docker image). This should be somewhere you have write access to, and want this data to be stored. The second mounted volume is for the couch configuration, which will retain settings if your container is removed. This is especially important after running the command to secure the instance (done in steps below).</li> -<li><code>apache/couchdb:2</code> will install the latest package for CouchDB 2.x</li> -</ul> -<p>Once this downloads and starts, you will need to <a href="http://localhost:5984/_utils/#/setup">initialise CouchDB</a> as noted in <a href="https://docs.couchdb.org/en/2.3.1/setup/index.html#setup">their install instructions</a>.</p> -<p>You can use <code>docker stop medic-couchdb</code> to stop it and <code>docker start medic-couchdb</code> to start it again. Remember that you&rsquo;ll need to start it whenever you restart your OS, which might not be the case if you use a normal OS package. <code>docker rm medic-couchdb</code> will totally remove the container.</p> -<p>Medic recommends you familiarise yourself with other Docker commands to make docker image and container management clearer.</p> -<h3 id="required-environment-variables">Required environment variables</h3> -<p>Medic needs the following environment variables to be declared:</p> -<ul> -<li><code>COUCH_URL</code>: the full authenticated url to the <code>medic</code> DB. Locally this would be <code>http://myadminuser:myadminpass@localhost:5984/medic</code></li> -<li><code>COUCH_NODE_NAME</code>: the name of your CouchDB&rsquo;s node. The Docker image default is <code>nonode@nohost</code>. Other installations may use <code>couchdb@127.0.0.1</code>. You can find out by querying <a href="https://docs.couchdb.org/en/stable/api/server/common.html#membership">CouchDB&rsquo;s membership API</a></li> -<li>(optional) <code>COUCHDB_USER</code>: the name of your CouchDB&rsquo;s user. The Docker image default is <code>medic</code></li> -<li>(optional) <code>COUCHDB_PASSWORD</code>: the credentials of your CouchDB user. The Docker image default is <code>password</code></li> -<li>(optional) <code>API_PORT</code>: the port API will run on. If not defined, the port defaults to <code>5988</code></li> -<li>(optional) <code>CHROME_BIN</code>: only required if tests complain that they can&rsquo;t find Chrome or if you want to run a specific version of the Chrome webdriver.</li> -</ul> -<p>How to permanently define environment variables depends on your OS and shell (e.g. for bash you can put them <code>~/.bashrc</code>). You can temporarily define them with <code>export</code>:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">export</span> <span style="color:#000">COUCH_NODE_NAME</span><span style="color:#ce5c00;font-weight:bold">=</span>nonode@nohost -</span></span><span style="display:flex;"><span><span style="color:#204a87">export</span> <span style="color:#000">COUCH_URL</span><span style="color:#ce5c00;font-weight:bold">=</span>http://myadminuser:myadminpass@localhost:5984/medic -</span></span></code></pre></div><h2 id="tests">Tests</h2> -<p>Refer to <a href="https://github.com/medic/cht-core/blob/master/TESTING.md">the testing doc</a> in the GitHub repo.</p> -<h2 id="nginx-local-ip">nginx-local-ip</h2> -<p><a href="https://github.com/medic/nginx-local-ip"><code>nginx-local-ip</code></a> is a local proxy that keeps all traffic local, and runs without latency or throttling. If sharing your local CHT instance is not required, it is the recommended method to add a valid SSL certificate (rather than <code>ngrok</code> or similar).</p> -<ol> -<li>Clone the repo: <code>git clone https://github.com/medic/nginx-local-ip.git</code></li> -<li><code>cd</code> into the new directory: <code>cd nginx-local-ip</code></li> -<li>Assuming your IP is <code>192.168.0.3</code>, start <code>nginx-local-ip</code> to connect to: -<ul> -<li>The CHT API running via <code>npm run</code> or <code>horti</code>, execute <code>APP_URL=http://192.168.0.3:5988 docker compose up</code> and then access it at <code>https://192-168-0-3.local-ip.medicmobile.org/</code>.</li> -<li>The CHT API running via <code>docker</code>, the ports are remapped, so execute <code>HTTP=8080 HTTPS=8443 APP_URL=https://192.168.0.3 docker compose up</code> and then access it at <code>https://192-168-0-3.local-ip.medicmobile.org:8443/</code>.</li> -</ul> -</li> -<li>The HTTP/HTTPS ports (<code>80</code>/<code>443</code>) need to accept traffic from the IP address of your host machine and your local webapp port (e.g. <code>5988</code>) needs to accept traffic from the IP address of the <code>nginx-local-ip</code> container (on the Docker network). If you are using the UFW firewall (in a Linux environment) you can allow traffic on these ports with the following commands:</li> -</ol> -<p>(Since local IP addresses can change over time, ranges are used in these rules so that the firewall configuration does not have to be updated each time a new address is assigned.)</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>sudo ufw allow proto tcp from 192.168.0.0/16 to any port 80,443 -</span></span><span style="display:flex;"><span>sudo ufw allow proto tcp from 172.16.0.0/16 to any port <span style="color:#0000cf;font-weight:bold">5988</span> -</span></span></code></pre></div><h2 id="remote-proxies">Remote Proxies</h2> -<p><code>ngrok</code> and <code>pagekite</code> are remote proxies that route local traffic between your client and the CHT via a remote SSL terminator. While easy and handy, they introduce latency and are sometimes throttled. Always use <code>nginx-local-ip</code> when you need a TLS certificate and only use these when you need to share your dev instance.</p> -<h3 id="ngrok">ngrok</h3> -<ol> -<li>Create an <a href="https://ngrok.com/">ngrok account</a>, download and install the binary, then link your computer to your ngrok account.</li> -<li>Start <code>ngrok</code> to connect to: -<ul> -<li>The CHT API running via <code>npm run</code> or <code>horti</code>, execute <code>./ngrok http 5988</code></li> -<li>The CHT API running via <code>docker</code>, execute <code>./ngrok http 443</code></li> -</ul> -</li> -<li>Access the app using the https address shown (e.g. <code>https://YOUR-NGROK-NAME.ngrok.io</code>, replacing <code>YOUR-NGROK-NAME</code> with what you signed up with).</li> -</ol> -<p><strong>Note:</strong> The service worker cache preload sometimes fails due to connection throttling (thereby causing an <code>ngrok</code> failure at startup).</p> -<h3 id="pagekite">pagekite</h3> -<ol> -<li>Create a <a href="https://pagekite.net/signup/">pagekite account</a>, download and install the python script.</li> -<li>Start pagekite (be sure to replace <code>YOUR-PAGEKIT-NAME</code> with the URL you signed up for) to connect to:</li> -</ol> -<ul> -<li>The CHT API running via <code>npm run</code> or <code>horti</code>, execute <code>python pagekite.py 5988 YOUR-PAGEKIT-NAME.pagekite.me</code></li> -<li>The CHT API running via <code>docker</code>, execute <code>python pagekite.py 443 YOUR-PAGEKIT-NAME.pagekite.me</code></li> -</ul> -<ol> -<li>Access the app using the https address shown (e.g. <code>https://YOUR-PAGEKIT-NAME.pagekite.me</code>).</li> -</ol>Contribute: Build commandshttps://docs.communityhealthtoolkit.org/contribute/code/core/build-commands/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/core/build-commands/ -<h1 id="cht-core-build-commands">CHT Core build commands</h1> -<p>These commands are defined in the <code>package.json</code> and can be executed with <code>npm run &lt;command&gt;</code> from the cht-core repository directory.</p> -<h2 id="development-build-commands">Development build commands</h2> -<p>For developers (humans) to execute to build <code>cht-core</code>.</p> -<table> -<thead> -<tr> -<th>Command</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>build-ddocs</code></td> -<td>Compiles all the DDocs and outputs them into <code>/api/build/ddocs</code> ready for deployment.</td> -</tr> -<tr> -<td><code>build-dev</code></td> -<td>Updates dependencies and builds all the applications.</td> -</tr> -<tr> -<td><code>build-dev-watch</code></td> -<td>Same as <code>build-dev</code>, but keeps watching for any code changes and automatically deploys on change.</td> -</tr> -<tr> -<td><code>build-documentation</code></td> -<td>Executes jsdoc on all the applications.</td> -</tr> -<tr> -<td><code>build-webapp-dev</code></td> -<td>Compiles the <code>/webapp</code> application.</td> -</tr> -<tr> -<td><code>build-cht-form</code></td> -<td>Compiles the <code>cht-form</code> web component.</td> -</tr> -<tr> -<td><code>copy-api-resources</code></td> -<td>Copies the static api files into the <code>api</code> build directory ready for deployment.</td> -</tr> -<tr> -<td><code>dev-api</code></td> -<td>Sets up and runs the <code>api</code> server, and automatically deploys source changes.</td> -</tr> -<tr> -<td><code>dev-sentinel</code></td> -<td>Sets up and runs the <code>sentinel</code> server, and automatically deploys source changes.</td> -</tr> -<tr> -<td><code>local-images</code></td> -<td>Builds the docker images and updates the docker compose files.</td> -</tr> -<tr> -<td><code>update-service-worker</code></td> -<td>Updates the service worker file for deployment.</td> -</tr> -</tbody> -</table> -<h2 id="development-test-commands">Development test commands</h2> -<p>For developers to execute to test <code>cht-core</code>.</p> -<table> -<thead> -<tr> -<th>Command</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>integration-all-local</code></td> -<td>Compiles the app and executes the integration test suite except for the sentinel tests.</td> -</tr> -<tr> -<td><code>integration-api</code></td> -<td>Compiles the app and executes the api integration test suite.</td> -</tr> -<tr> -<td><code>integration-sentinel-local</code></td> -<td>Compiles the app and executes the sentinel integration test suite.</td> -</tr> -<tr> -<td><code>lint</code></td> -<td>Performs static analysis checks on the codebase.</td> -</tr> -<tr> -<td><code>test</code></td> -<td>Same as running <code>lint</code>, <code>unit</code>, and <code>integration-api</code>.</td> -</tr> -<tr> -<td><code>unit</code></td> -<td>Executes unit test suites for all applications.</td> -</tr> -<tr> -<td><code>unit-admin</code></td> -<td>Executes the unit test suite on <code>admin</code>.</td> -</tr> -<tr> -<td><code>unit-api</code></td> -<td>Executes the unit test suite on <code>api</code>.</td> -</tr> -<tr> -<td><code>unit-sentinel</code></td> -<td>Executes the unit test suite on <code>sentinel</code>.</td> -</tr> -<tr> -<td><code>unit-shared-lib</code></td> -<td>Executes the unit test suite on all <code>shared-lib</code> modules.</td> -</tr> -<tr> -<td><code>unit-webapp</code></td> -<td>Executes the unit test suite on <code>webapp</code>.</td> -</tr> -<tr> -<td><code>unit-webapp-continuous</code></td> -<td>Executes the unit test suite on <code>webapp</code>, and re-runs on code change.</td> -</tr> -<tr> -<td><code>wdio-default-mobile-local</code></td> -<td>Compiles the app and executes the mobile e2e test suite.</td> -</tr> -<tr> -<td><code>wdio-local</code></td> -<td>Compiles the app and executes the default e2e test suite.</td> -</tr> -<tr> -<td><code>wdio-standard-local</code></td> -<td>Compiles the app and executes the standard e2e test suite.</td> -</tr> -<tr> -<td><code>wdio-cht-form</code></td> -<td>Executes the default e2e test suite on code change.</td> -</tr> -</tbody> -</table> -<h2 id="ci-commands">CI commands</h2> -<p>For Continuous Integration (robots) to run to build and test <code>cht-core</code>.</p> -<table> -<thead> -<tr> -<th>Command</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>build</code></td> -<td>Compiles, minifies, bundles the code, and builds the DDocs for publishing.</td> -</tr> -<tr> -<td><code>ci-compile</code></td> -<td>Builds, does static analysis, and runs unit tests for all applications.</td> -</tr> -<tr> -<td><code>ci-e2e-integration</code></td> -<td>Executes the integration e2e test suite.</td> -</tr> -<tr> -<td><code>ci-webdriver-default</code></td> -<td>Executes the default e2e test suite.</td> -</tr> -<tr> -<td><code>ci-webdriver-default-mobile</code></td> -<td>Executes the mobile e2e test suite.</td> -</tr> -<tr> -<td><code>ci-webdriver-standard</code></td> -<td>Executes the standard e2e test suite.</td> -</tr> -<tr> -<td><code>publish-for-testing</code></td> -<td>Builds docker images and publishes to the staging server for use in e2e test builds.</td> -</tr> -<tr> -<td><code>test-config-default</code></td> -<td>Executes the default config test suite.</td> -</tr> -<tr> -<td><code>test-config-standard</code></td> -<td>Executes the standard config test suite.</td> -</tr> -<tr> -<td><code>upgrade-wdio</code></td> -<td>Executes the upgrade e2e test suite.</td> -</tr> -</tbody> -</table>Contribute: Deploy CHT Core on Medic hosted EKShttps://docs.communityhealthtoolkit.org/contribute/code/core/deploy-on-eks/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/core/deploy-on-eks/ -<p>While not directly available to the public who might be doing CHT Core development, having Medic&rsquo;s process for using our <a href="https://docs.aws.amazon.com/eks/latest/userguide/what-is-eks.html">Amazon Elastic Kubernetes Service</a> (AWS EKS) publicly documented will help Medic employees new to EKS. As well, hopefully external developers looking to re-use Medic tools and process to use EKS will find it helpful.</p> -<p>While these instructions assume you work at Medic and have access to private GitHub repositories, many of the tools are fully open source.</p> -<h2 id="prerequisites">Prerequisites</h2> -<h3 id="command-line">Command Line</h3> -<p>Be sure you have these tools installed and repos cloned:</p> -<ul> -<li><a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html">awscli</a>: version <code>2</code> or newer</li> -<li><a href="https://kubernetes.io/docs/tasks/tools">kubectl</a>: Must be within one minor version of cluster. If cluster is <code>1.24.x</code>, use <code>1.23.x</code>, <code>1.24.x</code> or <code>1.25.x</code>.</li> -<li><a href="https://helm.sh/docs/intro/install/">helm</a></li> -<li><a href="https://jqlang.github.io/jq/download/">jq</a></li> -<li><a href="https://github.com/medic/medic-infrastructure/">Medic Infra</a> repo cloned</li> -</ul> -<h4 id="optional--autocomplete">Optional: Autocomplete</h4> -<p>Both <code>helm</code> and <code>kubectl</code> have autocomplete libraries. For power users and beginners alike, it adds a lot of discoverability. This code is for <code>zsh</code>, but <code>bash</code>, <code>fish</code> and <code>powershell</code> are supported as well:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">source</span> &lt;<span style="color:#ce5c00;font-weight:bold">(</span>kubectl completion zsh<span style="color:#ce5c00;font-weight:bold">)</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87">source</span> &lt;<span style="color:#ce5c00;font-weight:bold">(</span>helm completion zsh<span style="color:#ce5c00;font-weight:bold">)</span> -</span></span></code></pre></div><p>See <a href="https://helm.sh/docs/helm/helm_completion_bash/">helm</a> and <a href="https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/#enable-shell-autocompletion">kubectl</a> docs to automatically loading these on every new session.</p> -<h3 id="request-permission">Request permission</h3> -<p>By default, Medic teammates do not have EKS access and must file a ticket to request it:</p> -<ol> -<li><a href="https://github.com/medic/medic-infrastructure/issues/new">Create a ticket</a> to get your DNS and Namespace created for EKS, which should match each other. As an example, a <code>mrjones-dev</code> name space would match <code>mrjones.dev.medicmobile.org</code> DNS. The ticket should include requesting EKS access to be granted.</li> -<li>Once the ticket in step one is complete, follow the <a href="https://github.com/medic/medic-infrastructure/blob/master/terraform/aws/dev/eks/access/README.md">CLI setup guide</a>.</li> -</ol> -<p><strong>NB</strong> - Security key (e.g. Yubikey) users need to add a TOTP MFA (Time-based, One-Time Password Multi-Factor Authentication) too! CLI requires the TOTP values (6-digit number) and security keys are not supported. Security keys can only be used on web logins.</p> -<h3 id="first-time-setup">First time setup</h3> -<p>These steps only need to be run once!</p> -<p>After you have created a ticket per &ldquo;Request permission&rdquo; above, you should get a link to sign up for AWS. Click the link and:</p> -<ol> -<li> -<p>Create new password ensure it&rsquo;s 10+ characters including one alpha (<code>a-z</code>) and one special (<code>~!@#$%^&amp;*_-+=`|\(){}[]:;&quot;'&lt;&gt;,.?/</code>) character.</p> -</li> -<li> -<p>Setup MFA. In top-right corner of browser, there is a drop-down menu with your <code>username @ medic</code>. Click that and then on &ldquo;My Security Credentials&rdquo;</p> -</li> -<li> -<p>Assign an MFA device and give it the <strong>same name</strong> as your username: In AWS web GUI, click your name in upper right:</p> -<ol> -<li>Security Credentials</li> -<li>scroll down to &ldquo;Multi-factor authentication (MFA)&rdquo;</li> -<li>click &ldquo;Assign MFA device&rdquo;</li> -<li>enter a &ldquo;Device name&rdquo; (should match username)</li> -<li>&ldquo;Select MFA device&rdquo; that you&rsquo;re using</li> -</ol> -</li> -<li> -<p>Create Access Keys for Command Line Interface: In AWS web GUI, click your name in upper right -&gt; Security Credentials -&gt; scroll down to &ldquo;Access keys&rdquo; -&gt; click &ldquo;Create access key&rdquo; -&gt; for use case choose &ldquo;Command Line Interface&rdquo; -&gt; click &ldquo;Next&rdquo; -&gt; enter description and click &ldquo;Create access key&rdquo;</p> -</li> -<li> -<p>Run <code>aws configure</code> and place appropriate access keys during prompts. Use <code>eu-west-2</code> region. It should look like this:</p> -<pre tabindex="0"><code>$ aws configure -AWS Access Key ID [None]: &lt;ACCESS-KEY-HERE&gt; -AWS Secret Access Key [None]: &lt;SECRET-HERE&gt; -Default region name [None]: eu-west-2 -Default output format [None]: -</code></pre></li> -<li> -<p>Run the Update Kubeconfig command, assuming username is <code>mrjones</code> and namespace is <code>mrjones-dev</code> - be sure to place these with yours: <code>aws eks update-kubeconfig --name mrjones-dev --profile mrjones --region eu-west-2</code></p> -</li> -</ol> -<h2 id="starting-and-stopping-aka-deleting">Starting and stopping (aka deleting)</h2> -<ol> -<li>Login with <code>eks-aws-mfa-login</code> script in the <a href="https://github.com/medic/medic-infrastructure/tree/master/terraform/aws/dev/eks/access">infra repo</a>: -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>./eks-aws-mfa-login USERNAME TOTP_HERE -</span></span></code></pre></div></li> -<li>Ensure you&rsquo;re using dev EKS cluster: -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>kubectl config use-context arn:aws:eks:eu-west-2:720541322708:cluster/dev-cht-eks -</span></span></code></pre></div></li> -<li>Create a new <code>values.yaml</code> file by <a href="https://github.com/medic/medic-infrastructure/blob/master/terraform/aws/dev/cht-projects/alpha-dev-cht-deploy-values.yaml">copying this one</a>. Be sure to update these values after you create it: -<ul> -<li><code>alpha-dev</code> values to <code>USERNAME-dev</code></li> -<li>Update <code>certificate</code> to the latest value from SRE - currently it&rsquo;s <code>arn:aws:iam::720541322708:server-certificate/2024-wildcard-dev-medicmobile-org-chain</code></li> -<li>Add a strong <code>password</code> - this instance is exposed to the Internet!</li> -<li>Put a UUID in <code>secret</code> - the command <code>uuidgen</code> is great for this</li> -<li>Update <code>host</code> to be your <code>username</code>. For example: <code>mrjones.dev.medicmobile.org</code></li> -</ul> -</li> -<li>Use <code>uuidgen</code> to fill in the <code>secret</code> in <code>values.yaml</code></li> -<li>Use a good passphrase (diceware!) to fill in <code>password</code> in <code>values.yaml</code>. <em>Please note that a few special characters are unsupported in this field like <code>:</code>, <code>@</code>, <code>&quot;</code>, <code>'</code>, etc. Add your password as a string by enclosing it in quotes <code>&quot;&quot;</code>, and do not use spaces in your password. This will not impact the deployment but will not let you log in to the CHT instance.</em></li> -<li>Ensure you have the latest code of <code>cht-core</code> <a href="https://github.com/medic/cht-core">repo</a>: -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>git checkout master<span style="color:#000;font-weight:bold">;</span>git pull origin -</span></span></code></pre></div></li> -<li>Deploy!: -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> scripts/deploy<span style="color:#000;font-weight:bold">;</span>./cht-deploy -f PATH_TO/values.yaml -</span></span></code></pre></div></li> -<li>Delete it when you&rsquo;re done: -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>helm delete USERNAME-dev --namespace USERNAME-dev -</span></span></code></pre></div></li> -</ol> -<h2 id="references-and-debugging">References and Debugging</h2> -<p>More information on <code>cht-deploy</code> script is available in the <a href="https://github.com/medic/cht-core/blob/master/scripts/deploy/README.md">CHT Core GitHub repository</a> which includes specifics of the <code>values.yaml</code> file and more details about the debugging utilities listed below.</p> -<h3 id="debugging">Debugging</h3> -<p>A summary of the utilities in <code>cht-core/scripts/deploy</code> directory, assuming <code>mrjones-dev</code> namespace:</p> -<ul> -<li>list all resources: <code>./troubleshooting/list-all-resources mrjones-dev</code></li> -<li>view logs, assuming <code>cht-couchdb-1</code> returned from prior command: <code>./troubleshooting/view-logs mrjones-dev cht-couchdb-1</code></li> -<li>describe deployment, assuming <code>cht-couchdb-1</code> returned from 1st command: <code>./troubleshooting/describe-deployment mrjones-dev cht-couchdb-1</code></li> -<li>list all deployments: <code>./troubleshooting/list-all-resources mrjones-dev</code></li> -</ul> -<h3 id="getting-shell">Getting shell</h3> -<p>Sometimes you need to look at files and other key pieces of data that are not available with <a href="https://github.com/medic/cht-core/blob/master/scripts/deploy/troubleshooting/view-logs">the current</a> <code>troubleshooting/view-logs</code> script. In this case, getting an interactive shell on the pod can be helpful.</p> -<ol> -<li>First, get a list pods for your namespace: <code>kubectl -n NAMESPACE get pods</code></li> -<li>After finding the pod you&rsquo;re interested, connect to the pod to get a shell: <code>kubectl -n NAMESPACE exec -it PODNAME/CONTAINERNAME -- /bin/bash</code></li> -</ol> -<h3 id="invalid-apiversion-error"><code>invalid apiVersion</code> Error</h3> -<p>If you get the error:</p> -<blockquote> -<p>exec plugin: invalid apiVersion &ldquo;client.authentication.k8s.io/v1alpha1&rdquo; when running <code>kubectl version</code></p> -</blockquote> -<p>You might be using an version of kubernetes api <code>client.authentication.k8s.io</code> which is not supported by your <code>kubectl</code> client. This can sometimes happen in EKS clusters if aws cli is an older version, in most cases you need at least version <code>2</code> of aws cli. Check version by running: <code>aws --version</code> and note that version <code>2</code> <em>cannot</em> be installed through <code>pip</code> (See <a href="#command-line">Command Line</a> section above for installation instructions)</p> -<h2 id="sre-steps-for-granting-users-access-to-a-namespace">SRE Steps for granting users access to a namespace</h2> -<p>If you&rsquo;re on the SRE/Infra team and want to grant a Medic teammate access to EKS:</p> -<ol> -<li>Tools required: aws, eksctl, kubectl</li> -<li>Create AWS User. -<ul> -<li>Attach IAM policy: Force_MFA and share auto-generated password safely</li> -<li>Have user log in and finish MFA, access key setup</li> -<li>SRE adds you to mfa-required-users group</li> -</ul> -</li> -<li>Add the namespaces and users to <code>tf/eks/dev/access/main.tf</code></li> -<li>Run tofu apply in the folder <code>tf/eks/dev/access</code></li> -<li>Create <code>identitymapping</code> if needed:</li> -</ol> -<p>Reading the <a href="https://docs.aws.amazon.com/eks/latest/userguide/add-user-role.html">AWS guide for principal access</a> may help here!</p>Contribute: Updating Dependencieshttps://docs.communityhealthtoolkit.org/contribute/code/core/update-dependencies/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/core/update-dependencies/ -<p>Every minor release we update dependencies to get the latest fixes and improvements. We do this early in the release cycle so that we have some more time to find regressions and issues. This is done on all folders with a package.json, including:</p> -<ul> -<li>cht-core -<ul> -<li>/ (root)</li> -<li>/admin</li> -<li>/api</li> -<li>/sentinel</li> -<li>/shared-libs/*</li> -<li>/webapp</li> -</ul> -</li> -<li>cht-conf</li> -</ul> -<h2 id="steps">Steps</h2> -<ol> -<li>Checkout and pull the latest default branch - get the latest code</li> -<li>Make a branch: <code>git checkout -b &quot;&lt;issue&gt;-update-dependencies&quot;</code></li> -<li>Take a look at the current <a href="https://github.com/medic/cht-core/issues?q=is%3Aopen+is%3Aissue+label%3ADependencies">list of dependencies related issues</a>, where you can find the latest conversations and information.</li> -</ol> -<p>Then for each folder go through these steps.</p> -<ol> -<li><code>npm ci</code> - update your local node_modules to match expected</li> -<li><code>npm outdated</code> - report on any dependencies which aren&rsquo;t at the latest</li> -<li><code>npm install --save[-dev] package@version</code> - install the latest version (be careful and read the release notes if the new version is a major change from the current)</li> -<li><code>npm dedupe</code> - remove duplicated dependencies</li> -<li><code>npm audit fix</code> - automatically fix any nested dependencies with vulnerabilities</li> -<li><code>npm audit</code> - get a report on any remaining vulnerabilities and manually scan it to see if there&rsquo;s anything else you can do</li> -</ol> -<h2 id="problems">Problems</h2> -<ul> -<li> -<p>Don&rsquo;t update <code>bootstrap</code> to 4+ as it has many breaking changes. One day we will either raise an issue to upgrade it or migrate off it, but that is outside the scope of this change.</p> -</li> -<li> -<p>Don&rsquo;t update <code>bootstrap-daterangepicker</code>.</p> -</li> -<li> -<p>Don&rsquo;t update <code>select2</code> as the latest patch always seems to fail.</p> -</li> -<li> -<p>Don&rsquo;t update <code>jquery</code> to 3.6.0+ as the <code>select2</code> search input loses focus on click event, this is an <a href="https://github.com/select2/select2/issues/5993">open issue</a> in their repository.</p> -</li> -<li> -<p>CHT-Core&rsquo;s webapp is using Enketo and jQuery library, at the same time Enketo internally uses a specific version of jQuery. Make sure webapp installs the same jQuery version than the one Enketo uses internally: <code>3.2.x</code>.</p> -<p>Do this by checking the jquery entry in <code>./webapp/package.json</code> matches <code>./webapp/node_modules/enketo-core/package.json</code>:</p> -<pre tabindex="0"><code>grep &#39;&#34;jquery&#34;&#39; ./webapp/package.json -&#34;jquery&#34;: &#34;3.2.x&#34;, -grep &#39;&#34;jquery&#34;&#39; ./webapp/node_modules/enketo-core/package.json -&#34;jquery&#34;: &#34;3.2.x&#34;, -</code></pre></li> -<li> -<p>Make sure the version of <code>api/enketo-xslt</code> is the same as <code>webapp/enketo-core/enketo-transformer/enketo-xslt</code>.</p> -</li> -<li> -<p>If you have trouble upgrading any other dependency and you think it&rsquo;ll be challenging to fix it then raise a new issue with <code>Upgrade dependencies</code> tag, to upgrade just that dependency. Don&rsquo;t hold up all the other upgrades you&rsquo;ve made.</p> -</li> -</ul> -<h2 id="troubleshooting">Troubleshooting</h2> -<h3 id="angular-exception">Angular exception</h3> -<p>When upgrading Webapp&rsquo;s Angular, you might get the following exception:</p> -<pre tabindex="0"><code>Running &#34;exec:build-webapp&#34; (exec) task -________________________________________ -An unhandled exception occurred: Class extends value undefined is not a constructor or null -see &#34;/private/var/folders/tx/lskdwi/T/ng-23kdi/angular-errors.log&#34; for further details. -&gt;&gt; Exited with code: 127 -</code></pre><p>This error is thrown by the Webpack&rsquo;s subresource integrity. It&rsquo;s likely that <code>@angular/compiler</code>, <code>@angular-devkit/build-angular</code> or <code>@angular-builders/custom-webpack</code> aren&rsquo;t resolved properly in the <code>package-lock.json</code>.</p> -<p>To fix it, uninstall these 3 dependencies and then install them again in this order:</p> -<ol> -<li><code>@angular/compiler</code></li> -<li><code>@angular-devkit/build-angular</code></li> -<li><code>@angular-builders/custom-webpack</code></li> -</ol> -<p>If the error is still happening, try reinstalling <code>@angular/cli</code>.</p> -<h3 id="npm-errno--17">npm errno -17</h3> -<p>If <code>npm ci</code> errors with &ldquo;errno -17&rdquo; in shared-libs you may need to manually remove the nested dependencies from the package-lock.json. This needs move investigation to work out why this is happening.</p> -<h3 id="select2-is-not-a-function">select2 is not a function</h3> -<p>If you get <code>TypeError: &quot;$(...).select2 is not a function&quot;</code> then either:</p> -<ol> -<li>You bumped select2. For some reason this breaks it.</li> -<li>You have multiple jquery libraries and select2 is getting attached to one but not the other. Make sure the jquery versions in enketo-core and webapp match and you&rsquo;ve <code>run dedupe</code> to remove the enketo-core copy.</li> -</ol>Contribute: Automated Testshttps://docs.communityhealthtoolkit.org/contribute/code/core/automated-tests/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/core/automated-tests/ -<h2 id="the-goal-of-automated-testing">The goal of automated testing</h2> -<p>Developers should be able to make changes in the codebase quickly and confidently. A big part of this means knowing that new changes have not impacted other functionality in the system and everything continues to work as expected.</p> -<p>Of course any new functionality itself may or may not work as expected and it is up to the developer to write the appropriate tests to ensure it works correctly in both expected and unexpected scenarios. Tests should give a developer confidence in their own work, and prior tests should give future developers similar confidence.</p> -<p>Automation of testing should speed up development in two significant areas:</p> -<ol> -<li>While making changes, new automated tests can be run regularly to ensure (without lots of manual effort) that the changes continue to work as expected</li> -<li>Avoid large amounts of time spent manually performing regression testing of the whole application to ensure existing functionality keeps working</li> -</ol> -<h2 id="test-types-and-expectations">Test types and expectations</h2> -<p>We seek to have a quality codebase that developers can work on with speed. This means balancing test strategies, quantity, and coverage.</p> -<p>When looking at a well-factored codebase there are three common ways to automate tests (ordered by levels low to high):</p> -<ol> -<li>Unit tests</li> -<li>Integration tests -<ul> -<li>Backend integration tests</li> -<li>Frontend integration tests</li> -</ul> -</li> -<li>End-to-end tests</li> -</ol> -<p><strong>Note:</strong> All the commands to execute the different tests can be found in <a href="https://github.com/medic/cht-core/blob/master/package.json">package.json</a> file.</p> -<h2 id="unit-tests">Unit Tests</h2> -<h3 id="description">Description</h3> -<p>Small tests of specific behavior. Each unit test is only intended to validate an isolated piece (unit) of functionality separated from the rest of the system. Any dependencies are often mocked.</p> -<h3 id="expectations">Expectations</h3> -<p>High coverage of functionality. If measured in branch coverage percentage, aim for 100%. This is the place to guarantee confidence in the system. If a higher-level test spots and error and there&rsquo;s no lower-level test failing, you need to evaluate if a lower test should be written.</p> -<table> -<thead> -<tr> -<th>Execution Speed</th> -<th>Complexity</th> -<th>Fragility</th> -</tr> -</thead> -<tbody> -<tr> -<td>Extremely fast</td> -<td>Extremely low</td> -<td>Extremely stable</td> -</tr> -</tbody> -</table> -<h3 id="implementation">Implementation</h3> -<p>In cht-core unit tests are located in the <code>tests</code> directories of each app (e.g. in <a href="https://github.com/medic/cht-core/tree/master/webapp/tests"><code>webapp/tests</code></a> you can find unit test for the webapp). Run them locally with: <code>npm run unit</code>.</p> -<h2 id="integration-tests">Integration Tests</h2> -<h3 id="description-1">Description</h3> -<p>Tests to exercise how multiple components interact with each other. With a dynamic language like JavaScript these are especially important to verify expectations of interface points. These may mock some parts, but often use the &ldquo;real&rdquo; components since the point is to exercise those components together. As a result, these tests likely involve more setup, potentially involving data scenarios.</p> -<h3 id="expectations-1">Expectations</h3> -<p>Dramatically fewer than unit tests. The goal is not to verify all branches; it is to gain confidence in interface points.</p> -<table> -<thead> -<tr> -<th>Execution Speed</th> -<th>Complexity</th> -<th>Fragility</th> -</tr> -</thead> -<tbody> -<tr> -<td>Fast execution, but slower startup when working with a DB</td> -<td>Mid-to-high. Things can get complex fast when combining parts!</td> -<td>Mostly stable. Fragility risks tend to come from DB setup.</td> -</tr> -</tbody> -</table> -<h3 id="implementation-1">Implementation</h3> -<p>For us, backend integration testing means testing through the entire stack of our application connected to other applications within our system. In the image below, it means that we test each application (box) and its interaction with other applications within our system. -We isolate the tests from the webapp and make the necessary shortcuts to make the test more straightforward and faster. We do not mock any part of the system.</p> -<p><strong>Backend integration tests</strong> are located in <a href="https://github.com/medic/cht-core/tree/master/tests/integration"><code>tests/integration</code></a>. Run them locally with <code>npm run integration-all-local</code> and <code>npm run integration-sentinel-local</code>.</p> -<pre class="mermaid">flowchart LR -subgraph cht-e2e [Docker: cht-e2e] -api -couchdb-1.local[(couchdb-1)] -couchdb-2.local[(couchdb-2)] -couchdb-3.local[(couchdb-3)] -haproxy -nginx -sentinel -nginx --&gt; api -sentinel --&gt; api -api --&gt; haproxy -haproxy --&gt; couchdb-1.local &amp; couchdb-2.local &amp; couchdb-3.local -end -integration-tests{Integration Tests} -integration-tests &lt;--Pouch/HTTPS--&gt; cht-e2e</pre> -<p><strong>Frontend integration tests</strong> (or web component tests) are designed to validate form behavior (including page layout) without needing to run the whole CHT. The web component isolates the enketo form functionality from the CHT webapp. This only covers forms and not other parts of the webapp. It does not trace behavior though the whole system and the database is never involved. Instead, the whole idea of the web component is to abstract the UI functionality away from the underlying backend complexity.</p> -<p>Frontend integration tests are located in <a href="https://github.com/medic/cht-core/tree/master/tests/integration"><code>tests/integration</code></a>. To run them locally you need to build a cht-form Web Component with <code>npm run build-cht-form</code> and <code>npm run integration-cht-form</code> to run the web component tests.</p> -<h2 id="e2e-tests">E2E Tests</h2> -<h3 id="description-2">Description</h3> -<p>Tests that simulate real user experiences to validate the complete system. You can think of e2e test as the user main workflows when using the system.</p> -<h3 id="expectations-2">Expectations</h3> -<p>E2e tests give us the most confidence to decide if the feature is working, but must only check the parts of code that the lower-level tests can&rsquo;t cover. We should push the testing levels as far down as possible.</p> -<table> -<thead> -<tr> -<th>Execution Speed</th> -<th>Complexity</th> -<th>Fragility</th> -</tr> -</thead> -<tbody> -<tr> -<td>Slow. So please make sure to check existent tests and maybe just add extra assertions or minor changes instead of directly adding a specific e2e test for your new change. Also, make sure your code is performant.</td> -<td>Low for the test itself (click tab, enter text into form, click submit, check text on screen. Extremely high for the setup.</td> -<td>Painful fragility with high risk of race conditions and high maintenance burden. Please ensure your code is clean, organized, and utilizes effective selectors.</td> -</tr> -</tbody> -</table> -<h3 id="implementation-2">Implementation</h3> -<p>Our end-to-end tests are designed to test the entire system as a whole. They interact with the webapp as a user would, using <a href="https://webdriver.io/">WebdriverIO</a> to control a headless browser session. They are not isolated from the rest of the system, and they do not use mocking.</p> -<p>End-to-end tests are located in <a href="https://github.com/medic/cht-core/tree/master/tests/e2e"><code>tests/e2e</code></a>. Run them locally with the following:</p> -<ul> -<li><code>npm run wdio-local</code> to run the tests for the default config</li> -<li><code>npm run wdio-default-mobile-local</code> to run the mobile tests</li> -</ul> -<pre class="mermaid">flowchart LR -subgraph cht-e2e [Docker: cht-e2e] -api -couchdb-1.local[(couchdb-1)] -couchdb-2.local[(couchdb-2)] -couchdb-3.local[(couchdb-3)] -haproxy -nginx -sentinel -nginx --&gt; api -sentinel --&gt; api -api --&gt; haproxy -haproxy --&gt; couchdb-1.local &amp; couchdb-2.local &amp; couchdb-3.local -end -subgraph browser -webapp -end -e2e-tests{E2E Tests} -browser &lt;--HTTPS--&gt; cht-e2e -e2e-tests &lt;--wdio--&gt; browser -e2e-tests o--Pouch/HTTPS--o cht-e2e</pre> -<h3 id="how-to-write-automated-e2e-tests">How to write automated e2e tests</h3> -<p>Read the <a href="https://docs.communityhealthtoolkit.org/contribute/code/core/style-guide-automated-e2e-tests/">style guide for automated tests</a> for guidelines on how to create new automated test cases for CHT-Core.</p> -<h3 id="debugging-e2e-tests">Debugging E2E tests</h3> -<p>End to end (e2e) tests can be really difficult to debug - sometimes they fail seemingly at random, and sometimes they only fail on certain environments (eg: ci but not locally). This can make reproducing and reliably fixing the issue challenging, so here are some tips to help!</p> -<h4 id="set-the-debug-flag">Set the <code>DEBUG</code> flag</h4> -<p>Setting the <code>DEBUG</code> environment variable (e.g. <a href="https://github.com/medic/cht-core/blob/master/tests/wdio.conf.js#L103"><code>DEBUG=true npm run wdio-local</code></a>) when running the tests locally will do the following:</p> -<ul> -<li>Run the browser without the <code>headless</code> flag (details in the <a href="https://github.com/medic/cht-core/blob/master/tests/wdio.conf.js#L35"><code>wdio.conf</code></a> file), so the browser will be displayed when running the tests</li> -<li>Increase the test timeout from 2 minutes to 10 minutes</li> -<li>Prevent Mocha from automatically retrying tests that fail (by default a failing test is retried 5 times, details in the <a href="https://github.com/medic/cht-core/blob/master/tests/wdio.conf.js#L198"><code>wdio.conf</code></a>file)</li> -<li>Prevent the <code>cht-e2e</code> Docker containers from being torn down after the test finishes</li> -</ul> -<h4 id="read-the-logs">Read the logs</h4> -<p>Read the failure carefully - it often has really good info but sometimes it&rsquo;s just hard to find. Most importantly it tells you exactly the line in the test that failed and you can look that up in the source to see what it was trying to do. The error message itself is also really useful. Also sometimes one error causes the next, so always start with the first test failure before looking at the others.</p> -<h5 id="known-failure-patterns">Known failure patterns</h5> -<ul> -<li>Can&rsquo;t click on an element because another element would get the click. This usually means a modal dialog was being shown. 90% of the time this is the update notification modal which means some settings change has been detected after the test started execution.</li> -<li>Stale element. This means the DOM element has been removed after it was found on the page but before trying to do something with it. Generally try to find the element just before it needs it to reduce the chance of this happening</li> -</ul> -<h4 id="other-logs-and-screenshots">Other logs and screenshots</h4> -<p>GitHub actions will artifact all files in tests/logs. This is the directory any logs, results, images, etc&hellip; should save to if you want to review them if a build fails.</p> -<h5 id="view-the-ci-report">View the CI report</h5> -<p>There are logs and screenshots stored in the allure reports when a job failed on the CI. To access to those logs follow these steps:</p> -<ul> -<li>Download the CI run artifact zip file located in the failed build&rsquo;s <code>Archive Results</code> section. -<figure class=" center col-12 col-lg-12"><a href="archiveResultsSection.png"> -<img src="archiveResultsSection.png"/> </a> -</figure> -</li> -<li>Extract the <code>.zip</code> file.</li> -<li>From your cht-core directory, run <code>npx allure open &lt;path&gt;/allure-report/</code>. Being <code>&lt;path&gt;</code> the location where the zip file was extracted.</li> -</ul> -<h4 id="running-just-the-failing-test">Running just the failing test</h4> -<p>Running e2e tests can be quite slow so to save time modify the <code>specs</code> property of <a href="https://github.com/medic/cht-core/blob/master/tests/e2e/default/wdio.conf.js#L7"><code>/tests/e2e/**/wdio.conf.js</code></a> so it only finds your test. You can also use <code>describe.skip</code> and <code>it.skip</code> to skip specific tests.</p> -<h5 id="intellij-based">IntelliJ Based</h5> -<ol> -<li>In a terminal, run <code>npm run build-dev-watch</code></li> -<li>In Intellij, open the <a href="https://github.com/medic/cht-core/blob/master/package.json">package.json</a> file</li> -<li>Scroll to the scripts section and click the ▶ button next to <code>wdio-local</code></li> -<li>Select <code>Debug 'wdio-local'</code></li> -</ol> -<h4 id="watching-the-test-run">Watching the test run</h4> -<p>Running the tests locally (e.g. with <code>npm run wdio-local</code>) will allow you to watch it run but if you interact with the page the test will fail in unexpected ways. Furthermore the browser will close after a short timeout so you won&rsquo;t be able to inspect the console or DOM. To do this, force quit the process running the test before it tears down and you will be able to navigate around the app, use Chrome dev tools, and inspect the docs in the database to (hopefully) work out what&rsquo;s going wrong.</p> -<h4 id="running-the-upgrade-e2e-test-locally">Running the upgrade e2e test locally</h4> -<p>To run the upgrade e2e tests in your local environment, follow these steps:</p> -<ul> -<li>Make sure your branch has been published, and it&rsquo;s available in the market: -<ul> -<li>A way to do this is by pushing the branch, let the GitHubActions to run, if all the other e2e are okay, then it will publish the branch.</li> -<li>Check that your branch name is available <a href="https://staging.dev.medicmobile.org/_couch/builds_4/_design/builds/_view/releases">here</a>.</li> -</ul> -</li> -<li>Make sure to stop all existing containers</li> -<li>Set these environment variables: -<ul> -<li><code>export MARKET_URL_READ=https://staging.dev.medicmobile.org</code>.</li> -<li><code>export STAGING_SERVER=_couch/builds_4</code>.</li> -<li><code>export BRANCH=&lt;your branch name&gt;</code>.</li> -<li><code>export BASE_VERSION=&lt;CHT base version&gt;</code>(it can be used <code>latest</code> as the value).</li> -<li><code>export TAG=&lt;CHT version&gt;</code>(Optional, e.g. <code>4.8.1</code>).</li> -</ul> -</li> -<li>Run the upgrade e2e tests: <code>npm run upgrade-wdio</code></li> -</ul> -<p>If you experience errors such as:</p> -<pre tabindex="0"><code>Error in hook: StatusCodeError: 404 - &#34;{\&#34;error\&#34;:\&#34;not_found\&#34;,\&#34;reason\&#34;:\&#34;Document is missing attachment\&#34;}\n&#34; -</code></pre><p>Try the following:</p> -<ul> -<li>It&rsquo;s probably because it can&rsquo;t find the latest released version of CHT, double check that <code>MARKET_URL_READ</code> and <code>STAGING_SERVER</code> environment variables are set.</li> -</ul> -<p>If you experience errors such as:</p> -<pre tabindex="0"><code>If you are seeing this locally, it can mean that your internet is too slow to download all images in the allotted time. -Either run the test multiple times until you load all images, download images manually or increase this timeout. -</code></pre><p>Try the following:</p> -<ul> -<li>Manually downloaded the images. To download images manually, you can use either docker-compose or docker: -<ul> -<li>With docker, you&rsquo;d do a docker pull <image tag> for every image you want to download.</li> -<li>With docker-compose, you&rsquo;d save all docker-compose files in a folder, do a docker-compose pull, and point to your files as a source. Read more on <a href="https://docs.docker.com/engine/reference/commandline/compose_pull/">docker compose pull</a></li> -</ul> -</li> -</ul> -<h3 id="test-architecture">Test Architecture</h3> -<p>Our GitHub actions spin up an ubuntu-22.04 machine. They install software and then launch couchdb and horticulturalist in a docker container. This is needed to run our applications in the specific node versions we support, while allowing our test code to run in versions of node it supports. This creates a paradigm to keep in mind when writing tests. Tests run on the ubuntu machine. -Any test code that starts a server or runs an executable is running outside of the horti container. The ports are exposed for all our services and horti has access to the cht-core root via a volume. Horti can also talk to the host by getting the gateway of the docker network.</p>Contribute: Style guide for automated testshttps://docs.communityhealthtoolkit.org/contribute/code/core/style-guide-automated-e2e-tests/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/core/style-guide-automated-e2e-tests/ -<p>There are three files that are the base of every new automated test case, the most important one is the <code>spec</code> file, which contains the actual test that will be executed.</p> -<p>Automated tests cover different <a href="https://github.com/medic/cht-core/tree/master/config">CHT Configs</a>, consider the following setups when writing a new test:</p> -<ul> -<li><strong>default</strong> -<ul> -<li>Config file: <a href="https://github.com/medic/cht-core/blob/master/tests/e2e/default/wdio.conf.js"><code>../tests/e2e/default/wdio.conf.js</code></a></li> -<li>Name convention for the <code>spec</code> file: <code>../tests/e2e/default/*/&lt;name&gt;.wdio-spec.js</code></li> -<li>Command to execute the tests that belong to this config: <code>npm run wdio-local</code></li> -</ul> -</li> -</ul> -<p><strong>Important:</strong> Make sure the <code>spec</code> file follows the name convention, otherwise the file won&rsquo;t be executed.</p> -<h2 id="base-files-to-use">Base files to use</h2> -<ul> -<li> -<p>Page-object file. (<code>../tests/page-objects/../enketo/&lt;name&gt;.wdio.page.js</code>) -We are leveraging the <a href="https://www.thoughtworks.com/insights/blog/using-page-objects-overcome-protractors-shortcomings">page object model</a> for structure. When identifying elements they should be added to a page object and not within a test file. Add functions that perform actions to the page within the page object. Keep expects outside of page objects. The tests should be self-documenting.</p> -</li> -<li> -<p>Data file.</p> -<ul> -<li> -<p>Create test data using the <a href="https://github.com/medic/cht-core/blob/master/tests/factories/cht/contacts/place.js"><code>place factory</code></a>, the <a href="https://github.com/medic/cht-core/blob/master/tests/factories/cht/users/users.js"><code>user factory</code></a> or the <a href="https://github.com/medic/cht-core/blob/master/tests/factories/cht/contacts/person.js"><code>person factory</code></a> files.</p> -<p>Using the &ldquo;factories&rdquo; will allow you to create hierarchies, contacts and patients that are associated with specific places and with their specific attributes and information. It can create offline or online users that can be used to login with different roles. <strong>Everything can be customised depending on the test requirements</strong>.</p> -</li> -</ul> -</li> -<li> -<p>e2e testing file. Use the correct name convention when working with the following configs:</p> -<ul> -<li>default: (<code>../tests/e2e/default/*/&lt;name&gt;.wdio-spec.js</code>)</li> -</ul> -<p>This file should contain <strong>only</strong> the scenario setup and assertions of the test that is going to be executed. All the DOM queries, logging, contact creation and data assignments should be delegated to the Page Object file and the Data file. This will increase test readability and code reusability. For a better understanding follow these files as examples:</p> -<ul> -<li>default config: <a href="https://github.com/medic/cht-core/blob/master/tests/e2e/default/enketo/pregnancy-visit.wdio-spec.js"><code>../tests/e2e/default/enketo/pregnancy-visit.wdio-spec.js</code></a>.</li> -</ul> -</li> -</ul> -<h2 id="tips-to-write-test-cases">Tips to write test cases</h2> -<h3 id="file-structure-spec-files">File Structure (spec files)</h3> -<p>Test files should represent a feature within the application. Use <code>describe</code> to identify the feature and to group test cases. Use <code>it</code> to detail individual test cases covering the feature&rsquo;s functionality.</p> -<p>Observe how the following example defines a <code>describe</code> using the feature name <code>Immunization Visit</code>. It contains two test cases which detail the expected results <code>should add a new child under 2 years old</code> and <code>should submit an immunization visit</code>.</p> -<p>Ex:</p> -<pre tabindex="0"><code> describe(&#39;Immunization Visit&#39;, () =&gt; { -it(&#39;should add a new child under 2 years old&#39;, () =&gt; { .... }); -it(&#39;should submit an immunization visit&#39;, () =&gt; { .... }); -... -}); -</code></pre><h3 id="name-convention-and-file-location">Name convention and file location</h3> -<ul> -<li>Since every test is created by <em>&ldquo;selectors&rdquo;</em>, it makes sense to locate them into a folder that represents the page being tested and not the feature. Please try to locate every test file in the correct folder.</li> -<li>Every file name should use <code>dash-case</code> (<code>-</code>). Do not use <code>snake-case</code> (<code>_</code>) nor <code>camelCase</code>. Please consider the following examples: -<ul> -<li>Correct: -<ul> -<li><code>pregnancy-visit.wdio-spec.js</code></li> -</ul> -</li> -<li>Incorrect: -<ul> -<li><code>pregnancy_visit.wdio-spec.js</code></li> -<li><code>pregnancyVisit.wdio-spec.js</code></li> -<li><code>PregnancyVisit.wdio-spec.js</code></li> -</ul> -</li> -</ul> -</li> -<li>Whenever possible avoid repeating the folder name in the file name. Please consider the following examples where the file is located in the path <code>e2e/default/enketo</code>: -<ul> -<li>Correct: -<ul> -<li><code>pregnancy-visit.wdio-spec.js</code></li> -</ul> -</li> -<li>Incorrect: -<ul> -<li><code>pregnancy-visit-enketo.wdio-spec.js</code></li> -<li><code>pregnancy-visit-default.wdio-spec.js</code></li> -<li><code>pregnancy-visit-enketo-default.wdio-spec.js</code></li> -</ul> -</li> -</ul> -</li> -</ul> -<h3 id="adding-identifiers">Adding identifiers</h3> -<p>In some cases, adding a unique identifier to an element may be necessary. This could be a piece of data related to the element, or a unique name (which can be done by adding a <code>test-</code> attribute to the app code).</p> -<p>Ex: <code>attr.test-id=&quot;{{ msg.key }}&quot; </code> Will add a <code>test-id</code> attribute with the data from the app.</p> -<p>Then it can be consumed in the test by getting an element by css. EX: <code>element(by.css(`#message-list li[test-id=&quot;${identifier}&quot;]`)),</code></p> -<p>Adding a test identifier is a good option for cases where a CSS selector would otherwise be fragile such as selecting based on an assumed element structure or selecting on CSS classes intended for visual design that may change.</p> -<h3 id="test-tags">Test tags</h3> -<p>You can tag mocha tests and update suites to only include or exclude certain tags (<a href="https://github.com/mochajs/mocha/wiki/Tagging">Mocha Wiki</a>).</p> -<p>Existent tags:</p> -<h4 id="docker">@docker</h4> -<p>Tests that should run exclusively when running the suite over docker infrastructure. These tests will fail if run over k3d.</p> -<h3 id="notes">Notes:</h3> -<ul> -<li> -<p>We decided to separate every functionality in files/folders because we want to make sure that we can reuse as much code as possible. If something new is implemented and might be used for another test, then please isolate the code in a separate file, so it can be reused in future tests.</p> -</li> -<li> -<p>If the new test is not associated to a specific configuration, please locate the test inside the correct folder of the default config <a href="https://github.com/medic/cht-core/tree/master/tests/e2e/default"><code>e2e/default/*</code></a>.</p> -</li> -</ul>Contribute: Developing on Windowshttps://docs.communityhealthtoolkit.org/contribute/code/core/using-windows/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/core/using-windows/ -<p>We don&rsquo;t actively support development on Windows, instead preferring MacOS or Linux.</p> -<p>However, Microsoft has recently been stabilizing their <a href="https://docs.microsoft.com/en-us/windows/wsl/about">Windows Subsystem for Linux</a>, which appears to work reasonably well for development.</p> -<p>Installation instructions are mostly the same as they written in <a href="https://github.com/medic/cht-core/blob/master/README.md">the README</a> with a couple of caveats as of time of writing (2019-07-25), noted below.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Both the Windows Subsystem for Linux and Medic&rsquo;s support for developing in it is very much in beta. These are advanced instructions, expect some understanding of linux and may not always work. Be patient and raise bugs as you find them! -</div> -<h2 id="installing-ubuntu-in-the-windows-subsystem-for-linux">Installing Ubuntu in the Windows Subsystem for Linux.</h2> -<p>For the rest of this document we&rsquo;re going to presume that you&rsquo;re using Ubuntu (18.04) in WSL. Medic probably works on all distributions, but Ubuntu is likely the best supported.</p> -<p>First, follow Microsoft&rsquo;s <a href="https://docs.microsoft.com/en-us/windows/wsl/install-win10">instructions on enabling and installing linux</a>. At the end of this process you should have a linux terminal.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -For the rest of this tutorial <strong>in linux</strong> means code executing or performing actions in the WSL, while <strong>in Windows</strong> means code executing or performing actions in Windows natively. -</div> -<h2 id="couchdb">CouchDB</h2> -<p>As of writing CouchDB wouldn&rsquo;t autostart (due to systemd not existing?), and wasn&rsquo;t manually starting due to erlang errors.</p> -<p>Luckily, there is a perfectly working CouchDB installation for Windows:</p> -<ul> -<li>Download from <a href="https://couchdb.apache.org/#download">CouchDB</a> and install the Windows version. This will create a Windows service.</li> -<li>Run it either by directly executing <code>C:\CouchDB\bin\couchdb.cmd</code> or by starting the service</li> -</ul> -<p>Then go to <code>http://localhost:5984/_utils/#/setup</code> in Windows and do the single node setup. Once done head back to linux and confirm it works:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$: curl http://localhost:5984/ -</span></span><span style="display:flex;"><span><span style="color:#ce5c00;font-weight:bold">{</span><span style="color:#4e9a06">&#34;couchdb&#34;</span>:<span style="color:#4e9a06">&#34;Welcome&#34;</span>,<span style="color:#4e9a06">&#34;version&#34;</span>:<span style="color:#4e9a06">&#34;2.3.1&#34;</span>,<span style="color:#4e9a06">&#34;git_sha&#34;</span>:<span style="color:#4e9a06">&#34;c298091a4&#34;</span>,<span style="color:#4e9a06">&#34;uuid&#34;</span>:<span style="color:#4e9a06">&#34;5f60350abaaa11c0131a5630e83ae979&#34;</span>,<span style="color:#4e9a06">&#34;features&#34;</span>:<span style="color:#ce5c00;font-weight:bold">[</span><span style="color:#4e9a06">&#34;pluggable-storage-engines&#34;</span>,<span style="color:#4e9a06">&#34;scheduler&#34;</span><span style="color:#ce5c00;font-weight:bold">]</span>,<span style="color:#4e9a06">&#34;vendor&#34;</span>:<span style="color:#ce5c00;font-weight:bold">{</span><span style="color:#4e9a06">&#34;name&#34;</span>:<span style="color:#4e9a06">&#34;The Apache Software Foundation&#34;</span><span style="color:#ce5c00;font-weight:bold">}}</span> -</span></span></code></pre></div><h2 id="installing-npm">Installing NPM</h2> -<p>Start your WSL instance (Ubuntu), not WSL as they take you to two different default directories.</p> -<p>The default <code>npm</code> in linux is really old and doesn&rsquo;t have <code>npm ci</code>, which we need.</p> -<p>Instead use <a href="https://github.com/nvm-sh/nvm">nvm</a> to install <code>nvm install 11.3</code> .</p> -<h2 id="checking-out-the-code">Checking out the code</h2> -<p>We used git that&rsquo;s preinstalled with Ubuntu to check out the code.</p> -<p>You can checkout cht code inside WSL itself. You can checkout anywhere you have write access. We&rsquo;ll checkout inside /home/username/medic directory.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$: mkdir ~/medic <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#204a87">cd</span> ~/medic -</span></span><span style="display:flex;"><span>$: git clone https://github.com/medic/cht-core.git -</span></span></code></pre></div><h2 id="setup-environment-variables">Setup Environment Variables</h2> -<p>Using <code>.bashrc</code> works as expected, and so is a good place to put exports:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"># Medic stuff</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87">export</span> <span style="color:#000">COUCH_URL</span><span style="color:#ce5c00;font-weight:bold">=</span>http://admin:pass@localhost:5984/medic -</span></span><span style="display:flex;"><span><span style="color:#204a87">export</span> <span style="color:#000">COUCH_NODE_NAME</span><span style="color:#ce5c00;font-weight:bold">=</span>couchdb@localhost -</span></span></code></pre></div><h2 id="everything-else">Everything else</h2> -<p><code>npm ci</code> should just work once you&rsquo;ve installed a latest version of node via nvm as noted above.</p> -<p>Also install xstproc in your WSL:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$: sudo apt-get update -</span></span><span style="display:flex;"><span>$: sudo apt-get install xsltproc -</span></span></code></pre></div><p>Now you can build the web app.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$: <span style="color:#204a87">cd</span> ~/medic/cht-core/ -</span></span><span style="display:flex;"><span>$: npm ci -</span></span><span style="display:flex;"><span>$: npm run build-dev-watch -</span></span></code></pre></div><p>From this point, follow the <code>harden couch</code> section in <a href="https://docs.communityhealthtoolkit.org/contribute/code/core/dev-environment/#cht-core-cloning-and-setup">Core Developer Setup</a>.</p> -<p>To get multiple linux terminals (so you can run <code>npm run</code>, <code>api</code> and <code>sentinel</code> at the same time) either install and use something like Tmux, or if you click <code>Ubuntu</code> in the Windows start menu again it will open up a new terminal in the same linux instance.</p> -<p>Once you&rsquo;re done with the default instructions and have api running, check if it works by going to http://localhost:5988 in Chrome or Firefox.</p> -<h2 id="editing-code">Editing Code</h2> -<p>If you want to make changes to your code or contribute to our community health toolkit, you can do so by editing code from your favorite editor. If you editor supports UNC path, you can access and edit files inside WSL from <code>\\wsl$\Ubuntu\&lt;cht-core-location&gt;</code>. If you use Visual Studio Code, it&rsquo;s even easier to edit your code. Just navigate to where you have checked out cht-core and type <code>code .</code> This will download VS Code Server for Ubuntu and open the project in Visual Studio Code in windows.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$: <span style="color:#204a87">cd</span> ~/medic/cht-core -</span></span><span style="display:flex;"><span>$: code . -</span></span></code></pre></div><h2 id="default-port-and-credentials-for-cht-core-web">Default port and credentials for cht-core web</h2> -<p>The default launch port for cht-core is 5988, which can be changed by providing the environment variable at runtime <code>API_PORT</code>, for example: <code>API_PORT=6000 node server.js</code>.</p> -<p>The deployed web app&rsquo;s default user name and password is the username and password we set for CouchDB in the initial steps.</p> -<h2 id="problems">Problems?</h2> -<p>As none of our code developers use Windows as a development environment daily this solution may not be as stable as directly using MacOS or Linux. If you encounter issues please let a developer know</p>Contribute: Running multiple Chrome versionshttps://docs.communityhealthtoolkit.org/contribute/code/core/run-multiple-chrome-versions/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/core/run-multiple-chrome-versions/ -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -These steps are suitable for Mac. It was tested in a Mac Intel. It can be adapted to any Chrome version. -</div> -<p>Follow these steps on a Mac to run Chrome version 90 while having another Chrome app on a different version.</p> -<ul> -<li>Download Chrome <code>90.0.4430.72</code> from <a href="https://www.slimjet.com/chrome/google-chrome-old-version.php?cmtx_sort=">slimjet</a></li> -<li>Do not install the <code>Google Chrome.app</code> in your <code>Application</code> folder. Install it in your <code>Desktop</code> folder for example.</li> -<li>Change the name of the app to <code>Google Chrome 90.app</code> and then move it to the <code>Application</code> folder, <em><strong>without</strong></em> overwriting your current Chrome</li> -<li>Remove Chrome’s automatic updates by: -<ul> -<li>Close all Chrome open instances.</li> -<li>Open <code>Google Chrome 90.app</code> while not having Internet, so it doesn&rsquo;t give an error after modifying the <code>Info.plis</code>.</li> -<li>Right-click on <code>Application/Google Chrome 90.app</code></li> -<li>Click on <code>Show package content</code></li> -<li>Open the file <code>Contents/Info.plist</code> in your IDE</li> -<li>Find the key <code>KSUpdateURL</code></li> -<li>Replace the string below: <string><a href="https://tools.google.com/service/update2">https://tools.google.com/service/update2</a></string> to <string><a href="https://tools.google.com/%5BDUMMYTEXT%5D">https://tools.google.com/[DUMMYTEXT]</a></string></li> -</ul> -</li> -<li>Run <code>Google Chrome 90.app</code> as long as it’s the only one running</li> -</ul> \ No newline at end of file +Contributing CHT Core Code on Community Health Toolkithttps://docs.communityhealthtoolkit.org/contribute/code/core/Recent content in Contributing CHT Core Code on Community Health ToolkitHugo -- gohugo.ioenCHT Core dev environment setuphttps://docs.communityhealthtoolkit.org/contribute/code/core/dev-environment/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/core/dev-environment/Note This guide assumes you are a CHT Core developer wanting to run the CHT Core from source code to make commits to the public GitHub repository. To set up your environment for developing apps, see the app guide. +To deploy the CHT in production, see either AWS hosting or Self hosting +Note These steps apply to both 3.x and 4.x CHT core development, unless stated otherwise. The Happy Path Installation CHT Core development can be done on Linux, macOS, or Windows (using the Windows Subsystem for Linux (WSL2)).Build commandshttps://docs.communityhealthtoolkit.org/contribute/code/core/build-commands/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/core/build-commands/CHT Core build commands These commands are defined in the package.json and can be executed with npm run &lt;command&gt; from the cht-core repository directory. +Development build commands For developers (humans) to execute to build cht-core. +Command Description build-ddocs Compiles all the DDocs and outputs them into /api/build/ddocs ready for deployment. build-dev Updates dependencies and builds all the applications. build-dev-watch Same as build-dev, but keeps watching for any code changes and automatically deploys on change.Deploy CHT Core on Medic hosted EKShttps://docs.communityhealthtoolkit.org/contribute/code/core/deploy-on-eks/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/core/deploy-on-eks/While not directly available to the public who might be doing CHT Core development, having Medic&rsquo;s process for using our Amazon Elastic Kubernetes Service (AWS EKS) publicly documented will help Medic employees new to EKS. As well, hopefully external developers looking to re-use Medic tools and process to use EKS will find it helpful. +While these instructions assume you work at Medic and have access to private GitHub repositories, many of the tools are fully open source.Updating Dependencieshttps://docs.communityhealthtoolkit.org/contribute/code/core/update-dependencies/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/core/update-dependencies/Every minor release we update dependencies to get the latest fixes and improvements. We do this early in the release cycle so that we have some more time to find regressions and issues. This is done on all folders with a package.json, including: +cht-core / (root) /admin /api /sentinel /shared-libs/* /webapp cht-conf Steps Checkout and pull the latest default branch - get the latest code Make a branch: git checkout -b &quot;&lt;issue&gt;-update-dependencies&quot; Take a look at the current list of dependencies related issues, where you can find the latest conversations and information.Automated Testshttps://docs.communityhealthtoolkit.org/contribute/code/core/automated-tests/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/core/automated-tests/The goal of automated testing Developers should be able to make changes in the codebase quickly and confidently. A big part of this means knowing that new changes have not impacted other functionality in the system and everything continues to work as expected. +Of course any new functionality itself may or may not work as expected and it is up to the developer to write the appropriate tests to ensure it works correctly in both expected and unexpected scenarios.Style guide for automated testshttps://docs.communityhealthtoolkit.org/contribute/code/core/style-guide-automated-e2e-tests/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/core/style-guide-automated-e2e-tests/There are three files that are the base of every new automated test case, the most important one is the spec file, which contains the actual test that will be executed. +Automated tests cover different CHT Configs, consider the following setups when writing a new test: +default Config file: ../tests/e2e/default/wdio.conf.js Name convention for the spec file: ../tests/e2e/default/*/&lt;name&gt;.wdio-spec.js Command to execute the tests that belong to this config: npm run wdio-local Important: Make sure the spec file follows the name convention, otherwise the file won&rsquo;t be executed.Developing on Windowshttps://docs.communityhealthtoolkit.org/contribute/code/core/using-windows/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/core/using-windows/We don&rsquo;t actively support development on Windows, instead preferring MacOS or Linux. +However, Microsoft has recently been stabilizing their Windows Subsystem for Linux, which appears to work reasonably well for development. +Installation instructions are mostly the same as they written in the README with a couple of caveats as of time of writing (2019-07-25), noted below. +Note Both the Windows Subsystem for Linux and Medic&rsquo;s support for developing in it is very much in beta.Running multiple Chrome versionshttps://docs.communityhealthtoolkit.org/contribute/code/core/run-multiple-chrome-versions/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/core/run-multiple-chrome-versions/Note These steps are suitable for Mac. It was tested in a Mac Intel. It can be adapted to any Chrome version. Follow these steps on a Mac to run Chrome version 90 while having another Chrome app on a different version. +Download Chrome 90.0.4430.72 from slimjet Do not install the Google Chrome.app in your Application folder. Install it in your Desktop folder for example. Change the name of the app to Google Chrome 90. \ No newline at end of file diff --git a/contribute/code/core/run-multiple-chrome-versions/index.html b/contribute/code/core/run-multiple-chrome-versions/index.html index ff4a9518a6..5941ebac01 100644 --- a/contribute/code/core/run-multiple-chrome-versions/index.html +++ b/contribute/code/core/run-multiple-chrome-versions/index.html @@ -1,9 +1,9 @@ -Running multiple Chrome versions | Community Health Toolkit +Running multiple Chrome versions | Community Health Toolkit

    Running multiple Chrome versions

    How to run multiple Chrome versions on your local machine

    Follow these steps on a Mac to run Chrome version 90 while having another Chrome app on a different version.

    • Download Chrome 90.0.4430.72 from slimjet
    • Do not install the Google Chrome.app in your Application folder. Install it in your Desktop folder for example.
    • Change the name of the app to Google Chrome 90.app and then move it to the Application folder, without overwriting your current Chrome
    • Remove Chrome’s automatic updates by:
      • Close all Chrome open instances.
      • Open Google Chrome 90.app while not having Internet, so it doesn’t give an error after modifying the Info.plis.
      • Right-click on Application/Google Chrome 90.app
      • Click on Show package content
      • Open the file Contents/Info.plist in your IDE
      • Find the key KSUpdateURL
      • Replace the string below: https://tools.google.com/service/update2 to https://tools.google.com/[DUMMYTEXT]
    • Run Google Chrome 90.app as long as it’s the only one running
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/code/core/style-guide-automated-e2e-tests/index.html b/contribute/code/core/style-guide-automated-e2e-tests/index.html index ad3f754db7..c938c20eaf 100644 --- a/contribute/code/core/style-guide-automated-e2e-tests/index.html +++ b/contribute/code/core/style-guide-automated-e2e-tests/index.html @@ -1,9 +1,9 @@ -Style guide for automated tests | Community Health Toolkit +Style guide for automated tests | Community Health Toolkit

    Style guide for automated tests

    This style guide provides editorial guidelines for anyone creating new automated test cases for the CHT-Core.

    There are three files that are the base of every new automated test case, the most important one is the spec file, which contains the actual test that will be executed.

    Automated tests cover different CHT Configs, consider the following setups when writing a new test:

    • default
      • Config file: ../tests/e2e/default/wdio.conf.js
      • Name convention for the spec file: ../tests/e2e/default/*/<name>.wdio-spec.js
      • Command to execute the tests that belong to this config: npm run wdio-local

    Important: Make sure the spec file follows the name convention, otherwise the file won’t be executed.

    Base files to use

    Style guide for automated tests

    This style guide provides editorial guidelines for anyone creating new automated test cases for the CHT-Core.

    There are three files that are the base of every new automated test case, the most important one is the spec file, which contains the actual test that will be executed.

    Automated tests cover different CHT Configs, consider the following setups when writing a new test:

    • default
      • Config file: ../tests/e2e/default/wdio.conf.js
      • Name convention for the spec file: ../tests/e2e/default/*/<name>.wdio-spec.js
      • Command to execute the tests that belong to this config: npm run wdio-local

    Important: Make sure the spec file follows the name convention, otherwise the file won’t be executed.

    Base files to use

    • Page-object file. (../tests/page-objects/../enketo/<name>.wdio.page.js) We are leveraging the page object model for structure. When identifying elements they should be added to a page object and not within a test file. Add functions that perform actions to the page within the page object. Keep expects outside of page objects. The tests should be self-documenting.

    • Data file.

      • Create test data using the place factory, the user factory or the person factory files.

        Using the “factories” will allow you to create hierarchies, contacts and patients that are associated with specific places and with their specific attributes and information. It can create offline or online users that can be used to login with different roles. Everything can be customised depending on the test requirements.

    • e2e testing file. Use the correct name convention when working with the following configs:

      • default: (../tests/e2e/default/*/<name>.wdio-spec.js)

      This file should contain only the scenario setup and assertions of the test that is going to be executed. All the DOM queries, logging, contact creation and data assignments should be delegated to the Page Object file and the Data file. This will increase test readability and code reusability. For a better understanding follow these files as examples:

    Tips to write test cases

    File Structure (spec files)

    Test files should represent a feature within the application. Use describe to identify the feature and to group test cases. Use it to detail individual test cases covering the feature’s functionality.

    Observe how the following example defines a describe using the feature name Immunization Visit. It contains two test cases which detail the expected results should add a new child under 2 years old and should submit an immunization visit.

    Ex:

      describe('Immunization Visit', () => {
         it('should add a new child under 2 years old', () => { .... });
         it('should submit an immunization visit', () => { .... });
         ...
       });
     

    Name convention and file location

    • Since every test is created by “selectors”, it makes sense to locate them into a folder that represents the page being tested and not the feature. Please try to locate every test file in the correct folder.
    • Every file name should use dash-case (-). Do not use snake-case (_) nor camelCase. Please consider the following examples:
      • Correct:
        • pregnancy-visit.wdio-spec.js
      • Incorrect:
        • pregnancy_visit.wdio-spec.js
        • pregnancyVisit.wdio-spec.js
        • PregnancyVisit.wdio-spec.js
    • Whenever possible avoid repeating the folder name in the file name. Please consider the following examples where the file is located in the path e2e/default/enketo:
      • Correct:
        • pregnancy-visit.wdio-spec.js
      • Incorrect:
        • pregnancy-visit-enketo.wdio-spec.js
        • pregnancy-visit-default.wdio-spec.js
        • pregnancy-visit-enketo-default.wdio-spec.js

    Adding identifiers

    In some cases, adding a unique identifier to an element may be necessary. This could be a piece of data related to the element, or a unique name (which can be done by adding a test- attribute to the app code).

    Ex: attr.test-id="{{ msg.key }}" Will add a test-id attribute with the data from the app.

    Then it can be consumed in the test by getting an element by css. EX: element(by.css(`#message-list li[test-id="${identifier}"]`)),

    Adding a test identifier is a good option for cases where a CSS selector would otherwise be fragile such as selecting based on an assumed element structure or selecting on CSS classes intended for visual design that may change.

    Test tags

    You can tag mocha tests and update suites to only include or exclude certain tags (Mocha Wiki).

    Existent tags:

    @docker

    Tests that should run exclusively when running the suite over docker infrastructure. These tests will fail if run over k3d.

    Notes:

    • We decided to separate every functionality in files/folders because we want to make sure that we can reuse as much code as possible. If something new is implemented and might be used for another test, then please isolate the code in a separate file, so it can be reused in future tests.

    • If the new test is not associated to a specific configuration, please locate the test inside the correct folder of the default config e2e/default/*.

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/contribute/code/core/update-dependencies/index.html b/contribute/code/core/update-dependencies/index.html index e4b59c9898..60766a766b 100644 --- a/contribute/code/core/update-dependencies/index.html +++ b/contribute/code/core/update-dependencies/index.html @@ -1,9 +1,9 @@ -Updating Dependencies | Community Health Toolkit +Updating Dependencies | Community Health Toolkit

    Updating Dependencies

    Process for updating dependencies

    Every minor release we update dependencies to get the latest fixes and improvements. We do this early in the release cycle so that we have some more time to find regressions and issues. This is done on all folders with a package.json, including:

    • cht-core
      • / (root)
      • /admin
      • /api
      • /sentinel
      • /shared-libs/*
      • /webapp
    • cht-conf

    Steps

    1. Checkout and pull the latest default branch - get the latest code
    2. Make a branch: git checkout -b "<issue>-update-dependencies"
    3. Take a look at the current list of dependencies related issues, where you can find the latest conversations and information.

    Then for each folder go through these steps.

    1. npm ci - update your local node_modules to match expected
    2. npm outdated - report on any dependencies which aren’t at the latest
    3. npm install --save[-dev] package@version - install the latest version (be careful and read the release notes if the new version is a major change from the current)
    4. npm dedupe - remove duplicated dependencies
    5. npm audit fix - automatically fix any nested dependencies with vulnerabilities
    6. npm audit - get a report on any remaining vulnerabilities and manually scan it to see if there’s anything else you can do

    Problems

    • Don’t update bootstrap to 4+ as it has many breaking changes. One day we will either raise an issue to upgrade it or migrate off it, but that is outside the scope of this change.

    • Don’t update bootstrap-daterangepicker.

    • Don’t update select2 as the latest patch always seems to fail.

    • Don’t update jquery to 3.6.0+ as the select2 search input loses focus on click event, this is an open issue in their repository.

    • CHT-Core’s webapp is using Enketo and jQuery library, at the same time Enketo internally uses a specific version of jQuery. Make sure webapp installs the same jQuery version than the one Enketo uses internally: 3.2.x.

      Do this by checking the jquery entry in ./webapp/package.json matches ./webapp/node_modules/enketo-core/package.json:

      grep '"jquery"' ./webapp/package.json
      + Create project issue

    Updating Dependencies

    Process for updating dependencies

    Every minor release we update dependencies to get the latest fixes and improvements. We do this early in the release cycle so that we have some more time to find regressions and issues. This is done on all folders with a package.json, including:

    • cht-core
      • / (root)
      • /admin
      • /api
      • /sentinel
      • /shared-libs/*
      • /webapp
    • cht-conf

    Steps

    1. Checkout and pull the latest default branch - get the latest code
    2. Make a branch: git checkout -b "<issue>-update-dependencies"
    3. Take a look at the current list of dependencies related issues, where you can find the latest conversations and information.

    Then for each folder go through these steps.

    1. npm ci - update your local node_modules to match expected
    2. npm outdated - report on any dependencies which aren’t at the latest
    3. npm install --save[-dev] package@version - install the latest version (be careful and read the release notes if the new version is a major change from the current)
    4. npm dedupe - remove duplicated dependencies
    5. npm audit fix - automatically fix any nested dependencies with vulnerabilities
    6. npm audit - get a report on any remaining vulnerabilities and manually scan it to see if there’s anything else you can do

    Problems

    • Don’t update bootstrap to 4+ as it has many breaking changes. One day we will either raise an issue to upgrade it or migrate off it, but that is outside the scope of this change.

    • Don’t update bootstrap-daterangepicker.

    • Don’t update select2 as the latest patch always seems to fail.

    • Don’t update jquery to 3.6.0+ as the select2 search input loses focus on click event, this is an open issue in their repository.

    • CHT-Core’s webapp is using Enketo and jQuery library, at the same time Enketo internally uses a specific version of jQuery. Make sure webapp installs the same jQuery version than the one Enketo uses internally: 3.2.x.

      Do this by checking the jquery entry in ./webapp/package.json matches ./webapp/node_modules/enketo-core/package.json:

      grep '"jquery"' ./webapp/package.json
       "jquery": "3.2.x",
       grep '"jquery"' ./webapp/node_modules/enketo-core/package.json
       "jquery": "3.2.x",
      @@ -310,7 +310,8 @@
       see "/private/var/folders/tx/lskdwi/T/ng-23kdi/angular-errors.log" for further details.
       >> Exited with code: 127
       

      This error is thrown by the Webpack’s subresource integrity. It’s likely that @angular/compiler, @angular-devkit/build-angular or @angular-builders/custom-webpack aren’t resolved properly in the package-lock.json.

      To fix it, uninstall these 3 dependencies and then install them again in this order:

      1. @angular/compiler
      2. @angular-devkit/build-angular
      3. @angular-builders/custom-webpack

      If the error is still happening, try reinstalling @angular/cli.

      npm errno -17

      If npm ci errors with “errno -17” in shared-libs you may need to manually remove the nested dependencies from the package-lock.json. This needs move investigation to work out why this is happening.

      select2 is not a function

      If you get TypeError: "$(...).select2 is not a function" then either:

      1. You bumped select2. For some reason this breaks it.
      2. You have multiple jquery libraries and select2 is getting attached to one but not the other. Make sure the jquery versions in enketo-core and webapp match and you’ve run dedupe to remove the enketo-core copy.
      -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/contribute/code/core/using-windows/index.html b/contribute/code/core/using-windows/index.html index 5472b1593d..d5fb2c362f 100644 --- a/contribute/code/core/using-windows/index.html +++ b/contribute/code/core/using-windows/index.html @@ -1,9 +1,9 @@ -Developing on Windows | Community Health Toolkit +Developing on Windows | Community Health Toolkit

    Developing on Windows

    Notes for developing on Windows

    We don’t actively support development on Windows, instead preferring MacOS or Linux.

    However, Microsoft has recently been stabilizing their Windows Subsystem for Linux, which appears to work reasonably well for development.

    Installation instructions are mostly the same as they written in the README with a couple of caveats as of time of writing (2019-07-25), noted below.

    Installing Ubuntu in the Windows Subsystem for Linux.

    For the rest of this document we’re going to presume that you’re using Ubuntu (18.04) in WSL. Medic probably works on all distributions, but Ubuntu is likely the best supported.

    First, follow Microsoft’s instructions on enabling and installing linux. At the end of this process you should have a linux terminal.

    CouchDB

    As of writing CouchDB wouldn’t autostart (due to systemd not existing?), and wasn’t manually starting due to erlang errors.

    Luckily, there is a perfectly working CouchDB installation for Windows:

    • Download from CouchDB and install the Windows version. This will create a Windows service.
    • Run it either by directly executing C:\CouchDB\bin\couchdb.cmd or by starting the service

    Then go to http://localhost:5984/_utils/#/setup in Windows and do the single node setup. Once done head back to linux and confirm it works:

    $: curl http://localhost:5984/
    + Create project issue

    Developing on Windows

    Notes for developing on Windows

    We don’t actively support development on Windows, instead preferring MacOS or Linux.

    However, Microsoft has recently been stabilizing their Windows Subsystem for Linux, which appears to work reasonably well for development.

    Installation instructions are mostly the same as they written in the README with a couple of caveats as of time of writing (2019-07-25), noted below.

    Installing Ubuntu in the Windows Subsystem for Linux.

    For the rest of this document we’re going to presume that you’re using Ubuntu (18.04) in WSL. Medic probably works on all distributions, but Ubuntu is likely the best supported.

    First, follow Microsoft’s instructions on enabling and installing linux. At the end of this process you should have a linux terminal.

    CouchDB

    As of writing CouchDB wouldn’t autostart (due to systemd not existing?), and wasn’t manually starting due to erlang errors.

    Luckily, there is a perfectly working CouchDB installation for Windows:

    • Download from CouchDB and install the Windows version. This will create a Windows service.
    • Run it either by directly executing C:\CouchDB\bin\couchdb.cmd or by starting the service

    Then go to http://localhost:5984/_utils/#/setup in Windows and do the single node setup. Once done head back to linux and confirm it works:

    $: curl http://localhost:5984/
     {"couchdb":"Welcome","version":"2.3.1","git_sha":"c298091a4","uuid":"5f60350abaaa11c0131a5630e83ae979","features":["pluggable-storage-engines","scheduler"],"vendor":{"name":"The Apache Software Foundation"}}
     

    Installing NPM

    Start your WSL instance (Ubuntu), not WSL as they take you to two different default directories.

    The default npm in linux is really old and doesn’t have npm ci, which we need.

    Instead use nvm to install nvm install 11.3 .

    Checking out the code

    We used git that’s preinstalled with Ubuntu to check out the code.

    You can checkout cht code inside WSL itself. You can checkout anywhere you have write access. We’ll checkout inside /home/username/medic directory.

    $: mkdir ~/medic && cd ~/medic
     $: git clone https://github.com/medic/cht-core.git
    @@ -315,7 +315,8 @@
     

    From this point, follow the harden couch section in Core Developer Setup.

    To get multiple linux terminals (so you can run npm run, api and sentinel at the same time) either install and use something like Tmux, or if you click Ubuntu in the Windows start menu again it will open up a new terminal in the same linux instance.

    Once you’re done with the default instructions and have api running, check if it works by going to http://localhost:5988 in Chrome or Firefox.

    Editing Code

    If you want to make changes to your code or contribute to our community health toolkit, you can do so by editing code from your favorite editor. If you editor supports UNC path, you can access and edit files inside WSL from \\wsl$\Ubuntu\<cht-core-location>. If you use Visual Studio Code, it’s even easier to edit your code. Just navigate to where you have checked out cht-core and type code . This will download VS Code Server for Ubuntu and open the project in Visual Studio Code in windows.

    $: cd ~/medic/cht-core
     $: code .
     

    Default port and credentials for cht-core web

    The default launch port for cht-core is 5988, which can be changed by providing the environment variable at runtime API_PORT, for example: API_PORT=6000 node server.js.

    The deployed web app’s default user name and password is the username and password we set for CouchDB in the initial steps.

    Problems?

    As none of our code developers use Windows as a development environment daily this solution may not be as stable as directly using MacOS or Linux. If you encounter issues please let a developer know

    -

    Last modified 21.08.2023: feat: remove grunt (c38caa1d)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/code/design-docs/index.html b/contribute/code/design-docs/index.html index 83c1b9dc05..ecb2cd499e 100644 --- a/contribute/code/design-docs/index.html +++ b/contribute/code/design-docs/index.html @@ -1,9 +1,9 @@ -Design Documents Guide | Community Health Toolkit +Design Documents Guide | Community Health Toolkit

    Design Documents Guide

    Guidelines for writing technical design documents

    What are design docs?

    Software development is not just about writing code, but rather about solving problems and building the right solutions. Before diving into an initiative or feature and starting coding, it’s essential that the developers (and other team members) have a high-level understanding of what a solution might look like.

    Design docs are informal documents that the leading developer of a certain piece of software creates before they start the actual coding of a solution. The design doc contains high-level technical design decisions and alternatives that were considered when making those decisions.

    Why write design docs?

    Besides being an excellent way of documenting software design, design docs come with several additional benefits:

    • Involve the team early and get feedback before implementation, after which changes are more difficult.
    • Identify solution concerns and issues rapidly, as making changes to a solution in the design phase is faster.
    • Provide a fantastic way to document technical decisions, which benefits the team, the community, and future contributors.
    • Ensure the consideration of alternative solutions, assumptions and eventual constraints.
    • Achieve consensus in the team around the design and get everyone on the same page.

    What could a design doc contain?

    The design doc can be added to the GitHub issue related to the feature or initiative to be implemented or in a Google Doc.

    The list below contains a non-exhaustive list of items a design doc could cover.

    Context

    An overview of the context in which the piece of software is being built and what is actually being built. It’s important to keep this section succinct as it’s only meant to bring the readers up to speed with the background facts.

    Goals and scope

    A short list of what the goals of the piece of software are, and, very importantly, what is out of scope. The out of scope items are explicitly chosen not to be goals, as for example “FHIR compliance of the API”. Please note that a solution could cover out of scope items, as long as it doesn’t introduce trade-offs that prevent achieving the goals.

    Proposed solution

    The details of the solution that was chosen for implementation. This flexible-format section can contain how the developer envisions to code the solution, diagrams, sample code, pseudo-code, security considerations, and references to similar solutions or frameworks to be used. It’s important that this section explains why this particular solution best satisfies the goals.

    Alternative solutions considered

    A list of alternative designs that would have achieved similar outcomes, together with the trade-offs that each respective design makes and how those trade-offs led to the decision to select the proposed solution.

    Assumptions

    A description of the assumptions made, for example user interface design or general system characteristics (e.g. operating systems).

    Constraints

    These can include constraints such as security, scalability, or performance.

    Open questions

    Any open issues that you aren’t sure about, or suggested future work.

    This example shows how a design doc could look like.

    When not to write a design doc

    Writing design docs takes time and energy. When deciding to whether write a design doc or not, it’s essential to reflect on the core trade-off of whether the benefits in the alignment around technical design, documentation, senior review, outweigh the extra work of actually creating the doc. If a doc basically says “This is how I am going to implement this feature” without going into trade-offs, alternatives, and explaining decision making (or if the solution is so obvious that there were no trade-offs), then it would probably have been a better idea to write the actual code right away instead of going through the effort of putting together a design doc.

    Often, the overhead of creating and reviewing a design doc may not be compatible with prototyping and fast iteration. If “you tried it out and it worked”, it might mean that you already have a solution that’s worth pursuing without having to write a document. However, it’s important to remember that subscribing to agile methodologies and fast iteration is not an excuse for not taking the time to get solutions to known problems right.

    How to review a design doc?

    When added as a design doc reviewer, there are some details about the problem to solve that should be clear to you after reading the content of the doc, and also some questions you should ask before giving your sign off:

    • What problem is this initiative solving? How will we know it will work?
    • Are there any risky pieces? How are they handled?
    • Are there any non-obvious edge cases?
    • What are the key technical decisions? What are the tradeoffs being made as a result of these decisions?
    • Is there any information you are aware of which the writer may not have known?
    • Have you seen a similar solution (successful or not) used before?
    • What external systems does this initiative interact with?
    • Does it follow current good practices and patterns? Does it fit into the long-term direction? Does it create tech debt?

    More info

    This policy was inspired by Design docs guidelines at Google and How to write a good software design doc.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/code/hall-of-fame/index.html b/contribute/code/hall-of-fame/index.html index a26b2bcb29..592a2c6380 100644 --- a/contribute/code/hall-of-fame/index.html +++ b/contribute/code/hall-of-fame/index.html @@ -1,9 +1,9 @@ -Contributor Hall of Fame | Community Health Toolkit +Contributor Hall of Fame | Community Health Toolkit

    Contributor Hall of Fame

    Tributes to those who have contributed to the codebase

    Code

    Thank you to everyone who has contributed to the CHT codebase over the years! To see the full list, visit each repo on GitHub.

    Security

    Kudos to everyone who has disclosed security vulnerabilities.

    1. Alex Anderson with 4 disclosures
      #9122, #9121, #9120, #9108
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/code/index.html b/contribute/code/index.html index 0665cefa87..70a94f8408 100644 --- a/contribute/code/index.html +++ b/contribute/code/index.html @@ -1,9 +1,9 @@ -Contributing Code | Community Health Toolkit +Contributing Code | Community Health Toolkit

    Contributing Code

    How to contribute to code to the CHT

    The Core Framework of the Community Health Toolkit is powered by people like you. Your contributions help us create open source technology for a new model of healthcare that reaches everyone.

    The Development section of the forum is a great place to introduce yourself and ask questions. Or you can also jump right in:

    The CHT community welcomes first-time contributors and experts alike. All comments, questions, and ideas are welcome!

    Ways to contribute

    First time contributor?

    Issues labeled help wanted are a great place to start.

    Looking for other ways to help? You can also:

    Submitting code

    Note: We recommend you raise an issue on Github or start a conversation on our Community Forum about the change you want to make before you start on code.

    1. Read our Development Workflow to understand how we work, and review our Code Style Guide before you begin.
    2. Setup your development environment
    3. Before you submit a pull request, please make sure your contribution passes all tests. Test failures need to be addressed before we can merge your contribution.
    4. Provide detail about the issue you are solving in the pull request description. Note: If your pull request addresses a specific issue, please reference it using medic/#
    5. Our CI will automatically schedule a build; monitor the build to ensure it passes.
    6. Your PR will be reviewed by one of the repository’s maintainers. Most PRs have at least one change requested before they’re merged so don’t be offended if your change doesn’t get accepted on the first try!

    Working on your first Pull Request? Check out How to Contribute to an Open Source Project on GitHub

    Improving our documentation

    Note: cht-docs does not involve release management and acceptance testing. Help us maintain the quality of our documentation by submitting a pull request (PR) with any suggested changes.

    Is our documentation up to date? Have we covered everything we should? Could our wording be improved? Read our Documentation Style Guide then open a pull request with your suggested changes or additions. + Create project issue

    Contributing Code

    How to contribute to code to the CHT

    The Core Framework of the Community Health Toolkit is powered by people like you. Your contributions help us create open source technology for a new model of healthcare that reaches everyone.

    The Development section of the forum is a great place to introduce yourself and ask questions. Or you can also jump right in:

    The CHT community welcomes first-time contributors and experts alike. All comments, questions, and ideas are welcome!

    Ways to contribute

    First time contributor?

    Issues labeled help wanted are a great place to start.

    Looking for other ways to help? You can also:

    Submitting code

    Note: We recommend you raise an issue on Github or start a conversation on our Community Forum about the change you want to make before you start on code.

    1. Read our Development Workflow to understand how we work, and review our Code Style Guide before you begin.
    2. Setup your development environment
    3. Before you submit a pull request, please make sure your contribution passes all tests. Test failures need to be addressed before we can merge your contribution.
    4. Provide detail about the issue you are solving in the pull request description. Note: If your pull request addresses a specific issue, please reference it using medic/#
    5. Our CI will automatically schedule a build; monitor the build to ensure it passes.
    6. Your PR will be reviewed by one of the repository’s maintainers. Most PRs have at least one change requested before they’re merged so don’t be offended if your change doesn’t get accepted on the first try!

    Working on your first Pull Request? Check out How to Contribute to an Open Source Project on GitHub

    Improving our documentation

    Note: cht-docs does not involve release management and acceptance testing. Help us maintain the quality of our documentation by submitting a pull request (PR) with any suggested changes.

    Is our documentation up to date? Have we covered everything we should? Could our wording be improved? Read our Documentation Style Guide then open a pull request with your suggested changes or additions. Want to talk about Documentation generally? Join our Community Forum!

    Translations

    If you are a translator but not a developer, we understand that you may need extra help to follow the process of translating software for the first time. If that is the case, please open an issue on the GitHub repo or start a topic on the community forum.

    Disclosing vulnerabilities

    We take the security of our systems seriously, and we value the security community. The disclosure of security vulnerabilities helps us ensure the security and privacy of our users.

    Guidelines

    We require that all researchers:

    • Make every effort to avoid privacy violations, degradation of user experience, disruption to production systems, and destruction of data during security testing;
    • Refrain from using any in-scope compromise as a platform to probe or conduct additional research, on any other system, regardless of scope;
    • Perform research only within the scope set out below;
    • Use the identified communication channels to report vulnerability information to us; and
    • Keep information about any vulnerabilities you’ve discovered confidential between yourself and Medic until all production systems have been patched.

    If you follow these guidelines when reporting an issue to us, we commit to:

    • Not pursue or support any legal action related to your research;
    • Work with you to understand and resolve the issue quickly (including an initial confirmation of your report within 72 hours of submission);
    • Recognize your contribution on our Security Researcher Hall of Fame, if you are the first to report the issue and we make a code or configuration change based on the issue.

    Scope

    Out of scope

    Any services hosted by 3rd party providers and any and all other services hosted on or beneath the medicmobile.org and hopephones.org domains are excluded from scope.

    In the interest of the safety of our users, staff, the Internet at large and you as a security researcher, the following test types are excluded from scope:

    • Findings from physical testing such as office access (e.g. open doors, tailgating)
    • Findings derived primarily from social engineering (e.g. phishing, vishing)
    • Findings from applications or systems not listed in the ‘Scope’ section
    • UI and UX bugs and spelling mistakes
    • Network level Denial of Service (DoS/DDoS) vulnerabilities

    Things we do not want to receive:

    • Personally identifiable information (PII)
    • Any exploits or proofs-of-concept in binary format (e.g. ELF)

    How to report a security vulnerability?

    If you believe you’ve found a security vulnerability in one of our products or platforms please send it to us by emailing dev@medic.org. Please include the following details with your report:

    • Description of the location and potential impact of the vulnerability;
    • A detailed description of the steps required to reproduce the vulnerability (proof of concept source code, screenshots, and compressed screen captures are all helpful to us); and
    • Your name/handle and a link for recognition in our Hall of Fame.

    Code of Conduct

    All maintainers and contributors are required to act according to our Code of Conduct. Thank you for your help building a positive community and a safe environment for everyone.

    License

    The software is provided under AGPL-3.0. Contributions to this project are accepted under the same license.


    Development Workflow

    Overview of the development workflow

    Releasing

    Instructions for releasing CHT tools

    CHT Product Repository Checklist

    Checklist to consider when creating CHT Product repositories under Medic’s GitHub organization account

    Coding Style Guide

    Guidelines for writing code

    CHT App Configurer

    CHT Conf is a command-line interface tool to manage and configure apps built using the Core Framework of the Community Health Toolkit.

    Contributing CHT Core Code

    How to contribute to code to the CHT Core Framework

    Using NPM

    Quick guide to using NPM

    Static Analysis

    Guidelines for static analysis of CHT code.

    Design Documents Guide

    Guidelines for writing technical design documents

    Contributing Android Code

    How to contribute to code to the CHT Android app

    Contributor Hall of Fame

    Tributes to those who have contributed to the codebase

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/contribute/code/index.xml b/contribute/code/index.xml index 9f25224f0e..2852f84a3e 100644 --- a/contribute/code/index.xml +++ b/contribute/code/index.xml @@ -1,1264 +1,17 @@ -Community Health Toolkit – Contributing Codehttps://docs.communityhealthtoolkit.org/contribute/code/Recent content in Contributing Code on Community Health ToolkitHugo -- gohugo.ioenContribute: Development Workflowhttps://docs.communityhealthtoolkit.org/contribute/code/workflow/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/workflow/ -<h2 id="code">Code</h2> -<h3 id="writing">Writing</h3> -<p>Where possible, follow our <a href="https://docs.communityhealthtoolkit.org/contribute/code/style-guide/">coding style guide</a>.</p> -<p>Aim for self-documenting code. Where code cannot be made self-documenting add commenting. Usually comments are useful when they explain why some code exists, and should not be explaining what some code is doing.</p> -<h3 id="pushing-code--opening-pull-requests">Pushing Code &amp; Opening Pull Requests</h3> -<p>Never push commits directly to the main branch (<code>main</code> or <code>master</code>). Always use a pull request.</p> -<ul> -<li>If your code is in a regular pull request, it is assumed to be done and only needing a review and testing as checks before merging. It is best to request a reviewer, but otherwise anyone may freely review your PR.</li> -<li>If your code is in a draft PR, it is assumed to be a work-in-progress where collaboration is welcome, but best to communicate about specifics before assuming anything is complete.</li> -<li>If you have pushed code to a remote branch without a pull request, it is assumed to be a work-in-progress where collaboration is unexpected.</li> -</ul> -<p>A good workflow would be to work locally, pushing to a remote branch as you make progress, possibly open a draft PR for some initial collaboration on tricky parts, and once everything is done, convert the draft PR to a regular PR to be reviewed.</p> -<p>Once your pull request has been approved, it can be merged to the main branch by anyone with write access to the repository. When merging a PR, avoid the &ldquo;Create a merge commit&rdquo; option. Merge commits in the main branch cause the history of the branch to be non-linear and make it more difficult to understand exactly when a code change was introduced. Instead, use the &ldquo;Squash and merge&rdquo; option to combine the commits in the PR into a single commit on the main branch. Alternatively, you can use the &ldquo;Rebase and merge&rdquo; option if you want <em>all</em> the commits in the PR to be preserved in the main branch (this should only be used in special cases). See below for instructions on <a href="#commit-message-format">how to format your commit messages</a>.</p> -<h3 id="code-reviews">Code reviews</h3> -<h4 id="guidelines">Guidelines</h4> -<p>The author and reviewer should use this <a href="https://google.github.io/eng-practices/review/developer/">guide to code reviewing</a>.</p> -<h4 id="suggestions">Suggestions</h4> -<p>When doing a code review aim to be extremely clear. This helps things move quickly and avoids lost time in misunderstandings. One especially useful GitHub feature for doing this is suggesting a change. Consider the following example code:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#000">contacts</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">map</span><span style="color:#000;font-weight:bold">(</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">c</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">c</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">id</span> <span style="color:#000;font-weight:bold">});</span> -</span></span></code></pre></div><p>The function body can be abbreviated. In a review you can leave a comment asking for the change, which would likely involve writing up a comment trying to have the author change it to the following:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#000">contacts</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">map</span><span style="color:#000;font-weight:bold">(</span> <span style="color:#000">c</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000">c</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">id</span><span style="color:#000;font-weight:bold">);</span> -</span></span></code></pre></div><p>That means leaving a comment, having the author read and understand it, and then making and pushing up a change, hopefully matching your review expectations.</p> -<p>To be clear and save all that back-and-forth though, you can make a code suggestion directly in your review, which will let the author simply click a button to accept the change (and have it automatically applied as a commit by GitHub).</p> -<p><img src="gh-review-suggestion.png" alt="GitHub review suggest change"></p> -<h4 id="timeliness">Timeliness</h4> -<p>Timely code reviews are important to getting improvements into the hands of users faster and allowing developers to stay focused on the task at hand and see it through to production.</p> -<p>Code reviews should be completed within 24 hours of assignment (excluding weekends and holidays). In some cases, a code review may not be possible if a larger discussion needs to be had for design choices or solution objectives, but even in cases like those, some feedback is still to be expected within 24 hours.</p> -<h3 id="updating-the-issue-with-what-you-actually-did">Updating The Issue With What You Actually Did</h3> -<p>Add <a href="https://github.com/medic/cht-core/labels">labels</a> to the GitHub issue as needed. At this stage, the two to look out for are:</p> -<ul> -<li><code>Breaking change</code></li> -<li><code>UI/UX</code></li> -</ul> -<p>Add a comment to the GitHub issue with what the final change actually was. This is important for multiple cases including:</p> -<ul> -<li>Non-technical people may not understand the conversation thread on the issue. GitHub is a place that developers work, but it is also common to send non-technical people links to issues in GitHub.</li> -<li>The QA team should have a quick way to know where to start testing.</li> -<li>Issues with a lot of discussion of alternative solutions need a clear resolution and indication of which route was taken.</li> -</ul> -<p>Options for doing this:</p> -<ul> -<li>Attach a short video - these are usually very well received and can often help people understand what happened much more clearly than a text description.</li> -<li>Screenshots - pictures with big arrows on them can quickly convey important things to look at. If you start to need multiple screenshots consider the video option instead.</li> -<li>Write up a few sentences - be sure to consider a non-technical audience when writing this.</li> -</ul> -<p>An example of a good thorough comment/template is as follows:</p> -<pre tabindex="0"><code>### Testing -1. Install branch `81-do-great-things` -2. [a specific thing to be sure it has been set up correctly] -3. ... -### What was actually built -[video|screenshots|text] -### Documentation -- [link](url) -</code></pre><h3 id="testing">Testing</h3> -<p>Reach out to the Quality Assurance Engineers with the work to be done as early as possible in the development process to ensure they are informed and can guide development (see more in the <a href="https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/quality-assistance/">Quality Assistance</a> dedicated page).</p> -<p>Before asking for testing support from the QA Engineers, you should test your work after performing it. Correcting a small code error, such as a typo, or adding a missing step in the testing instructions could save QA Engineers hours of work. Also, by testing your code, you may get a better sense of why you make certain common mistakes, and learn to avoid repeating them in the future.</p> -<p>All features and bug fixes must have at least one unit test. All features must have at least one end-to-end test.</p> -<p>The CHT has a <a href="https://github.com/medic/cht-core/tree/master/tests/e2e">fully automated end-to-end testing suite</a> which is executed in CI and must pass before any change is merged. This means you can have reasonable confidence that all code merged to the main branch is safe and ready for release without further regression testing. The suite isn&rsquo;t fully comprehensive but it is being constantly improved and expanded.</p> -<p>From time to time QA Engineers will perform smoke tests, scalability tests, performance tests, and penetration tests to pick up on gradual regressions that may have crept in. The ultimate goal is that these tests will eventually be automated and added to the CI suite as well.</p> -<h3 id="migrating">Migrating</h3> -<p>When the schema is changed you must also provide a migration so when instances are upgraded existing data is compatible with the new code.</p> -<h2 id="commits">Commits</h2> -<p>The main branch is <code>main</code> (or <code>master</code>) which must be kept stable so as not to impact other developers and so a release branch can be created as needed. To achieve this (almost) all development should be done in a branch and submitted via a PR for code review. This means the CI runs and another developer has signed off on the change before it hits the <code>main</code> branch.</p> -<h3 id="commit-message-format">Commit message format</h3> -<p>The commit format should follow this <a href="https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular">conventional-changelog angular preset</a>. This means that some of the release process can be automated. See the list of commit types and examples below:</p> -<table> -<thead> -<tr> -<th>Type</th> -<th>Description</th> -<th>Example commit message</th> -<th>Release type</th> -</tr> -</thead> -<tbody> -<tr> -<td>Bug fixes</td> -<td>Change code that wasn&rsquo;t working as intended.</td> -<td>fix(#123): infinite spinner when clicking contacts tab twice</td> -<td>patch</td> -</tr> -<tr> -<td>Performance</td> -<td>A code change that improves performance. Measure the performance improvement to inform the community.</td> -<td>perf(#789): lazily loaded angular modules</td> -<td>patch</td> -</tr> -<tr> -<td>Features</td> -<td>A new feature or improvement that users will notice.</td> -<td>feat(#456): add home tab</td> -<td>minor</td> -</tr> -<tr> -<td>Non-code</td> -<td>A change that user won&rsquo;t notice, like a change in a README file, adding e2e tests, updating dependencies, removing unused code, etc.</td> -<td>chore(#123): update README</td> -<td>none</td> -</tr> -</tbody> -</table> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -<p>Breaking changes should be explained under the commit type (feat, fix and perf) using the prefix <code>BREAKING CHANGE</code>. -Consider the following example:</p> -<pre tabindex="0"><code> perf(#2): remove reporting rates feature -BREAKING CHANGE: reporting rates no longer supported -</code></pre><p>Any other further information should be provided in the second line of the commit message, respecting 79 character line widths. Using <code>git commit -v</code> is recommended to review your diff while you write your commit message.</p> -</div> -<p>See tips on <a href="https://chris.beams.io/posts/git-commit/">How to Write a Git Commit Message</a> and add your favorites here.</p> -<p>Never force push remote. Prefer rebasing over merging as it makes for a cleaner history.</p> -<p>Commit reformats and refactors separately from actual code changes to make reviewing easier.</p> -<p>Read more about <a href="https://git-scm.com/doc/ext">using git</a>.</p> -<h2 id="branches">Branches</h2> -<ul> -<li>The main branch is <code>main</code> or <code>master</code> and is the github default branch and contains the latest code.</li> -<li>Release branches have the form <code>&lt;major&gt;.&lt;minor&gt;.x</code> and should be stable.</li> -<li>Feature branches have the form <code>&lt;issue-number&gt;-&lt;issue-description&gt;</code> and are work in progress.</li> -</ul> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -When backporting changes to an earlier release branch you should <code>git cherry-pick</code> the appropriate commit(s) from the main branch into the release branch. Then use a pull request to make sure tests pass on CI before merging (you do not need to get the pull request approved if there were no conflicts when cherry-picking). -</div> -<h2 id="issues">Issues</h2> -<p>Issues are managed in Github. Issues should be created in the repository where the changes need to be made. If it is not clear in which repo to open an issue the default should be the <code>cht-core</code> repository. If it is a security or sensitive issue it should be opened in the private <code>medic-projects</code> repository.</p> -<p>When creating issues add the appropriate <a href="https://github.com/medic/medic/labels?utf8=%E2%9C%93&amp;q=Priority%3A+">Priority</a> and <a href="https://github.com/medic/medic/labels?utf8=%E2%9C%93&amp;q=Type%3A+">Type</a> labels.</p> -<h3 id="regressions">Regressions</h3> -<p>When a bug is found that impacts functionality that worked in a previous version, it&rsquo;s important that these are labelled properly so someone who is planning to upgrade can find it. To flag this, add the &ldquo;Regression&rdquo; label, and a labels in the form &ldquo;Affects: {{version}}&rdquo; (e.g.: &ldquo;Affects: 3.14.0&rdquo;) for each version where this bug exists. It&rsquo;s likely that the label for this specific version doesn&rsquo;t exist so you may have to create it. This ensures that issue is listed as a Known Issue in the Release Notes for that version.</p> -<h2 id="project-states">Project States</h2> -<p>When the issue is scheduled for development it will be added to the <a href="https://github.com/orgs/medic/projects/134">Product Team Activities project</a>. Each column in the project represents the state the issue is in.</p> -<h3 id="todo">Todo</h3> -<p>Issues in this column have been prioritised and are ready for development. The issue has all the detail needed to begin development and it is free for anyone to start work on. If you start work on an issue, assign it to yourself and move it to &ldquo;In progress&rdquo;.</p> -<h3 id="in-progress">In progress</h3> -<p>Issues in this column are being actively worked on, which includes development, design, code reviews, and testing.</p> -<p>Any code should be in a branch in each of the repositories you update. The name of the branch should be in the form <code>&lt;issue-number&gt;-&lt;readable-name&gt;</code>, for example <code>1104-inclusive-export</code>. Follow the <a href="https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/quality-assistance/">Quality Assistance</a> process to take full ownership of what you are building.</p> -<p>Use the following template for QA feedback throughout the development.</p> -<ul class="nav nav-tabs" id="tabs-6" role="tablist"> -<li class="nav-item"> -<button class="nav-link active" -id="tabs-06-00-tab" data-bs-toggle="tab" data-bs-target="#tabs-06-00" role="tab" -aria-controls="tabs-06-00" aria-selected="true"> -Test passed -</button> -</li><li class="nav-item"> -<button class="nav-link" -id="tabs-06-01-tab" data-bs-toggle="tab" data-bs-target="#tabs-06-01" role="tab" -aria-controls="tabs-06-01" aria-selected="false"> -Test failed -</button> -</li> -</ul> -<div class="tab-content" id="tabs-6-content"> -<div class="tab-pane fade show active" -id="tabs-06-00" role="tabpanel" aria-labelled-by="tabs-06-00-tab" tabindex="6"> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span><span style="color:#800080;font-weight:bold">### Test details -</span></span></span><span style="display:flex;"><span><span style="color:#800080;font-weight:bold"></span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">**Config:**</span> <span style="color:#000;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">Default</span><span style="color:#a40000">/</span><span style="color:#c4a000">standard</span><span style="color:#000;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">**Environment:**</span> <span style="color:#000;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">Local</span><span style="color:#000;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">**Platform:**</span> <span style="color:#000;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">WebApp</span><span style="color:#000;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">**Browser:**</span> <span style="color:#000;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">Chrome</span><span style="color:#000;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span>--- -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#800080;font-weight:bold">### Test scenario: -</span></span></span><span style="display:flex;"><span><span style="color:#800080;font-weight:bold"></span>Description of the scenario - This is not required for all the tests -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#800080;font-weight:bold">### Reproducible on `master` -</span></span></span><span style="display:flex;"><span><span style="color:#800080;font-weight:bold"></span>A small description of how it was reproduced, and images or videos that support the comment. -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">details</span><span style="color:#000;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">summary</span><span style="color:#000;font-weight:bold">&gt;</span>Image/video attached<span style="color:#000;font-weight:bold">&lt;/</span><span style="color:#204a87;font-weight:bold">summary</span><span style="color:#000;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">&lt;/</span><span style="color:#204a87;font-weight:bold">details</span><span style="color:#000;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#800080;font-weight:bold">### Fixed on `####-branch-name` -</span></span></span><span style="display:flex;"><span><span style="color:#800080;font-weight:bold"></span>A small description, and images or videos that support the comment. -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">details</span><span style="color:#000;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">summary</span><span style="color:#000;font-weight:bold">&gt;</span>Image/video attached<span style="color:#000;font-weight:bold">&lt;/</span><span style="color:#204a87;font-weight:bold">summary</span><span style="color:#000;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">&lt;/</span><span style="color:#204a87;font-weight:bold">details</span><span style="color:#000;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span>--- -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span>Test passed successfully. :white_check_mark: -</span></span><span style="display:flex;"><span>The ticket is ready to merge. -</span></span><span style="display:flex;"><span>@<span style="color:#000;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">developer</span><span style="color:#a40000">&#39;</span><span style="color:#c4a000">s</span> <span style="color:#c4a000">name</span><span style="color:#000;font-weight:bold">&gt;</span></span></span></code></pre></div> -</div> -<div class="tab-pane fade" -id="tabs-06-01" role="tabpanel" aria-labelled-by="tabs-06-01-tab" tabindex="6"> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span><span style="color:#800080;font-weight:bold">### Test details -</span></span></span><span style="display:flex;"><span><span style="color:#800080;font-weight:bold"></span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">**Config:**</span> <span style="color:#000;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">Default</span><span style="color:#a40000">/</span><span style="color:#c4a000">standard</span><span style="color:#000;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">**Environment:**</span> <span style="color:#000;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">Local</span><span style="color:#000;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">**Platform:**</span> <span style="color:#000;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">WebApp</span><span style="color:#000;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">**Browser:**</span> <span style="color:#000;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">Chrome</span><span style="color:#000;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span>--- -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#800080;font-weight:bold">### Test scenario: -</span></span></span><span style="display:flex;"><span><span style="color:#800080;font-weight:bold"></span>Description of the scenario - This is not required for all the tests -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#800080;font-weight:bold">### Reproducible on `master` -</span></span></span><span style="display:flex;"><span><span style="color:#800080;font-weight:bold"></span>A small description of how it was reproduced, and images or videos that support the comment. -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">details</span><span style="color:#000;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">summary</span><span style="color:#000;font-weight:bold">&gt;</span>Image/video attached<span style="color:#000;font-weight:bold">&lt;/</span><span style="color:#204a87;font-weight:bold">summary</span><span style="color:#000;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">&lt;/</span><span style="color:#204a87;font-weight:bold">details</span><span style="color:#000;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#800080;font-weight:bold">### Not working on `####-branch-name` -</span></span></span><span style="display:flex;"><span><span style="color:#800080;font-weight:bold"></span>A small description, and images or videos that support the comment. -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">details</span><span style="color:#000;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">summary</span><span style="color:#000;font-weight:bold">&gt;</span>Image/video attached<span style="color:#000;font-weight:bold">&lt;/</span><span style="color:#204a87;font-weight:bold">summary</span><span style="color:#000;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">&lt;/</span><span style="color:#204a87;font-weight:bold">details</span><span style="color:#000;font-weight:bold">&gt;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span>--- -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span>Test failed :x: -</span></span><span style="display:flex;"><span>The ticket needs further development. -</span></span><span style="display:flex;"><span>@<span style="color:#000;font-weight:bold">&lt;</span><span style="color:#204a87;font-weight:bold">developer</span><span style="color:#a40000">&#39;</span><span style="color:#c4a000">s</span> <span style="color:#c4a000">name</span><span style="color:#000;font-weight:bold">&gt;</span></span></span></code></pre></div> -</div> -</div> -<p>A great way to facilitate discussion and collaboration is with a Draft PR.</p> -<p>Once you&rsquo;re confident that the change is complete and ready to be merged:</p> -<ol> -<li>Submit a PR for each of the repositories. Each PR message and description will become the commit message and description so keep the message concise, describe what and why rather than how, and link to the issue in the description (eg: &ldquo;medic/cht-core#123&rdquo;).</li> -<li>Wait for the builds to succeed and ensure there are no conflicts with the the main branch so the PR can be merged.</li> -<li>Pick one Reviewer for the PR and work with them until the code passes review. In some special cases more than one Reviewer may be necessary, but be specific about additional Reviewers and ensure you really need each of their additional reviews for a good reason. Remember, anyone can collaborate on PRs even if they aren&rsquo;t an official Reviewer. If you add a QA Engineer as a Reviewer, briefly comment in the ticket about what kind of testing review you expect from that engineer.</li> -</ol> -<p>Once all PRs have been approved:</p> -<ol> -<li>Write a useful commit message in the PR using the <a href="#commit-message-format">commit message format</a>.</li> -<li>Click the button to &ldquo;Squash and Merge&rdquo; the PR.</li> -<li>If a backport is required cherry-pick the merged commit back to the release branches it&rsquo;s required in.</li> -<li>Ensure the issue is added to the appropriate release milestone, which is the earliest semver version the change will be released in. This ensures it will be included in the release notes.</li> -<li>Once all PRs have been merged, close the issue. This will automatically move it to &ldquo;Done&rdquo;.</li> -</ol> -<h3 id="done">Done</h3> -<p>Issues in this column are complete, all code has been merged into the main branch and/or release branches, and are ready for release.</p>Contribute: Releasinghttps://docs.communityhealthtoolkit.org/contribute/code/releasing/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/releasing/ -<h2 id="cht-core">CHT Core</h2> -<p>A release is a set of code changes bundled together, ideally with at least one deployment of CHT apps ready to make use of it.</p> -<h3 id="building--releasing-cht-core-changes">Building &amp; Releasing CHT Core Changes</h3> -<p>The high-level steps for a release are as follows:</p> -<ul> -<li>The <a href="https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/focused-groups/">Focused Working Group</a> sees an opportunity they want to go after. The opportunity addresses a need of at least one CHT app deployment and will be used by that deployment after the release.</li> -<li>The Focused Working Group agrees on a solution for it.</li> -<li>Tickets are added to the <a href="https://github.com/orgs/medic/projects/134/views/3">Product Team Activities board</a> for what&rsquo;s being built.</li> -<li>A <a href="#release-manager">release manager</a> is assigned from the team.</li> -<li>The release manager <a href="https://github.com/medic/cht-core/issues/new/choose">creates an issue</a> for either a <a href="#majorminorpatch-release">Major/Minor or Patch</a> release and follows the process outlined in the issue template.</li> -<li>Code is built by a developer together with <a href="https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/quality-assistance/">quality assistance</a>.</li> -<li><a href="https://docs.communityhealthtoolkit.org/contribute/code/workflow/#code-reviews">Code is reviewed</a>.</li> -<li>Code is merged.</li> -<li>Code is released.</li> -</ul> -<h3 id="release-manager">Release Manager</h3> -<p>The overall coordination and operation of the release process are the responsibility of the release manager.</p> -<p>The release manager must perform several tasks for a new release, such as coordinating with team members and following all the steps in the <a href="https://github.com/medic/cht-core/issues/new/choose">release issue process</a>, some of them being manual. The release manager must have adequate permissions to the repositories where the release is made.</p> -<h3 id="majorminorpatch-release">Major/Minor/Patch Release</h3> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -The following classification is defined by the <a href="https://semver.org">Semantic Versioning 2.0.0</a>. -</div> -<p>Given a version number <code>MAJOR.MINOR.PATCH</code>, increment the:</p> -<ul> -<li><code>MAJOR</code> version when the release adds incompatible changes, e.g. when the apps built on top of the CHT require manual intervention to work as expected.</li> -<li><code>MINOR</code> version when the release adds functionality in a backward-compatible manner.</li> -<li><code>PATCH</code> version when the release adds backward-compatible bug fixes.</li> -</ul> -<p><code>MAJOR</code> releases represent the biggest scale of code change and their roll out effort is high, as they likely require time and effort to set up or configure. As a consequence, they are the least frequent of the three release types.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Info</h4> -You can find the versions currently supported, dependencies, and release notes for the CHT Core -<a href="https://docs.communityhealthtoolkit.org/core/releases/">on the Releases page</a>. -</div> -<h2 id="cht-conf">CHT Conf</h2> -<p>Follow the <a href="https://github.com/medic/cht-conf/#user-content-releasing">instructions in the readme</a>.</p> -<h2 id="android-apps">Android apps</h2> -<p>Follow the instructions in the <a href="https://docs.communityhealthtoolkit.org/contribute/code/android/releasing/">Android &gt; Releasing</a> section.</p>Contribute: CHT Product Repository Checklisthttps://docs.communityhealthtoolkit.org/contribute/code/repository-checklist/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/repository-checklist/ -<h2 id="repository-creation-checklist">Repository Creation Checklist</h2> -<p>When creating a new CHT Product repository under <a href="https://github.com/medic">Medic&rsquo;s GitHub organization</a>, the contributor(s) should use the <a href="https://github.com/medic/cht-repo-template">cht-repo-template</a> repository containing the following configurations:</p> -<h3 id="source-control">Source Control</h3> -<ul> -<li><input disabled="" type="checkbox"> The <code>main</code> branch is locked via <a href="https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/managing-a-branch-protection-rule">branch protection rules</a>.</li> -<li><input disabled="" type="checkbox"> Merges are done through PRs.</li> -<li><input disabled="" type="checkbox"> Automatically delete head branches.</li> -<li><input disabled="" type="checkbox"> Issue templates exist.</li> -<li><input disabled="" type="checkbox"> PR template exists.</li> -<li><input disabled="" type="checkbox"> PRs reference related issues.</li> -<li><input disabled="" type="checkbox"> Commit formats follow the <a href="https://docs.communityhealthtoolkit.org/contribute/code/workflow/#commits">guidelines</a>.</li> -<li><input disabled="" type="checkbox"> Secrets are not part of the commit history or made public.</li> -<li><input disabled="" type="checkbox"> The following files exist: -<ul> -<li><code>LICENSE</code> specifying AGPL-3.0 (<a href="https://github.com/medic/cht-core/blob/master/LICENSE">example</a>)</li> -<li><code>README.md</code>.</li> -</ul> -</li> -<li><input disabled="" type="checkbox"> <code>main</code> branch is always shippable.</li> -</ul> -<h3 id="code-reviews">Code Reviews</h3> -<ul> -<li><input disabled="" type="checkbox"> The PR template contains a code review checklist.</li> -<li><input disabled="" type="checkbox"> A reviewer for a PR merge is enforced by policy.</li> -<li><input disabled="" type="checkbox"> A <a href="https://github.com/medic/eslint-config">linter</a> is set up.</li> -</ul> -<p>The PR and issue template content can be adjusted according to the product&rsquo;s purpose.</p> -<p>Additionally, the person who creates the repository might need to share repository access with appropriate teams (this may require admin access).</p> -<h2 id="items-to-consider-when-developing-the-cht-product">Items to consider when developing the CHT Product</h2> -<p>To ensure quality, the CHT Products should also follow the guidelines below:</p> -<h3 id="cicd">CI/CD</h3> -<ul> -<li><input disabled="" type="checkbox"> Repository runs GitHub Actions CI with automated build and test on each PR.</li> -</ul> -<h3 id="testing">Testing</h3> -<ul> -<li><input disabled="" type="checkbox"> Unit tests and successful builds for PR merges are set up.</li> -<li><input disabled="" type="checkbox"> Unit tests cover the majority of the code.</li> -<li><input disabled="" type="checkbox"> If applicable, integration tests run to test the solution e2e.</li> -</ul> -<h3 id="observability">Observability</h3> -<ul> -<li><input disabled="" type="checkbox"> Application faults and errors are logged.</li> -<li><input disabled="" type="checkbox"> Logging configuration can be modified without code changes (eg: verbose mode).</li> -</ul> -<h2 id="medic-github-repository-faq">Medic GitHub repository FAQ</h2> -<h3 id="q-who-can-create-a-repository">Q: Who can create a repository?</h3> -<p>A: Anyone under Medic GitHub organization.</p> -<h3 id="q-is-it-ok-to-create-a-chtmedic-related-work-repository-under-a-personal-github-account">Q: Is it OK to create a CHT/Medic-related work repository under a personal GitHub account?</h3> -<p>A: If what you are working on is temporary and just for you then it is fine to create a repository under your personal account (it is the equivalent of having a script on your local machine), as long as it contains an Open-Source Software License. However, default to the Medic account so the other team members can collaborate on it.</p> -<h3 id="q-when-to-make-a-repo-public-vs-private">Q: When to make a repo public vs private?</h3> -<p>A: Repositories should be public unless there is very good reason to make it private (e.g. the repository contains partner details that cannot be disclosed to public). Always keep in mind that it is much easier to start public than change to public later.</p> -<h3 id="q-when-to-create-a-new-repository-vs-adding-a-directory-in-existing-monolithic">Q: When to create a new repository vs adding a directory in existing (monolithic)?</h3> -<p>A: It depends on the nature of the code. Some things to consider are: is the new code and the old code dependent, don&rsquo;t make sense on their own, must be versioned together, etc. If not, default to a new repo to reduce complexity.</p> -<h2 id="more-info">More info</h2> -<p>This policy was inspired by <a href="https://microsoft.github.io/code-with-engineering-playbook/ENG-FUNDAMENTALS-CHECKLIST/">Microsoft&rsquo;s Engineering Fundamentals Checklist</a>.</p>Contribute: Coding Style Guidehttps://docs.communityhealthtoolkit.org/contribute/code/style-guide/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/style-guide/ -<h2 id="language">Language</h2> -<p>Prefer JavaScript or TypeScript wherever possible, including in webapps, on the server, and for scripting. This is because:</p> -<ul> -<li>every developer on the team knows it already, so we can all maintain it together,</li> -<li>it makes it easy to write cross-platform code and we have developers on all major operating systems,</li> -<li>it has a vast number of libraries that are easy to include, and</li> -<li>it&rsquo;s easy to unit test</li> -</ul> -<p>Exceptions to this can be made on a case-by-case basis, but the decision must be made collectively before coding has begun to avoid having to rewrite.</p> -<h2 id="styles">Styles</h2> -<p>This is a guide, not a law - use your discretion. Mostly based on <a href="https://github.com/felixge/node-style-guide">Felix Geisendörfer&rsquo;s</a> guide with our own tweaks.</p> -<h3 id="indention">Indention</h3> -<p>Use 2 spaces for indenting your code and swear an oath to never mix tabs and -spaces - a special kind of hell is awaiting you otherwise.</p> -<h3 id="newlines">Newlines</h3> -<p>Use UNIX-style newlines (<code>\n</code>), and a newline character as the last character -of a file. Windows-style newlines (<code>\r\n</code>) are forbidden inside any repository.</p> -<h3 id="no-trailing-whitespace">No trailing whitespace</h3> -<p>Just like you brush your teeth after every meal, you clean up any trailing -whitespace in your JS files before committing. Otherwise the rotten smell of -careless neglect will eventually drive away contributors and/or co-workers.</p> -<h3 id="use-semicolons">Use Semicolons</h3> -<p>According to <a href="http://news.ycombinator.com/item?id=1547647">scientific research</a>, the usage of semicolons is -a core value of our community. Consider the points of <a href="http://blog.izs.me/post/2353458699/an-open-letter-to-javascript-leaders-regarding">the opposition</a>, but -be a traditionalist when it comes to abusing error correction mechanisms for -cheap syntactic pleasures.</p> -<h3 id="use-single-quotes">Use single quotes</h3> -<p>Use single quotes, unless you are writing JSON.</p> -<p><em>Right:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">foo</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#4e9a06">&#39;bar&#39;</span><span style="color:#000;font-weight:bold">;</span> -</span></span></code></pre></div><p><em>Wrong:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">foo</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#4e9a06">&#34;bar&#34;</span><span style="color:#000;font-weight:bold">;</span> -</span></span></code></pre></div><h3 id="opening-braces-go-on-the-same-line">Opening braces go on the same line</h3> -<p>Your opening braces go on the same line as the statement, with whitespace before and after the condition, followed by a new line.</p> -<p><em>Right:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">console</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">log</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;winning&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p><em>Wrong:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">)</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">console</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">log</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;losing&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">console</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">log</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;losing&#39;</span><span style="color:#000;font-weight:bold">);</span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">if</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">){</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">console</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">log</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;winning&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="method-chaining">Method chaining</h3> -<p>One method per line should be used if you want to chain methods.</p> -<p>You should also indent these methods so it&rsquo;s easier to tell they are part of the same chain.</p> -<p><em>Right:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#000">User</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">findOne</span><span style="color:#000;font-weight:bold">({</span> <span style="color:#000">name</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;foo&#39;</span> <span style="color:#000;font-weight:bold">})</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">populate</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;bar&#39;</span><span style="color:#000;font-weight:bold">)</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">exec</span><span style="color:#000;font-weight:bold">(()</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">);</span> -</span></span></code></pre></div><p><em>Wrong:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#000">User</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">findOne</span><span style="color:#000;font-weight:bold">({</span> <span style="color:#000">name</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;foo&#39;</span> <span style="color:#000;font-weight:bold">})</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">populate</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;bar&#39;</span><span style="color:#000;font-weight:bold">)</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">exec</span><span style="color:#000;font-weight:bold">(()</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#000">User</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">findOne</span><span style="color:#000;font-weight:bold">({</span> <span style="color:#000">name</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;foo&#39;</span> <span style="color:#000;font-weight:bold">})</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">populate</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;bar&#39;</span><span style="color:#000;font-weight:bold">)</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">exec</span><span style="color:#000;font-weight:bold">(()</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#000">User</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">findOne</span><span style="color:#000;font-weight:bold">({</span> <span style="color:#000">name</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;foo&#39;</span> <span style="color:#000;font-weight:bold">}).</span><span style="color:#000">populate</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;bar&#39;</span><span style="color:#000;font-weight:bold">)</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">exec</span><span style="color:#000;font-weight:bold">(()</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#000">User</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">findOne</span><span style="color:#000;font-weight:bold">({</span> <span style="color:#000">name</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;foo&#39;</span> <span style="color:#000;font-weight:bold">}).</span><span style="color:#000">populate</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;bar&#39;</span><span style="color:#000;font-weight:bold">)</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">exec</span><span style="color:#000;font-weight:bold">(()</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">);</span> -</span></span></code></pre></div><h3 id="use-lowercamelcase-for-variables-properties-and-function-names">Use lowerCamelCase for variables, properties, and function names</h3> -<p>Variables, properties and function names should use <code>lowerCamelCase</code>. They -should also be descriptive. Single character variables and uncommon -abbreviations should generally be avoided.</p> -<p><em>Right:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">adminUser</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">db</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">query</span><span style="color:#000;font-weight:bold">();</span> -</span></span></code></pre></div><p><em>Wrong:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">admin_user</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">db</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">query</span><span style="color:#000;font-weight:bold">();</span> -</span></span></code></pre></div><h3 id="use-uppercamelcase-for-class-names">Use UpperCamelCase for class names</h3> -<p>Class names should be capitalized using <code>UpperCamelCase</code>.</p> -<p><em>Right:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000">BankAccount</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p><em>Wrong:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000">bank_Account</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="use-snake_case-for-couchdb-document-property-names">Use snake_case for CouchDB document property names</h3> -<p>All property names in CouchDB documents use lowercase underscore-separated formatting.</p> -<p><em>Right:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;word&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;values can have spaces and CAPS&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;multiple_words&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p><em>Wrong:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;UPPER_CASE_NAME&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;lowercasename&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;camelCaseName&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;kebab-case-name&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;Title_case_name&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;sTuDlYcAsEnAmE&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="use-const-and-let">Use <code>const</code> and <code>let</code></h3> -<p>There is no longer a good reason to use <code>var</code>. Use <code>const</code> whenever you can, -and <code>let</code> when you must. Hardcoded constants should be named in all UPPERCASE.</p> -<p><em>Right:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">DELAY</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">10</span> <span style="color:#ce5c00;font-weight:bold">*</span> <span style="color:#0000cf;font-weight:bold">1000</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">output</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">input</span> <span style="color:#ce5c00;font-weight:bold">*</span> <span style="color:#0000cf;font-weight:bold">10</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">let</span> <span style="color:#000">temp</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">50</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">let</span> <span style="color:#000">unknown</span><span style="color:#000;font-weight:bold">;</span> -</span></span></code></pre></div><p><em>Wrong:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">var</span> <span style="color:#000">DELAY</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">10</span> <span style="color:#ce5c00;font-weight:bold">*</span> <span style="color:#0000cf;font-weight:bold">1000</span><span style="color:#000;font-weight:bold">;</span> -</span></span></code></pre></div><h3 id="use-arrow-functions">Use arrow functions</h3> -<p>Use arrow functions as much as possible for cleaner code and better scoping. Omit the -return keyword when the entire function definition fits on one line. Omit the parens -when taking a single parameter.</p> -<p>There are exceptions to this rule including when you want to access <code>arguments</code> or -<code>this</code>, or when you want to be able to debug browserified code.</p> -<p><em>Right:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">let</span> <span style="color:#000">result</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#4e9a06">&#39;&#39;</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">append</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">a</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">result</span> <span style="color:#ce5c00;font-weight:bold">+=</span> <span style="color:#000">a</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">};</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">combine</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">a</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">b</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">result</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">a</span> <span style="color:#ce5c00;font-weight:bold">+</span> <span style="color:#000">b</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">};</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">getResult</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000">result</span><span style="color:#000;font-weight:bold">;</span> -</span></span></code></pre></div><p><em>Wrong:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">let</span> <span style="color:#000">result</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#4e9a06">&#39;&#39;</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">append</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">a</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">result</span> <span style="color:#ce5c00;font-weight:bold">+=</span> <span style="color:#000">a</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">};</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">combine</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">function</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">a</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">b</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">result</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">a</span> <span style="color:#ce5c00;font-weight:bold">+</span> <span style="color:#000">b</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">};</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">getResult</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">=&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">result</span><span style="color:#000;font-weight:bold">;</span> -</span></span></code></pre></div><h3 id="implicit-returns">Implicit Returns</h3> -<p>As <a href="#use-arrow-functions">noted above</a>, implicit returns should be used for one-line arrow functions. However, for the sake of readability, they should not be used when returning a multi-line value.</p> -<p><em>Right:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">oneLineString</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#4e9a06">&#39;World&#39;</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">oneLineObject</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">({</span> <span style="color:#000">hello</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;World&#39;</span> <span style="color:#000;font-weight:bold">});</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">// Not using implicit return for multi-line value -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">multiLineObject</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">hello</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;World&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">foo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;bar&#39;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">};</span> -</span></span></code></pre></div><p><em>Wrong:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">multiLineString</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#4e9a06">&#39;This is a really long string that is &#39;</span> <span style="color:#ce5c00;font-weight:bold">+</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#39;on multiple lines&#39;</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">differentLineString</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">=&gt;</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#39;This is a string that does not fit on the same line as the arrow&#39;</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">multiLineObject</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">({</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">hello</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;World&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">foo</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;bar&#39;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">});</span> -</span></span></code></pre></div><h3 id="object--array-creation">Object / Array creation</h3> -<p>Put short declarations on a single line. For long declarations put a line -break after each comma.</p> -<p><em>Right:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">a</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;hello&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;world&#39;</span><span style="color:#000;font-weight:bold">];</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">b</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">good</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;code&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#39;is generally&#39;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;pretty&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">};</span> -</span></span></code></pre></div><p><em>Wrong:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">a</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#39;hello&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;world&#39;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">];</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">b</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">{</span><span style="color:#4e9a06">&#34;good&#34;</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;code&#39;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">,</span> <span style="color:#000">is</span> <span style="color:#000">generally</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;pretty&#39;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">};</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">c</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;one&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;two&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#39;three&#39;</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#4e9a06">&#39;four&#39;</span><span style="color:#000;font-weight:bold">];</span> -</span></span></code></pre></div><h3 id="use-the--operator">Use the === operator</h3> -<p>Programming is not about remembering <a href="https://developer.mozilla.org/en/JavaScript/Reference/Operators/Comparison_Operators">stupid rules</a>. Use -the triple equality operator as it will work just as expected.</p> -<p><em>Right:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">a</span> <span style="color:#ce5c00;font-weight:bold">!==</span> <span style="color:#4e9a06">&#39;&#39;</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">console</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">log</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;winning&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p><em>Wrong:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">a</span> <span style="color:#ce5c00;font-weight:bold">==</span> <span style="color:#4e9a06">&#39;&#39;</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">console</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">log</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;losing&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="do-not-extend-built-in-prototypes">Do not extend built-in prototypes</h3> -<p>Do not extend the prototype of native JavaScript objects. Your future self will be forever grateful.</p> -<p><em>Right:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">a</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">[];</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#000">a</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">length</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">console</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">log</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;winning&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p><em>Wrong:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87">Array</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">prototype</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">empty</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">function</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#204a87;font-weight:bold">this</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">length</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">a</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">[];</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">a</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">empty</span><span style="color:#000;font-weight:bold">())</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">console</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">log</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;losing&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="use-descriptive-conditions">Use descriptive conditions</h3> -<p>Any non-trivial conditions should be assigned to a descriptively named variable or function:</p> -<p><em>Right:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">isValidPassword</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">password</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">length</span> <span style="color:#ce5c00;font-weight:bold">&gt;=</span> <span style="color:#0000cf;font-weight:bold">4</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#4e9a06">/^(?=.*\d).{4,}$/</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">test</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">password</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">isValidPassword</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">console</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">log</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;winning&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p><em>Wrong:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">password</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">length</span> <span style="color:#ce5c00;font-weight:bold">&gt;=</span> <span style="color:#0000cf;font-weight:bold">4</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#4e9a06">/^(?=.*\d).{4,}$/</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">test</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">password</span><span style="color:#000;font-weight:bold">))</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">console</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">log</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">&#39;losing&#39;</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="write-small-functions">Write small functions</h3> -<p>Keep your functions short. A good function fits on a slide that the people in -the last row of a big room can comfortably read. So don&rsquo;t count on them having -perfect vision and limit yourself to ~15 lines of code per function.</p> -<h3 id="return-early-from-functions">Return early from functions</h3> -<p>To avoid deep nesting of if-statements, always return a function&rsquo;s value as early -as possible.</p> -<p><em>Right:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000">isPercentage</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">val</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">val</span> <span style="color:#ce5c00;font-weight:bold">&lt;</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">val</span> <span style="color:#ce5c00;font-weight:bold">&gt;</span> <span style="color:#0000cf;font-weight:bold">100</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p><em>Wrong:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000">isPercentage</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">val</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">val</span> <span style="color:#ce5c00;font-weight:bold">&gt;=</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">val</span> <span style="color:#ce5c00;font-weight:bold">&lt;</span> <span style="color:#0000cf;font-weight:bold">100</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#204a87;font-weight:bold">else</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#204a87;font-weight:bold">else</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>Or for this particular example it may also be fine to shorten things even -further:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000">isPercentage</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">val</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">var</span> <span style="color:#000">isInRange</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">val</span> <span style="color:#ce5c00;font-weight:bold">&gt;=</span> <span style="color:#0000cf;font-weight:bold">0</span> <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#000">val</span> <span style="color:#ce5c00;font-weight:bold">&lt;=</span> <span style="color:#0000cf;font-weight:bold">100</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">isInRange</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="asynchronous-code">Asynchronous code</h3> -<p>Where possible, use the async/await pattern for asynchronous code as it&rsquo;s generally easy to read as the statements line up down the screen. This may not be possible for legacy browser or Node version support, where promises should be used instead. Occasionally promises are better, for example, for executing multiple async methods in parallel. Avoid callbacks at all costs.</p> -<p><em>Right:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">async</span> <span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000">fetch</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">try</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">response</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#204a87;font-weight:bold">await</span> <span style="color:#000">request</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">get</span><span style="color:#000;font-weight:bold">();</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">response</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">data</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> <span style="color:#204a87;font-weight:bold">catch</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">e</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// handle error -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p><em>Wrong:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000">fetch</span><span style="color:#000;font-weight:bold">()</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">request</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">get</span><span style="color:#000;font-weight:bold">()</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#000">then</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">response</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000">response</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">data</span><span style="color:#000;font-weight:bold">)</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">.</span><span style="color:#204a87;font-weight:bold">catch</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">err</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#8f5902;font-style:italic">/* handle error */</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p><em>Wronger:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000">fetch</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">callback</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">request</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">get</span><span style="color:#000;font-weight:bold">((</span><span style="color:#000">err</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">response</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">err</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#8f5902;font-style:italic">// handle error -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#204a87;font-weight:bold">return</span><span style="color:#000;font-weight:bold">;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">callback</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">null</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">response</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">data</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">});</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="avoid-reduce">Avoid reduce</h3> -<p>Most uses of reduce have more readable alternatives.</p> -<p>When supporting older browsers and node versions where some features aren&rsquo;t available <code>reduce</code> can still be useful but its use should be hidden behind a utility function or polyfill to help readability and make it easier to replace later.</p> -<p><em>Right:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">properties</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">[];</span> -</span></span><span style="display:flex;"><span><span style="color:#000">elements</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">forEach</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">elem</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">properties</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">push</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">elem</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">a</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">elem</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">b</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">});</span> -</span></span></code></pre></div><p><em>Wrong:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">properties</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">elements</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">reduce</span><span style="color:#000;font-weight:bold">((</span><span style="color:#000">properties</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">elem</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">=&gt;</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">properties</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">concat</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">elem</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">a</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">elem</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">b</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">},</span> <span style="color:#000;font-weight:bold">[]);</span> -</span></span></code></pre></div><p>Refer to <a href="https://youtu.be/qaGjS7-qWzg">this YouTube video</a> for more examples.</p> -<h3 id="adding-documentation-comments">Adding documentation comments</h3> -<p>To add documentation comments that will be built using jsdocs, use -<a href="https://jsdoc.app/">jsdoc block tags</a>. For angular code use the -<a href="https://www.npmjs.com/package/angular-jsdoc#tags-available">angular tags</a>, see -<a href="https://www.npmjs.com/package/angular-jsdoc#example">examples</a>.</p> -<p>Try to write comments that explain higher level mechanisms or clarify -difficult segments of your code. Don&rsquo;t use comments to restate trivial -things.</p> -<p><em>Right:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">/** -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"> * &#39;ID_SOMETHING=VALUE&#39; -&gt; [&#39;ID_SOMETHING=VALUE&#39;, &#39;SOMETHING&#39;, &#39;VALUE&#39;] -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"> * @type {boolean} -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"> */</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">matches</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">item</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">match</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">/ID_([^\n]+)=([^\n]+)/</span><span style="color:#000;font-weight:bold">));</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">/** -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"> * Loads a user. This function has a nasty side effect where a failure to increment a -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"> * redis counter used for statistics will cause an exception. This needs -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"> * to be fixed in a later iteration. -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"> * @param {string} id the user id -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"> * @param {function} cb a callback function that applied to the user -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"> */</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000">loadUser</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">id</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">cb</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">...</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p><em>Wrong:</em></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">/** -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"> * Execute a regex -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"> */</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">matches</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">item</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">match</span><span style="color:#000;font-weight:bold">(</span><span style="color:#4e9a06">/ID_([^\n]+)=([^\n]+)/</span><span style="color:#000;font-weight:bold">);</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">/** -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"> * Usage: loadUser(5, function() { ... }) -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"> */</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">function</span> <span style="color:#000">loadUser</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">id</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">cb</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">...</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">/** -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"> * Check if the session is valid -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"> */</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">const</span> <span style="color:#000">isSessionValid</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">session</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">expires</span> <span style="color:#ce5c00;font-weight:bold">&lt;</span> <span style="color:#204a87">Date</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">now</span><span style="color:#000;font-weight:bold">());</span> -</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic">/** If the session is valid */</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">isSessionValid</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">...</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h3 id="objectfreeze-objectpreventextensions-objectseal-with-eval">Object.freeze, Object.preventExtensions, Object.seal, with, eval</h3> -<p>Crazy stuff that you will probably never need. Stay away from it.</p> -<h3 id="getters-and-setters">Getters and setters</h3> -<p>Do not use setters, they cause more problems for people who try to use your -software than they can solve.</p> -<p>Feel free to use getters that are free from <a href="http://en.wikipedia.org/wiki/Side_effect_(computer_science)">side effects</a>, like -providing a length property for a collection class.</p> -<h3 id="npm-dependencies">NPM Dependencies</h3> -<p>When picking version ranges we set an exact minimum version and an upper limit of the next major. This makes it easy to update dependencies without hitting breaking changes. In NPM this is done by using the <code>^</code> character which is the default setting for NPM.</p> -<p>Occasionally it is required to set an exact version to avoid an undeclared breaking change or some other issue, in this case the dependency can be specified exactly.</p> -<p><em>Right:</em></p> -<ul> -<li><code>&quot;^6.5.3&quot;</code> - preferred</li> -<li><code>&quot;6.5.3&quot;</code> - if required</li> -</ul> -<p><em>Wrong:</em></p> -<ul> -<li><code>&quot;&gt;6.5.3&quot;</code> - risks picking up breaking changes in the next major</li> -<li><code>&quot;*&quot;</code> - as above but also doesn&rsquo;t specify a minimum</li> -<li><code>&quot;~6.5.3&quot;</code> - too restrictive on the upper limit</li> -</ul> -<h3 id="github-actions">Github Actions</h3> -<h4 id="managing-secrets">Managing Secrets</h4> -<p>Secrets are encrypted for use for things like passwords or API keys and can be added at the org or repository level. They must be added by an admin.</p> -<p>To access a secret you can use this format in your yml.</p> -<pre tabindex="0"><code>steps: -- name: My first action -env: -GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -FIRST_NAME: Mona -LAST_NAME: Octocat -</code></pre><p>See the full <a href="https://docs.github.com/en/actions/reference/encrypted-secrets">documentation</a> on Github&rsquo;s site.</p> -<h4 id="third-party-actions">Third Party Actions</h4> -<p>Actions allow us to leverage code written by others to do tasks at build time. The concept is similar to NPM and packages.</p> -<p>Follow the github actions best practices for security purposes. The main points in the security best <a href="https://docs.github.com/en/actions/learn-github-actions/security-hardening-for-github-actions#using-third-party-actions">practices documents</a> are</p> -<ol> -<li>Pin actions to a full length commit SHA so any malicious or buggy updates are not silently included</li> -<li>Audit the source code of the action</li> -<li>Pin actions to a tag only if you trust the creator</li> -</ol>Contribute: CHT App Configurerhttps://docs.communityhealthtoolkit.org/contribute/code/cht-conf/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/cht-conf/ -<h2 id="requirements">Requirements</h2> -<ul> -<li>nodejs 18 or later</li> -<li>python 3</li> -<li>Docker(optional)</li> -</ul> -<h2 id="installation">Installation</h2> -<h3 id="operating-system-specific">Operating System Specific</h3> -<ul class="nav nav-tabs" id="tabs-0" role="tablist"> -<li class="nav-item"> -<button class="nav-link active" -id="tabs-00-00-tab" data-bs-toggle="tab" data-bs-target="#tabs-00-00" role="tab" -aria-controls="tabs-00-00" aria-selected="true"> -Linux (Ubuntu) -</button> -</li><li class="nav-item"> -<button class="nav-link" -id="tabs-00-01-tab" data-bs-toggle="tab" data-bs-target="#tabs-00-01" role="tab" -aria-controls="tabs-00-01" aria-selected="false"> -macOS -</button> -</li><li class="nav-item"> -<button class="nav-link" -id="tabs-00-02-tab" data-bs-toggle="tab" data-bs-target="#tabs-00-02" role="tab" -aria-controls="tabs-00-02" aria-selected="false"> -Windows (WSL2) -</button> -</li> -</ul> -<div class="tab-content" id="tabs-0-content"> -<div class="tab-pane fade show active" -id="tabs-00-00" role="tabpanel" aria-labelled-by="tabs-00-00-tab" tabindex="0"> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>npm install -g cht-conf -</span></span><span style="display:flex;"><span>sudo python -m pip install git+https://github.com/medic/pyxform.git@medic-conf-1.17#egg<span style="color:#ce5c00;font-weight:bold">=</span>pyxform-medic</span></span></code></pre></div> -</div> -<div class="tab-pane fade" -id="tabs-00-01" role="tabpanel" aria-labelled-by="tabs-00-01-tab" tabindex="0"> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>npm install -g cht-conf -</span></span><span style="display:flex;"><span>pip install git+https://github.com/medic/pyxform.git@medic-conf-1.17#egg<span style="color:#ce5c00;font-weight:bold">=</span>pyxform-medic</span></span></code></pre></div> -</div> -<div class="tab-pane fade" -id="tabs-00-02" role="tabpanel" aria-labelled-by="tabs-00-02-tab" tabindex="0"> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"># As Administrator:</span> -</span></span><span style="display:flex;"><span>npm install -g cht-conf -</span></span><span style="display:flex;"><span>python -m pip install git+https://github.com/medic/pyxform.git@medic-conf-1.17#egg<span style="color:#ce5c00;font-weight:bold">=</span>pyxform-medic --upgrade</span></span></code></pre></div> -</div> -</div> -<h3 id="using-docker">Using Docker</h3> -<p>CHT Conf can also be run from within a Docker container. This is useful if you are already familiar with Docker and do not wish to configure the various dependencies required for developing CHT apps on your local machine. The necessary dependencies are pre-packaged in the Docker image.</p> -<h4 id="using-the-image">Using the image</h4> -<p>The Docker image can be used as a <a href="https://code.visualstudio.com/docs/devcontainers/containers">VS Code Development Container</a> (easiest) or as a standalone Docker utility.</p> -<p>Install <a href="https://www.docker.com/">Docker</a>. If you are using Windows, you also need to enable the <a href="https://learn.microsoft.com/en-us/windows/wsl/install">Windows Subsystem for Linux (WSL2)</a> to perform the following steps.</p> -<h5 id="vs-code-development-container">VS Code Development Container</h5> -<p>If you want to develop CHT apps with VS Code, you can use the Docker image as a Development Container. This will allow you to use the <code>cht-conf</code> utility and its associated tech stack from within VS Code (without needing to install dependencies like NodeJS on your host system).</p> -<p>Look through <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/#developing-with-vs-code-dev-container">Developing with VS Code Dev Container Documentation</a> to get more information .</p> -<h5 id="standalone-docker-utility">Standalone Docker utility</h5> -<p>If you are not using VS Code, you can use the Docker image as a standalone utility from the command line. Instead of using the <code>cht ...</code> command, you can run <code>docker run -it --rm -v &quot;$PWD&quot;:/workdir medicmobile/cht-app-ide ....</code> This will create an ephemeral container with access to your current directory that will run the given cht command. (Do not include the <code>cht</code> part of the command, just your desired actions/parameters.)</p> -<p>Run the following command inside the project directory to bootstrap your new CHT project:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>docker run -it --rm -v <span style="color:#4e9a06">&#34;</span><span style="color:#000">$PWD</span><span style="color:#4e9a06">&#34;</span>:/workdir medicmobile/cht-app-ide initialise-project-layout -</span></span></code></pre></div><h4 id="note-on-connecting-to-a-local-cht-instance">Note on connecting to a local CHT instance</h4> -<p>When using <code>cht-conf</code> within a Docker container to connect to a CHT instance that is running on your local machine (e.g. a development instance), you cannot use the <code>--local</code> flag or <code>localhost</code> in your <code>--url</code> parameter (since these will be interpreted as &ldquo;local to the container&rdquo;).</p> -<p>It is recommended to run a local CHT instance using the <a href="https://docs.communityhealthtoolkit.org/apps/guides/hosting/4.x/app-developer/">CHT Docker Helper script</a>. You can connect to the resulting <code>...my.local-ip.co</code> URL from the Docker container (or the VS Code terminal). Ensure the port your CHT instance is hosted on is not blocked by your firewall.</p> -<h3 id="bash-completion">Bash completion</h3> -<p>To enable tab completion in bash, add the following to your <code>.bashrc</code>/<code>.bash_profile</code>:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">eval</span> <span style="color:#4e9a06">&#34;</span><span style="color:#204a87;font-weight:bold">$(</span>cht-conf --shell-completion<span style="color:#ce5c00;font-weight:bold">=</span>bash<span style="color:#204a87;font-weight:bold">)</span><span style="color:#4e9a06">&#34;</span> -</span></span></code></pre></div><h3 id="upgrading">Upgrading</h3> -<p>To upgrade to the latest version, run the command below. To view changes made to CHT Conf, view the <a href="https://docs.communityhealthtoolkit.org/apps/guides/updates/preparing-for-4/#cht-conf">CHANGELOG</a>.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>npm update -g cht-conf -</span></span></code></pre></div><h2 id="usage">Usage</h2> -<p><code>cht</code> will upload the configuration <strong>from your current directory</strong>.</p> -<h3 id="specifying-the-server-to-configure">Specifying the server to configure</h3> -<p>If you are using the default actionset, or performing any actions that require a CHT instance to function (e.g. <code>upload-xyz</code> or <code>backup-xyz</code> actions) you must specify the server you&rsquo;d like to function against.</p> -<h4 id="localhost">localhost</h4> -<p>For developers, this is the instance defined in your <code>COUCH_URL</code> environment variable.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>cht --local -</span></span></code></pre></div><h4 id="a-specific-medic-hosted-instance">A specific Medic-hosted instance</h4> -<p>For configuring Medic-hosted instances.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>cht --instance<span style="color:#ce5c00;font-weight:bold">=</span>instance-name.dev -</span></span></code></pre></div><p>Username <code>admin</code> is used. A prompt is shown for entering password. -If a different username is required, add the <code>--user</code> switch:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>--user user-name --instance<span style="color:#ce5c00;font-weight:bold">=</span>instance-name.dev -</span></span></code></pre></div><h4 id="an-arbitrary-url">An arbitrary URL</h4> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>cht --url<span style="color:#ce5c00;font-weight:bold">=</span>https://username:password@example.com:12345 -</span></span></code></pre></div><p><strong>NB</strong> - When specifying the URL with <code>--url</code>, be sure not to specify the CouchDB database name in the URL. The CHT API will find the correct database.</p> -<h4 id="using-a-session-token-for-authentication">Using a session token for authentication</h4> -<p>CHT Conf supports authentication using a session token by adding <code>--session-token</code> parameter:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>cht --url<span style="color:#ce5c00;font-weight:bold">=</span>https://example.com:12345 --session-token<span style="color:#ce5c00;font-weight:bold">=</span>*my_token* -</span></span></code></pre></div><h4 id="into-an-archive-to-be-uploaded-later">Into an archive to be uploaded later</h4> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>cht --archive -</span></span></code></pre></div><p>The resulting archive is consumable by CHT API &gt;v3.7 to create default configurations.</p> -<h3 id="perform-specific-actions">Perform specific action(s)</h3> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>cht &lt;--archive<span style="color:#000;font-weight:bold">|</span>--local<span style="color:#000;font-weight:bold">|</span>--instance<span style="color:#ce5c00;font-weight:bold">=</span>instance-name<span style="color:#000;font-weight:bold">|</span>--url<span style="color:#ce5c00;font-weight:bold">=</span>url&gt; &lt;...action&gt; -</span></span></code></pre></div><p>The list of available actions can be seen via <code>cht --help</code>.</p> -<h3 id="perform-actions-for-specific-forms">Perform actions for specific forms</h3> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>cht &lt;--local<span style="color:#000;font-weight:bold">|</span>--instance<span style="color:#ce5c00;font-weight:bold">=</span>instance-name<span style="color:#000;font-weight:bold">|</span>--url<span style="color:#ce5c00;font-weight:bold">=</span>url&gt; &lt;...action&gt; -- &lt;...form&gt; -</span></span></code></pre></div><h3 id="protecting-against-configuration-overwriting">Protecting against configuration overwriting</h3> -<p><em>Added in v3.2.0</em> -In order to avoid overwriting someone else&rsquo;s configuration cht-conf records the last uploaded configuration snapshot in the <code>.snapshots</code> directory. The <code>remote.json</code> file should be committed to your repository along with the associated configuration change. When uploading future configuration if cht-conf detects the snapshot doesn&rsquo;t match the configuration on the server you will be prompted to overwrite or cancel.</p> -<h2 id="development">Development</h2> -<p>To develop a new command that is part of cht-conf, or improve an existing one. For more information check <a href="https://github.com/medic/cht-conf/blob/main/src/fn/README.md">&ldquo;Actions&rdquo; doc</a>.</p> -<h3 id="testing">Testing</h3> -<h4 id="unit-tests">Unit tests</h4> -<p>Execute <code>npm test</code> to run static analysis checks and the test suite. Requires Docker to run integration tests against a CouchDB instance.</p> -<h4 id="end-to-end-tests">End-to-end tests</h4> -<p>Run <code>npm run test-e2e</code> to run the end-to-end test suite against an actual CHT instance locally. These tests rely on <a href="https://docs.communityhealthtoolkit.org/hosting/4.x/app-developer/#cht-docker-helper-for-4x">CHT Docker Helper</a> to spin up and tear down an instance locally.</p> -<p>The code interfacing with CHT Docker Helper lives in <a href="https://github.com/medic/cht-conf/blob/main/test/e2e/cht-docker-utils.js"><code>test/e2e/cht-docker-utils.js</code></a>. You should rely on the API exposed by this file to orchestrate CHT instances for testing purposes. It is preferable to keep the number of CHT instances orchestrated in E2E tests low as it takes a non-negligible amount of time to spin up an instance and can quickly lead to timeouts.</p> -<h3 id="executing-your-local-branch">Executing your local branch</h3> -<ol> -<li>Clone the project locally</li> -<li>Make changes to cht-conf or checkout a branch for testing</li> -<li>Test changes -<ol> -<li>To test CLI changes locally you can run <code>node &lt;project_dir&gt;/src/bin/index.js</code>. This will run as if you installed via npm.</li> -<li>To test changes that are imported in code run <code>npm install &lt;project_dir&gt;</code> to use the local version of cht-conf.</li> -</ol> -</li> -</ol> -<h3 id="releasing">Releasing</h3> -<ol> -<li>Create a pull request with prep for the new release.</li> -<li>Get the pull request reviewed and approved.</li> -<li>When doing the squash and merge, make sure that your commit message is clear and readable and follows the strict format described in the commit format section below. If the commit message does not comply, automatic release will fail.</li> -<li>In case you are planning to merge the pull request with a merge commit, make sure that every commit in your branch respects the format.</li> -</ol> -<h4 id="commit-format">Commit format</h4> -<p>The commit format should follow this <a href="https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular">conventional-changelog angular preset</a>. Examples are provided below.</p> -<table> -<thead> -<tr> -<th>Type</th> -<th>Example commit message</th> -<th>Release type</th> -</tr> -</thead> -<tbody> -<tr> -<td>Bug fixes</td> -<td>fix(#123): infinite spinner when clicking contacts tab twice</td> -<td>patch</td> -</tr> -<tr> -<td>Performance</td> -<td>perf(#789): lazily loaded angular modules</td> -<td>patch</td> -</tr> -<tr> -<td>Features</td> -<td>feat(#456): add home tab</td> -<td>minor</td> -</tr> -<tr> -<td>Non-code</td> -<td>chore(#123): update README</td> -<td>none</td> -</tr> -<tr> -<td>Breaking</td> -<td>perf(#2): remove reporting rates feature <br/> BREAKING CHANGE: reporting rates no longer supported</td> -<td>major</td> -</tr> -</tbody> -</table> -<h4 id="releasing-betas">Releasing betas</h4> -<ol> -<li>Checkout the default branch, for example <code>main</code></li> -<li>Run <code>npm version --no-git-tag-version &lt;major&gt;.&lt;minor&gt;.&lt;patch&gt;-beta.1</code>. This will only update the versions in <code>package.json</code> and <code>package-lock.json</code>. It will not create a git tag and not create an associated commit.</li> -<li>Run <code>npm publish --tag beta</code>. This will publish your beta tag to npm&rsquo;s beta channel.</li> -</ol> -<p>To install from the beta channel, run <code>npm install cht-conf@beta</code>.</p> -<h3 id="build-status">Build status</h3> -<p>Builds brought to you courtesy of GitHub actions. -<figure class=" center col-4 col-lg-4"><a href="build-status.png"> -<img src="build-status.png"/> </a> -</figure> -</p>Contribute: Contributing CHT Core Codehttps://docs.communityhealthtoolkit.org/contribute/code/core/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/core/Contribute: Using NPMhttps://docs.communityhealthtoolkit.org/contribute/code/using-npm/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/using-npm/ -<h2 id="npm-orgs">npm Orgs</h2> -<p>We use npm Orgs to organize our npm packages. It provides a centralized way -to manage a team&rsquo;s published npm packages and permissions. Here are some -guidelines when using this service.</p> -<p>See npm&rsquo;s <a href="https://docs.npmjs.com/orgs/">Orgs docs</a> for more information.</p> -<p>Our organization is <code>medic</code> or using npm&rsquo;s notation, <code>@medic</code>.</p> -<p>We also created <code>@medicmobile</code> but it&rsquo;s not currently in use, it was created to -reserve the namespace.</p> -<h3 id="adding-a-package">Adding a Package</h3> -<p>When you publish an npm module on npmjs.com, add it to the developers team -under the <code>@medic</code> org.</p> -<p>This can be done using the web interface:</p> -<ul> -<li>Login to npmjs.com then Navigate to <a href="https://www.npmjs.com/org/medic/team/developers">Medic Developer&rsquo;s Team</a> -and add your package there.</li> -</ul> -<p>Or command line:</p> -<ul> -<li>Change your directory to where the package&rsquo;s <code>package.json</code> lives, then run:</li> -</ul> -<pre tabindex="0"><code>npm access grant read-write medic:developers -</code></pre><ul> -<li>Then <code>npm access</code> should show the updated permissions for the team members.</li> -</ul> -<pre tabindex="0"><code>npm access ls-collaborators -{ -&#34;mandric&#34;: &#34;read-write&#34;, -&#34;estellecomment&#34;: &#34;read-write&#34;, -&#34;garethbowen&#34;: &#34;read-write&#34;, -&#34;scdf&#34;: &#34;read-write&#34;, -&#34;alxndrsn&#34;: &#34;read-write&#34;, -} -</code></pre><h3 id="using-an-org-scoped-package">Using an Org Scoped Package</h3> -<p>A scope should be specified when a published package is a fork of an existing -package, but otherwise scope is not needed since there is no conflict with the -registry.</p> -<p>For example if you fork <code>moment</code> and you can&rsquo;t get your changes merged upstream -and need to publish a new package then modify the package name (in -package.json) to specify a organizational scope, like <code>@medic/moment</code> and publish it.</p> -<p>For more info see <a href="https://docs.npmjs.com/creating-and-publishing-an-org-scoped-package">Publishing an Org Scoped Package</a>.</p>Contribute: Static Analysishttps://docs.communityhealthtoolkit.org/contribute/code/static-analysis/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/static-analysis/ -<h2 id="eslint">eslint</h2> -<p>All code must pass an eslint check which runs early in the CI cycle and uses the <a href="https://github.com/medic/eslint-config">standard medic eslint configuration</a>.</p> -<h2 id="sonar">Sonar</h2> -<p><a href="https://www.sonarsource.com/">Sonar</a> static analysis supports development by providing feedback on code quality and security issues. Sonar analysis must pass on all <em>new code</em>.</p> -<p><a href="https://www.sonarsource.com/products/sonarcloud/">SonarCloud</a> can be enabled on any public repo in the <code>medic</code> organization.</p> -<h3 id="workflow">Workflow</h3> -<h4 id="during-development">During development</h4> -<p>While writing code, the <a href="https://www.sonarsource.com/products/sonarlint/">SonarLint</a> plugin can be used to get real-time code analysis in your IDE. This is useful to avoid committing code with Sonar issues in the first place.</p> -<h4 id="pr-analysis">PR Analysis</h4> -<p>SonarCloud is integrated with the CHT GitHub repositories and runs on every pull request. The results are posted as a comment on the pull request. If the Sonar analysis fails the quality check, the pull request will be blocked from merging.</p> -<h5 id="what-should-i-do-if-sonar-finds-an-issue">What should I do if Sonar finds an issue?</h5> -<p>When Sonar flags an issue with the code in your pull request, use this decision tree to determine the proper mitigation:</p> -<ol> -<li>If the issue is a genuine concern that should be addressed: -<ol> -<li>Fix it and push the updated code to your PR. The PR will automatically be unblocked once the Sonar analysis succeeds.</li> -</ol> -</li> -<li>If the issue is a &ldquo;false-positive&rdquo; (i.e. Sonar has flagged some particular code as violating a rule, but it does not make sense to apply the rule in that context): -<ol> -<li>If the rule is one that should not be applied to any CHT code: -<ol> -<li><a href="#removing-a-rule">Remove the rule</a> from the default Quality Profile.</li> -</ol> -</li> -<li>If it does not make sense to apply the rule to this particular code, you can do one of the following: -<ol> -<li>Completely ignore Sonar issues <a href="https://docs.sonarsource.com/sonarqube/latest/user-guide/issues/managing/">on that line of code</a> by adding the <code>// NOSONAR</code> comment to the end of the line.</li> -<li>Completely ignore Sonar issues for <a href="#ignoring-all-rules-for-a-block-of-code">that block of code</a>.</li> -<li>Update the <code>.sonarcloud.properties</code> to <a href="#ignoring-a-specific-rule-for-a-file">ignore <em>that rule</em> for that particular file</a>.</li> -<li>Update the <code>.sonarcloud.properties</code> to <a href="#ignoring-all-rules-for-a-file">ignore <em>all rules</em> for that particular file</a> (useful if the file has been copied from an external dependency).</li> -</ol> -</li> -</ol> -</li> -</ol> -<h3 id="adding-a-new-repo-to-sonarcloud">Adding a new repo to SonarCloud</h3> -<ol> -<li>Add a <code>.sonarcloud.properties</code> file to the repository with your desired <a href="#ignoring-all-rules-for-a-file">repo-level configuration</a>.</li> -<li>In the GitHub UI, navigate to the settings for the <a href="https://github.com/organizations/medic/settings/installations"><code>medic</code> org &gt; Third-party Access &gt; GitHub Apps</a></li> -<li>Find SonarCloud and click the <code>Configure</code> button.</li> -<li>In the Repository access section, select your desired repository from the drop-down and click <code>Save</code>.</li> -<li>You will be automatically redirected to the SonarCloud UI where you can configure the repo-level settings.</li> -<li>Use the <code>+</code> button and choose <code>Analyze new project</code>.</li> -<li>Select the repo from the list and click <code>Set Up</code>.</li> -<li>In the SonarCloud configuration, <a href="https://github.com/medic/cht-sync/pull/68#issuecomment-1935677776">disable summary comments in GitHub PRs</a>.</li> -</ol> -<h4 id="new-code-definition">New Code Definition</h4> -<p>When setting up a new repository in SonarCloud, you will be asked to define what is considered to be &ldquo;new code&rdquo;. This is used to determine which code in the default branch is considered &ldquo;new&rdquo; (affects reporting of issues, etc). The new code definition is not applied to Sonar analysis of a PR. In that case, only the changes in the PR are considered &ldquo;new&rdquo;.</p> -<p>Consult <a href="https://docs.sonarcloud.io/improving/new-code-definition/">the documentation</a> for more details on the options available. For projects that do not use Gradle or Maven for version management, the <code>Number of days</code> option is recommended (since <code>Previous version</code> would require maintaining a version number in the <code>.sonarcloud.properties</code> file).</p> -<p>If you are using the <code>CHT Way</code> quality gate (or a similar zero-tolerance quality gate) it is recommended to set <code>Number of days = 1</code>. With a zero-tolerance quality gate, only issue-free code can be merged to the default branch. So, there is no need to check for issues accumulated over time. Also, having a higher <code>Number of days</code> opens up a greater opportunity for Sonar to introduce a <em>new rule</em> that will fail some code previously added to the default branch (code that is only included in the latest analysis because of the configured <code>Number of days</code>).</p> -<h3 id="sonar-configuration">Sonar Configuration</h3> -<p>Broadly speaking, Sonar configuration is separated into repo-level and org-level configuration.</p> -<h4 id="repo-level-configuration">Repo-level configuration</h4> -<p>Each repository can include a <code>.sonarcloud.properties</code> file in the root directory.</p> -<p>This file must specify the path to the source code in the repository as well as which source files should be considered to be test code. See <a href="https://docs.sonarsource.com/sonarqube/latest/project-administration/analysis-scope/">the documentation</a> for more details.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-properties" data-lang="properties"><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"># Path to sources</span> -</span></span><span style="display:flex;"><span><span style="color:#c4a000">sonar.sources</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">.</span> -</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"># Can have multiple comma-separated entries</span> -</span></span><span style="display:flex;"><span><span style="color:#c4a000">sonar.exclusions</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">**/test*/**/*</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"># Path to tests</span> -</span></span><span style="display:flex;"><span><span style="color:#c4a000">sonar.tests</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">.</span> -</span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"># Can have multiple comma-separated entries</span> -</span></span><span style="display:flex;"><span><span style="color:#c4a000">sonar.test.inclusions</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">**/test*/**/*</span> -</span></span></code></pre></div><p>Additionally, the <code>.sonarcloud.properties</code> file can contain configuration regarding ignoring certain rules.</p> -<h5 id="ignoring-all-rules-for-a-block-of-code">Ignoring all rules for a block of code</h5> -<p>You can ignore all the rules for a block of code by telling Sonar to <a href="https://docs.sonarsource.com/sonarqube/latest/project-administration/analysis-scope/#ignoring-blocks-within-files">ignore the block</a>. First, make sure your <code>.sonarcloud.properties</code> file has the following configuration:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-properties" data-lang="properties"><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span><span style="color:#a40000">```properties</span> -</span></span><span style="display:flex;"><span><span style="color:#c4a000">sonar.issue.ignore.block</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">e1</span> -</span></span><span style="display:flex;"><span><span style="color:#c4a000">sonar.issue.ignore.block.e1.beginBlockRegexp</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">NOSONAR_BEGIN</span> -</span></span><span style="display:flex;"><span><span style="color:#c4a000">sonar.issue.ignore.block.e1.endBlockRegexp</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">NOSONAR_END</span> -</span></span></code></pre></div><p>Then simply put <code>//NOSONAR_BEGIN</code> before the block to ignore and <code>//NOSONAR_END</code> after the block.</p> -<h5 id="ignoring-all-rules-for-a-file">Ignoring all rules for a file</h5> -<p>Use the following properties to completely ignore all rules for one or more files:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-properties" data-lang="properties"><span style="display:flex;"><span><span style="color:#c4a000">sonar.issue.ignore.allfile</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">r1</span> -</span></span><span style="display:flex;"><span><span style="color:#c4a000">sonar.issue.ignore.allfile.r1.fileRegexp</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">**/openrosa2html5form.xsl</span> -</span></span></code></pre></div><h5 id="ignoring-a-specific-rule-for-a-file">Ignoring a specific rule for a file</h5> -<p>Use the following properties to ignore a specific rule for one or more files:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-properties" data-lang="properties"><span style="display:flex;"><span><span style="color:#c4a000">sonar.issue.ignore.multicriteria</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">e1,e2</span> -</span></span><span style="display:flex;"><span><span style="color:#c4a000">sonar.issue.ignore.multicriteria.e1.ruleKey</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">javascript:S6582</span> -</span></span><span style="display:flex;"><span><span style="color:#c4a000">sonar.issue.ignore.multicriteria.e1.resourceKey</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">**/auth.js</span> -</span></span><span style="display:flex;"><span><span style="color:#c4a000">sonar.issue.ignore.multicriteria.e2.ruleKey</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">javascript:S2699</span> -</span></span><span style="display:flex;"><span><span style="color:#c4a000">sonar.issue.ignore.multicriteria.e2.resourceKey</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">**/config.js</span> -</span></span></code></pre></div><h4 id="org-level-configuration">Org-level configuration</h4> -<p>Organization-level configuration must be made by an authorized user in the <a href="https://sonarcloud.io/projects">SonarCloud UI</a>.</p> -<h5 id="quality-gates">Quality Gates</h5> -<p>Quality gates are used to define the criteria that must be met for a Sonar analysis to be considered &ldquo;passing&rdquo;. The <a href="https://docs.sonarcloud.io/improving/quality-gates/#how-quality-gates-are-defined"><code>Sonar way</code> quality gate</a> provides an example of a useful configuration. However, this gate config is not ideal for CHT code. Instead, the default quality gate for the <code>Medic</code> organization is the <code>CHT Way</code>. It has the following <a href="https://docs.sonarsource.com/sonarqube/latest/user-guide/metric-definitions/">metrics</a>:</p> -<table> -<thead> -<tr> -<th>Metric</th> -<th>Operator</th> -<th>Value</th> -</tr> -</thead> -<tbody> -<tr> -<td>Duplicated Lines (%)</td> -<td>is greater than</td> -<td>6.0%</td> -</tr> -<tr> -<td>Issues</td> -<td>is greater than</td> -<td>0</td> -</tr> -<tr> -<td>Reliability Rating</td> -<td>is worse than</td> -<td>A</td> -</tr> -<tr> -<td>Security Hotspots Reviewed</td> -<td>is less than</td> -<td>100%</td> -</tr> -<tr> -<td>Security Rating</td> -<td>is worse than</td> -<td>A</td> -</tr> -</tbody> -</table> -<h5 id="quality-profiles">Quality Profiles</h5> -<p>The quality profiles are the lists of rules that will be applied for the various supported languages. By default, we use the <code>Sonar Way</code> quality profile for each language as it provides sensible defaults and is actively maintained receiving updates with new rules and bug fixes as they are added to Sonar.</p> -<h6 id="modifying-rule-parameters">Modifying rule parameters</h6> -<p>To modify a rule parameter (e.g. change the allowed level of complexity for a function <a href="https://rules.sonarsource.com/javascript/RSPEC-3776/">according to <code>javascript:S3776</code></a>):</p> -<ol> -<li>Open a cht-docs PR to record your rule modification in the list below. This allows us to track the history of rule changes and record for posterity the discussions about them.</li> -<li>If not already using a custom quality profile, use the SonarCloud UI to create one that <em>extends</em> the <code>Sonar Way</code> profile. -<ol> -<li>Make sure to set the new quality profile as the default for that language, if desired.</li> -</ol> -</li> -<li>Open the rule in question in the SonarCloud UI and use the <code>Change</code> button associated with your quality profile to set your custom parameter value for the rule.</li> -</ol> -<h6 id="adding-a-rule">Adding a rule</h6> -<p>To include a new rule in the code analysis, add it to the quality profile:</p> -<ol> -<li>Open a cht-docs PR to record your rule addition in the list below. This allows us to track the history of rule changes and record for posterity the discussions about them.</li> -<li>If not already using a custom quality profile, use the SonarCloud UI to create one that <em>extends</em> the <code>Sonar Way</code> profile. -<ol> -<li>Make sure to set the new quality profile as the default for that language, if desired.</li> -</ol> -</li> -<li>Open the rule in question in the SonarCloud UI and use the <code>Activate</code> button to activate the rule in your quality profile</li> -</ol> -<h6 id="removing-a-rule">Removing a rule</h6> -<p>Removing a rule should only be done as a last resort. It is not possible to remove a rule inherited from the <code>Sonar Way</code> profile while at the same time still <em>extending</em> that profile. So, future updates to the <code>Sonar Way</code> profile will not be applied to your custom profile after a rule has been removed.</p> -<ol> -<li>Open a cht-docs PR to record your rule removal in the list below. This allows us to track the history of rule changes and record for posterity the discussions about them.</li> -<li>If not already using a custom quality profile, use the SonarCloud UI to <em>copy</em> (not extend) the <code>Sonar Way</code> profile into a new profile. -<ol> -<li>Make sure to set the new quality profile as the default for that language, if desired.</li> -</ol> -</li> -<li>Open the rule in question in the SonarCloud UI and use the <code>Activate</code> button to activate the rule in your quality profile</li> -</ol> -<h6 id="custom-cht-quality-profiles">Custom CHT Quality Profiles</h6> -<p>Java:</p> -<ul> -<li><strong>CHT Way</strong> <em>(default)</em> extends <strong>Sonar Way</strong> -<ul> -<li>Modified: -<ul> -<li><a href="https://rules.sonarsource.com/javascript/RSPEC-107/"><code>S107</code></a> - Functions should not have too many parameters -<ul> -<li><code>threshold</code> 7 -&gt; 4</li> -</ul> -</li> -<li><a href="https://rules.sonarsource.com/javascript/RSPEC-3776/"><code>S3776</code></a> - Cognitive Complexity of functions should not be too high -<ul> -<li><code>threshold</code> 15 -&gt; 5</li> -</ul> -</li> -</ul> -</li> -</ul> -</li> -</ul> -<p>JavaScript:</p> -<ul> -<li><strong>CHT Way</strong> <em>(default)</em> extends <strong>Sonar Way</strong> -<ul> -<li>Modified: -<ul> -<li><a href="https://rules.sonarsource.com/javascript/RSPEC-107/"><code>S107</code></a> - Functions should not have too many parameters -<ul> -<li><code>threshold</code> 7 -&gt; 4</li> -</ul> -</li> -<li><a href="https://rules.sonarsource.com/javascript/RSPEC-3776/"><code>S3776</code></a> - Cognitive Complexity of functions should not be too high -<ul> -<li><code>threshold</code> 15 -&gt; 5</li> -</ul> -</li> -</ul> -</li> -<li>Disabled -<ul> -<li><a href="https://rules.sonarsource.com/javascript/RSPEC-2699/"><code>S2699</code></a> - Tests should include assertions -<ul> -<li>Disabled due of rigidity of the rule when detecting <code>expect</code> imports and calls to imported functions that have assertions</li> -</ul> -</li> -</ul> -</li> -</ul> -</li> -</ul> -<p>Python:</p> -<ul> -<li><strong>CHT Way</strong> <em>(default)</em> extends <strong>Sonar Way</strong> -<ul> -<li>Modified: -<ul> -<li><a href="https://rules.sonarsource.com/javascript/RSPEC-107/"><code>S107</code></a> - Functions should not have too many parameters -<ul> -<li><code>threshold</code> 7 -&gt; 4</li> -</ul> -</li> -<li><a href="https://rules.sonarsource.com/javascript/RSPEC-3776/"><code>S3776</code></a> - Cognitive Complexity of functions should not be too high -<ul> -<li><code>threshold</code> 15 -&gt; 5</li> -</ul> -</li> -</ul> -</li> -</ul> -</li> -</ul> -<p>TypeScript:</p> -<ul> -<li><strong>CHT Way</strong> <em>(default)</em> extends <strong>Sonar Way</strong> -<ul> -<li>Modified: -<ul> -<li><a href="https://rules.sonarsource.com/javascript/RSPEC-107/"><code>S107</code></a> - Functions should not have too many parameters -<ul> -<li><code>threshold</code> 7 -&gt; 4</li> -</ul> -</li> -<li><a href="https://rules.sonarsource.com/javascript/RSPEC-3776/"><code>S3776</code></a> - Cognitive Complexity of functions should not be too high -<ul> -<li><code>threshold</code> 15 -&gt; 5</li> -</ul> -</li> -</ul> -</li> -</ul> -</li> -</ul>Contribute: Design Documents Guidehttps://docs.communityhealthtoolkit.org/contribute/code/design-docs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/design-docs/ -<h2 id="what-are-design-docs">What are design docs?</h2> -<p>Software development is not just about writing code, but rather about solving problems and building the right solutions. Before diving into an initiative or feature and starting coding, it’s essential that the developers (and other team members) have a high-level understanding of what a solution might look like.</p> -<p>Design docs are informal documents that the leading developer of a certain piece of software creates before they start the actual coding of a solution. The design doc contains high-level technical design decisions and alternatives that were considered when making those decisions.</p> -<h2 id="why-write-design-docs">Why write design docs?</h2> -<p>Besides being an excellent way of documenting software design, design docs come with several additional benefits:</p> -<ul> -<li>Involve the team early and get feedback before implementation, after which changes are more difficult.</li> -<li>Identify solution concerns and issues rapidly, as making changes to a solution in the design phase is faster.</li> -<li>Provide a fantastic way to document technical decisions, which benefits the team, the community, and future contributors.</li> -<li>Ensure the consideration of alternative solutions, assumptions and eventual constraints.</li> -<li>Achieve consensus in the team around the design and get everyone on the same page.</li> -</ul> -<h2 id="what-could-a-design-doc-contain">What could a design doc contain?</h2> -<p>The design doc can be added to the GitHub issue related to the feature or initiative to be implemented or in a Google Doc.</p> -<p>The list below contains a non-exhaustive list of items a design doc could cover.</p> -<h4 id="context">Context</h4> -<p>An overview of the context in which the piece of software is being built and what is actually being built. It&rsquo;s important to keep this section succinct as it&rsquo;s only meant to bring the readers up to speed with the background facts.</p> -<h4 id="goals-and-scope">Goals and scope</h4> -<p>A short list of what the goals of the piece of software are, and, very importantly, what is out of scope. The out of scope items are explicitly chosen not to be goals, as for example &ldquo;FHIR compliance of the API&rdquo;. Please note that a solution could cover out of scope items, as long as it doesn’t introduce trade-offs that prevent achieving the goals.</p> -<h4 id="proposed-solution">Proposed solution</h4> -<p>The details of the solution that was chosen for implementation. This flexible-format section can contain how the developer envisions to code the solution, diagrams, sample code, pseudo-code, security considerations, and references to similar solutions or frameworks to be used. It&rsquo;s important that this section explains why this particular solution best satisfies the goals.</p> -<h4 id="alternative-solutions-considered">Alternative solutions considered</h4> -<p>A list of alternative designs that would have achieved similar outcomes, together with the trade-offs that each respective design makes and how those trade-offs led to the decision to select the proposed solution.</p> -<h4 id="assumptions">Assumptions</h4> -<p>A description of the assumptions made, for example user interface design or general system characteristics (e.g. operating systems).</p> -<h4 id="constraints">Constraints</h4> -<p>These can include constraints such as security, scalability, or performance.</p> -<h4 id="open-questions">Open questions</h4> -<p>Any open issues that you aren’t sure about, or suggested future work.</p> -<p><a href="https://docs.google.com/document/d/1bR3jygKQvfIK1CkRaplxz4LyXQqgO21MTjy8Jsd6s6c/edit?usp=sharing">This example</a> shows how a design doc could look like.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Remember</h4> -<ul> -<li>Keep it simple and concise, write just enough documentation. Design docs should be sufficiently detailed but succinct enough to actually be read by busy people.</li> -<li>Be clear: Don&rsquo;t use unnecessarily complicated language and simplify whenever you can.</li> -<li>Make important points stand out (for example, in bold letters).</li> -</ul> -</div> -<h2 id="when-not-to-write-a-design-doc">When not to write a design doc</h2> -<p>Writing design docs takes time and energy. When deciding to whether write a design doc or not, it&rsquo;s essential to reflect on the core trade-off of whether the benefits in the alignment around technical design, documentation, senior review, outweigh the extra work of actually creating the doc. If a doc basically says &ldquo;This is how I am going to implement this feature&rdquo; without going into trade-offs, alternatives, and explaining decision making (or if the solution is so obvious that there were no trade-offs), then it would probably have been a better idea to write the actual code right away instead of going through the effort of putting together a design doc.</p> -<p>Often, the overhead of creating and reviewing a design doc may not be compatible with prototyping and fast iteration. If &ldquo;you tried it out and it worked&rdquo;, it might mean that you already have a solution that&rsquo;s worth pursuing without having to write a document. However, it&rsquo;s important to remember that subscribing to agile methodologies and fast iteration is not an excuse for not taking the time to get solutions to known problems right.</p> -<h2 id="how-to-review-a-design-doc">How to review a design doc?</h2> -<p>When added as a design doc reviewer, there are some details about the problem to solve that should be clear to you after reading the content of the doc, and also some questions you should ask before giving your sign off:</p> -<ul> -<li>What problem is this initiative solving? How will we know it will work?</li> -<li>Are there any risky pieces? How are they handled?</li> -<li>Are there any non-obvious edge cases?</li> -<li>What are the key technical decisions? What are the tradeoffs being made as a result of these decisions?</li> -<li>Is there any information you are aware of which the writer may not have known?</li> -<li>Have you seen a similar solution (successful or not) used before?</li> -<li>What external systems does this initiative interact with?</li> -<li>Does it follow current good practices and patterns? Does it fit into the long-term direction? Does it create tech debt?</li> -</ul> -<h2 id="more-info">More info</h2> -<p>This policy was inspired by <a href="https://www.industrialempathy.com/posts/design-docs-at-google/">Design docs guidelines at Google</a> and <a href="https://medium.com/free-code-camp/how-to-write-a-good-software-design-document-66fcf019569c">How to write a good software design doc</a>.</p>Contribute: Contributing Android Codehttps://docs.communityhealthtoolkit.org/contribute/code/android/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/android/Contribute: Contributor Hall of Famehttps://docs.communityhealthtoolkit.org/contribute/code/hall-of-fame/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/hall-of-fame/ -<h4 id="code">Code</h4> -<p>Thank you to everyone who has contributed to the CHT codebase over the years! To see the full list, visit each repo on GitHub.</p> -<ul> -<li><a href="https://github.com/medic/cht-android/graphs/contributors">CHT Android</a></li> -<li><a href="https://github.com/medic/cht-conf/graphs/contributors">CHT Conf</a></li> -<li><a href="https://github.com/medic/cht-core/graphs/contributors">CHT Core</a></li> -<li><a href="https://github.com/medic/cht-docs/graphs/contributors">CHT Docs</a></li> -<li><a href="https://github.com/medic/cht-interoperability/graphs/contributors">CHT Interoperability</a></li> -<li><a href="https://github.com/medic/cht-pipeline/graphs/contributors">CHT Pipeline</a></li> -<li><a href="https://github.com/medic/cht-sync/graphs/contributors">CHT Sync</a></li> -<li><a href="https://github.com/medic/cht-watchdog/graphs/contributors">CHT Watchdog</a></li> -</ul> -<h4 id="security">Security</h4> -<p>Kudos to everyone who has disclosed security vulnerabilities.</p> -<ol> -<li><a href="https://github.com/alxndrsn">Alex Anderson</a> with 4 disclosures<br> -<a href="https://github.com/medic/cht-core/issues/9122">#9122</a>, <a href="https://github.com/medic/cht-core/issues/9121">#9121</a>, <a href="https://github.com/medic/cht-core/issues/9120">#9120</a>, <a href="https://github.com/medic/cht-core/issues/9108">#9108</a></li> -</ol> \ No newline at end of file +Contributing Code on Community Health Toolkithttps://docs.communityhealthtoolkit.org/contribute/code/Recent content in Contributing Code on Community Health ToolkitHugo -- gohugo.ioenDevelopment Workflowhttps://docs.communityhealthtoolkit.org/contribute/code/workflow/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/workflow/Code Writing Where possible, follow our coding style guide. +Aim for self-documenting code. Where code cannot be made self-documenting add commenting. Usually comments are useful when they explain why some code exists, and should not be explaining what some code is doing. +Pushing Code &amp; Opening Pull Requests Never push commits directly to the main branch (main or master). Always use a pull request. +If your code is in a regular pull request, it is assumed to be done and only needing a review and testing as checks before merging.CHT Product Repository Checklisthttps://docs.communityhealthtoolkit.org/contribute/code/repository-checklist/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/repository-checklist/Repository Creation Checklist When creating a new CHT Product repository under Medic&rsquo;s GitHub organization, the contributor(s) should use the cht-repo-template repository containing the following configurations: +Source Control The main branch is locked via branch protection rules. Merges are done through PRs. Automatically delete head branches. Issue templates exist. PR template exists. PRs reference related issues. Commit formats follow the guidelines. Secrets are not part of the commit history or made public.Coding Style Guidehttps://docs.communityhealthtoolkit.org/contribute/code/style-guide/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/style-guide/Language Prefer JavaScript or TypeScript wherever possible, including in webapps, on the server, and for scripting. This is because: +every developer on the team knows it already, so we can all maintain it together, it makes it easy to write cross-platform code and we have developers on all major operating systems, it has a vast number of libraries that are easy to include, and it&rsquo;s easy to unit test Exceptions to this can be made on a case-by-case basis, but the decision must be made collectively before coding has begun to avoid having to rewrite.CHT App Configurerhttps://docs.communityhealthtoolkit.org/contribute/code/cht-conf/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/cht-conf/Requirements nodejs 18 or later python 3 Docker(optional) Installation Operating System Specific Linux (Ubuntu) macOS Windows (WSL2) npm install -g cht-conf sudo python -m pip install git+https://github.com/medic/pyxform.git@medic-conf-1.17#egg=pyxform-medic npm install -g cht-conf pip install git+https://github.com/medic/pyxform.git@medic-conf-1.17#egg=pyxform-medic # As Administrator: npm install -g cht-conf python -m pip install git+https://github.com/medic/pyxform.git@medic-conf-1.17#egg=pyxform-medic --upgrade Using Docker CHT Conf can also be run from within a Docker container. This is useful if you are already familiar with Docker and do not wish to configure the various dependencies required for developing CHT apps on your local machine.Using NPMhttps://docs.communityhealthtoolkit.org/contribute/code/using-npm/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/using-npm/npm Orgs We use npm Orgs to organize our npm packages. It provides a centralized way to manage a team&rsquo;s published npm packages and permissions. Here are some guidelines when using this service. +See npm&rsquo;s Orgs docs for more information. +Our organization is medic or using npm&rsquo;s notation, @medic. +We also created @medicmobile but it&rsquo;s not currently in use, it was created to reserve the namespace. +Adding a Package When you publish an npm module on npmjs.Static Analysishttps://docs.communityhealthtoolkit.org/contribute/code/static-analysis/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/static-analysis/eslint All code must pass an eslint check which runs early in the CI cycle and uses the standard medic eslint configuration. +Sonar Sonar static analysis supports development by providing feedback on code quality and security issues. Sonar analysis must pass on all new code. +SonarCloud can be enabled on any public repo in the medic organization. +Workflow During development While writing code, the SonarLint plugin can be used to get real-time code analysis in your IDE.Design Documents Guidehttps://docs.communityhealthtoolkit.org/contribute/code/design-docs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/design-docs/What are design docs? Software development is not just about writing code, but rather about solving problems and building the right solutions. Before diving into an initiative or feature and starting coding, it’s essential that the developers (and other team members) have a high-level understanding of what a solution might look like. +Design docs are informal documents that the leading developer of a certain piece of software creates before they start the actual coding of a solution.Contributor Hall of Famehttps://docs.communityhealthtoolkit.org/contribute/code/hall-of-fame/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/hall-of-fame/Code Thank you to everyone who has contributed to the CHT codebase over the years! To see the full list, visit each repo on GitHub. +CHT Android CHT Conf CHT Core CHT Docs CHT Interoperability CHT Pipeline CHT Sync CHT Watchdog Security Kudos to everyone who has disclosed security vulnerabilities. +Alex Anderson with 4 disclosures +#9122, #9121, #9120, #9108 \ No newline at end of file diff --git a/contribute/code/releasing/feature_releases/index.html b/contribute/code/releasing/feature_releases/index.html index aed2e5aba0..3755659114 100644 --- a/contribute/code/releasing/feature_releases/index.html +++ b/contribute/code/releasing/feature_releases/index.html @@ -1,9 +1,9 @@ -Feature Releases | Community Health Toolkit +Feature Releases | Community Health Toolkit

    Feature Releases

    Feature Releases for the CHT Core Framework

    To build and iterate on new features at a pace that is faster than our regular release cycle, some features are released in a Feature Release. Feature Releases (FRs) are based on the most recent release and only include improvements related to a feature being developed. These releases are tested to be production-ready so that new features can be studied with CHT partners in a live deployment, with the aim of getting the feature ready for wider use in an upcoming release.

    Release names

    A Feature Release can easily be identified by its version, which follows the pattern of {BASE-RELEASE-NUMBER}-FR-{FEATURE-NAME}. For example, if the latest release is 3.10.1 then the Feature Release for speedier upgrades would be 3.10.1-FR-speedier-upgrades. When this feature is found to be successful and ready for wider distribution it will be included in the next release.

    Initial FR Installation

    3.x

    When you are on a non-feature release, you need to use horticulturalist (horti) to do the initial install of the FR. For example, if your instance was running at 192-168-68-26.local-ip.medicmobile.org:8443 and you wanted to install 3.16.0-FR-offline-user-replace-beta.1, after installing horti you could start the installation with this command:

    COUCH_URL=https://medic:password@192-168-68-26.local-ip.medicmobile.org:8443/medic horti --local --install=3.16.0-FR-offline-user-replace-beta.1
    + Create project issue

    Feature Releases

    Feature Releases for the CHT Core Framework

    To build and iterate on new features at a pace that is faster than our regular release cycle, some features are released in a Feature Release. Feature Releases (FRs) are based on the most recent release and only include improvements related to a feature being developed. These releases are tested to be production-ready so that new features can be studied with CHT partners in a live deployment, with the aim of getting the feature ready for wider use in an upcoming release.

    Release names

    A Feature Release can easily be identified by its version, which follows the pattern of {BASE-RELEASE-NUMBER}-FR-{FEATURE-NAME}. For example, if the latest release is 3.10.1 then the Feature Release for speedier upgrades would be 3.10.1-FR-speedier-upgrades. When this feature is found to be successful and ready for wider distribution it will be included in the next release.

    Initial FR Installation

    3.x

    When you are on a non-feature release, you need to use horticulturalist (horti) to do the initial install of the FR. For example, if your instance was running at 192-168-68-26.local-ip.medicmobile.org:8443 and you wanted to install 3.16.0-FR-offline-user-replace-beta.1, after installing horti you could start the installation with this command:

    COUCH_URL=https://medic:password@192-168-68-26.local-ip.medicmobile.org:8443/medic horti --local --install=3.16.0-FR-offline-user-replace-beta.1
     

    On subsequent upgrades to the later beta’s of the FR, you will be able to more easily do it through the admin UI in the CHT.

    4.x

    As horticulturalist (horti) does not exist in CHT 4.x, the current way to upgrade an instance is to manually edit your compose file to have the correct version tag of the images. Find the CHT Core compose file and edit all 5 instances of the version tag for each image.

    For example, this image is using the label 4.0.1-4.0.1 (1 of 5 instances):

    services:
       haproxy:
         image: public.ecr.aws/s5s3h4s7/cht-haproxy:4.0.1-4.0.1
    @@ -314,7 +314,8 @@
     

    On subsequent upgrades to the later beta’s of the FR, you will be able to more easily do it through the admin UI in the CHT.

    Upgrades to release

    Once the feature is ready for widespread use, it will be included in a regular CHT release. Projects using the feature version can be upgraded as soon as practical to get back on to a fully supported release.


    CHT Core Framework > Releases

    Versions currently supported, dependencies, and release notes for the CHT Core Framework

    Contributor Handbook : Are You a Partner Wondering How Issues Are Prioritized

    How to contribute to the CHT tools and documentation

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/contribute/code/releasing/index.html b/contribute/code/releasing/index.html index 7ce8e4f998..0f28c7b0ef 100644 --- a/contribute/code/releasing/index.html +++ b/contribute/code/releasing/index.html @@ -1,9 +1,9 @@ -Releasing | Community Health Toolkit +Releasing | Community Health Toolkit

    Releasing

    Instructions for releasing CHT tools

    CHT Core

    A release is a set of code changes bundled together, ideally with at least one deployment of CHT apps ready to make use of it.

    Building & Releasing CHT Core Changes

    The high-level steps for a release are as follows:

    Release Manager

    The overall coordination and operation of the release process are the responsibility of the release manager.

    The release manager must perform several tasks for a new release, such as coordinating with team members and following all the steps in the release issue process, some of them being manual. The release manager must have adequate permissions to the repositories where the release is made.

    Major/Minor/Patch Release

    Given a version number MAJOR.MINOR.PATCH, increment the:

    • MAJOR version when the release adds incompatible changes, e.g. when the apps built on top of the CHT require manual intervention to work as expected.
    • MINOR version when the release adds functionality in a backward-compatible manner.
    • PATCH version when the release adds backward-compatible bug fixes.

    MAJOR releases represent the biggest scale of code change and their roll out effort is high, as they likely require time and effort to set up or configure. As a consequence, they are the least frequent of the three release types.

    Releasing

    Instructions for releasing CHT tools

    CHT Core

    A release is a set of code changes bundled together, ideally with at least one deployment of CHT apps ready to make use of it.

    Building & Releasing CHT Core Changes

    The high-level steps for a release are as follows:

    Release Manager

    The overall coordination and operation of the release process are the responsibility of the release manager.

    The release manager must perform several tasks for a new release, such as coordinating with team members and following all the steps in the release issue process, some of them being manual. The release manager must have adequate permissions to the repositories where the release is made.

    Major/Minor/Patch Release

    Given a version number MAJOR.MINOR.PATCH, increment the:

    • MAJOR version when the release adds incompatible changes, e.g. when the apps built on top of the CHT require manual intervention to work as expected.
    • MINOR version when the release adds functionality in a backward-compatible manner.
    • PATCH version when the release adds backward-compatible bug fixes.

    MAJOR releases represent the biggest scale of code change and their roll out effort is high, as they likely require time and effort to set up or configure. As a consequence, they are the least frequent of the three release types.

    CHT Conf

    Follow the instructions in the readme.

    Android apps

    Follow the instructions in the Android > Releasing section.


    Feature Releases

    Feature Releases for the CHT Core Framework

    Publishing Docker Images

    Using GitHub Actions to publish Docker images

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/code/releasing/index.xml b/contribute/code/releasing/index.xml index 2aba019a04..5975cb8435 100644 --- a/contribute/code/releasing/index.xml +++ b/contribute/code/releasing/index.xml @@ -1,76 +1,3 @@ -Community Health Toolkit – Releasinghttps://docs.communityhealthtoolkit.org/contribute/code/releasing/Recent content in Releasing on Community Health ToolkitHugo -- gohugo.ioenContribute: Feature Releaseshttps://docs.communityhealthtoolkit.org/contribute/code/releasing/feature_releases/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/releasing/feature_releases/ -<p>To build and iterate on new features at a pace that is faster than our regular release cycle, some features are released in a <em>Feature Release</em>. Feature Releases (FRs) are based on the most recent release and only include improvements related to a feature being developed. These releases are tested to be production-ready so that new features can be studied with CHT partners in a live deployment, with the aim of getting the feature ready for wider use in an upcoming release.</p> -<h2 id="release-names">Release names</h2> -<p>A Feature Release can easily be identified by its version, which follows the pattern of <code>{BASE-RELEASE-NUMBER}-FR-{FEATURE-NAME}</code>. For example, if the latest release is <code>3.10.1</code> then the Feature Release for speedier upgrades would be <code>3.10.1-FR-speedier-upgrades</code>. When this feature is found to be successful and ready for wider distribution it will be included in the next release.</p> -<h2 id="initial-fr-installation">Initial FR Installation</h2> -<h3 id="3x">3.x</h3> -<p>When you are on a non-feature release, you need to use horticulturalist (horti) to do the initial install of the FR. For example, if your instance was running at <code>192-168-68-26.local-ip.medicmobile.org:8443</code> and you wanted to install <code>3.16.0-FR-offline-user-replace-beta.1</code>, after <a href="https://github.com/medic/horticulturalist#usage">installing</a> <code>horti</code> you could start the installation with this command:</p> -<pre tabindex="0"><code>COUCH_URL=https://medic:password@192-168-68-26.local-ip.medicmobile.org:8443/medic horti --local --install=3.16.0-FR-offline-user-replace-beta.1 -</code></pre><p>On subsequent upgrades to the later beta&rsquo;s of the FR, you will be able to more easily do it through the admin UI in the CHT.</p> -<h3 id="4x">4.x</h3> -<p>As horticulturalist (horti) does not exist in CHT 4.x, the current way to upgrade an instance is to manually edit your compose file to have the correct version tag of the images. Find the CHT Core compose file and edit all 5 instances of the version tag for each image.</p> -<p>For example, this <code>image</code> is using the label <code>4.0.1-4.0.1</code> (1 of 5 instances):</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">services</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">haproxy</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">image</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">public.ecr.aws/s5s3h4s7/cht-haproxy:4.0.1-4.0.1</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">restart</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">always</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">hostname</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">haproxy</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span></code></pre></div><p>If you wanted to install <code>4.1.0-FR-supervisor-cwh-add-beta.1</code>, you would make it look like this:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">services</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">haproxy</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">image</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">public.ecr.aws/s5s3h4s7/cht-haproxy:4.1.0-FR-supervisor-cwh-add-beta.1</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">restart</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">always</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">hostname</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">haproxy</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span></code></pre></div><p>On subsequent upgrades to the later beta&rsquo;s of the FR, you will be able to more easily do it through the admin UI in the CHT.</p> -<h2 id="upgrades-to-release">Upgrades to release</h2> -<p>Once the feature is ready for widespread use, it will be included in a regular <a href="https://docs.communityhealthtoolkit.org/contribute/code/releasing/">CHT release</a>. Projects using the feature version can be upgraded as soon as practical to get back on to a fully supported release.</p>Contribute: Publishing Docker Imageshttps://docs.communityhealthtoolkit.org/contribute/code/releasing/publish-docker-image/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/releasing/publish-docker-image/ -<p>Docker images for CHT projects can be published to the <a href="https://hub.docker.com/u/medicmobile">medicmobile</a> Docker Hub organization, so they are easily accessible to the community. This process can be automated using GitHub actions.</p> -<h2 id="create-repository-on-docker-hub">Create repository on Docker Hub</h2> -<p>First, create a repository for your new image on Docker Hub.</p> -<ol> -<li>Use the admin Docker account to <a href="https://hub.docker.com/orgs/medicmobile/repositories">create a new repository</a> in the <code>medicmobile</code> organization.</li> -<li>For your new repository, update the permissions to give the <code>developers</code> team the ability to <code>Read &amp; Write</code>. This will allow the GitHub action to push to the repository.</li> -</ol> -<h2 id="add-github-action-workflow-configuration">Add GitHub Action Workflow configuration</h2> -<p>Now that the Docker Hub repository is created, you can add the GitHub Action workflow configuration to your project. This configuration should build the Docker image based on the code in the repository and then publish it to the Docker Hub repository.</p> -<p>Here is an example of a basic workflow configuration that publishes a new image for each tag that is pushed:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">Publish Docker image</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">on</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">push</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">tags</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;v*&#39;</span><span style="color:#000;font-weight:bold">]</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">env</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">DOCKER_HUB_USER</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">dockermedic</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">DOCKER_NAMESPACE</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">medicmobile</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">DOCKER_REPOSITORY</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#8f5902;font-style:italic">**REPLACE</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">WITH REPOSITORY NAME**</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">jobs</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">push_to_registry</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">Push Docker image to Docker Hub</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">runs-on</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">ubuntu-22.04</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">steps</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#204a87;font-weight:bold">name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">Check out the repo</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">uses</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">actions/checkout@v3</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#204a87;font-weight:bold">name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">Log in to Docker Hub</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">uses</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">docker/login-action@v2</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">with</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">username</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">${{ env.DOCKER_HUB_USER }}</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">password</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">${{ secrets.DOCKER_HUB_PASS }}</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#204a87;font-weight:bold">name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">Extract metadata (tags, labels) for Docker</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">id</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">meta</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">uses</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">docker/metadata-action@v4</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">with</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">images</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">${{ env.DOCKER_NAMESPACE }}/${{ env.DOCKER_REPOSITORY }}</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#204a87;font-weight:bold">name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">Build and push Docker image</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">uses</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">docker/build-push-action@v4</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">with</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">context</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">.</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">push</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">true</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">tags</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">${{ steps.meta.outputs.tags }}</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">labels</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">${{ steps.meta.outputs.labels }}</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span></code></pre></div><p>Do not forget to update the <code>DOCKER_REPOSITORY</code> environment variable with the name of the repository you created on Docker Hub. Also note that the <code>DOCKER_HUB_PASS</code> secret is an org level secret. So, no special configuration should be required to use this secret for any repositories in the <a href="https://github.com/medic"><code>medic</code> organization</a>.</p> -<p>This configuration will create and publish a new Docker image for each tag that is pushed to the repository. The image will be tagged with the tag name and the <code>latest</code> tag.</p> \ No newline at end of file +Releasing on Community Health Toolkithttps://docs.communityhealthtoolkit.org/contribute/code/releasing/Recent content in Releasing on Community Health ToolkitHugo -- gohugo.ioenFeature Releaseshttps://docs.communityhealthtoolkit.org/contribute/code/releasing/feature_releases/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/releasing/feature_releases/To build and iterate on new features at a pace that is faster than our regular release cycle, some features are released in a Feature Release. Feature Releases (FRs) are based on the most recent release and only include improvements related to a feature being developed. These releases are tested to be production-ready so that new features can be studied with CHT partners in a live deployment, with the aim of getting the feature ready for wider use in an upcoming release.Publishing Docker Imageshttps://docs.communityhealthtoolkit.org/contribute/code/releasing/publish-docker-image/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/releasing/publish-docker-image/Docker images for CHT projects can be published to the medicmobile Docker Hub organization, so they are easily accessible to the community. This process can be automated using GitHub actions. +Create repository on Docker Hub First, create a repository for your new image on Docker Hub. +Use the admin Docker account to create a new repository in the medicmobile organization. For your new repository, update the permissions to give the developers team the ability to Read &amp; Write. \ No newline at end of file diff --git a/contribute/code/releasing/publish-docker-image/index.html b/contribute/code/releasing/publish-docker-image/index.html index e10d53cf39..cbf839935a 100644 --- a/contribute/code/releasing/publish-docker-image/index.html +++ b/contribute/code/releasing/publish-docker-image/index.html @@ -1,9 +1,9 @@ -Publishing Docker Images | Community Health Toolkit +Publishing Docker Images | Community Health Toolkit

    Publishing Docker Images

    Using GitHub Actions to publish Docker images

    Docker images for CHT projects can be published to the medicmobile Docker Hub organization, so they are easily accessible to the community. This process can be automated using GitHub actions.

    Create repository on Docker Hub

    First, create a repository for your new image on Docker Hub.

    1. Use the admin Docker account to create a new repository in the medicmobile organization.
    2. For your new repository, update the permissions to give the developers team the ability to Read & Write. This will allow the GitHub action to push to the repository.

    Add GitHub Action Workflow configuration

    Now that the Docker Hub repository is created, you can add the GitHub Action workflow configuration to your project. This configuration should build the Docker image based on the code in the repository and then publish it to the Docker Hub repository.

    Here is an example of a basic workflow configuration that publishes a new image for each tag that is pushed:

    name: Publish Docker image
    + Create project issue

    Publishing Docker Images

    Using GitHub Actions to publish Docker images

    Docker images for CHT projects can be published to the medicmobile Docker Hub organization, so they are easily accessible to the community. This process can be automated using GitHub actions.

    Create repository on Docker Hub

    First, create a repository for your new image on Docker Hub.

    1. Use the admin Docker account to create a new repository in the medicmobile organization.
    2. For your new repository, update the permissions to give the developers team the ability to Read & Write. This will allow the GitHub action to push to the repository.

    Add GitHub Action Workflow configuration

    Now that the Docker Hub repository is created, you can add the GitHub Action workflow configuration to your project. This configuration should build the Docker image based on the code in the repository and then publish it to the Docker Hub repository.

    Here is an example of a basic workflow configuration that publishes a new image for each tag that is pushed:

    name: Publish Docker image
     
     on:
       push:
    @@ -340,7 +340,8 @@
               labels: ${{ steps.meta.outputs.labels }}
     

    Do not forget to update the DOCKER_REPOSITORY environment variable with the name of the repository you created on Docker Hub. Also note that the DOCKER_HUB_PASS secret is an org level secret. So, no special configuration should be required to use this secret for any repositories in the medic organization.

    This configuration will create and publish a new Docker image for each tag that is pushed to the repository. The image will be tagged with the tag name and the latest tag.


    CHT Core Framework > Releases

    Versions currently supported, dependencies, and release notes for the CHT Core Framework

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/code/repository-checklist/index.html b/contribute/code/repository-checklist/index.html index 9e6a24d4e2..34e84ce9a8 100644 --- a/contribute/code/repository-checklist/index.html +++ b/contribute/code/repository-checklist/index.html @@ -1,9 +1,9 @@ -CHT Product Repository Checklist | Community Health Toolkit +CHT Product Repository Checklist | Community Health Toolkit

    CHT Product Repository Checklist

    Checklist to consider when creating CHT Product repositories under Medic’s GitHub organization account

    Repository Creation Checklist

    When creating a new CHT Product repository under Medic’s GitHub organization, the contributor(s) should use the cht-repo-template repository containing the following configurations:

    Source Control

    • The main branch is locked via branch protection rules.
    • Merges are done through PRs.
    • Automatically delete head branches.
    • Issue templates exist.
    • PR template exists.
    • PRs reference related issues.
    • Commit formats follow the guidelines.
    • Secrets are not part of the commit history or made public.
    • The following files exist:
      • LICENSE specifying AGPL-3.0 (example)
      • README.md.
    • main branch is always shippable.

    Code Reviews

    • The PR template contains a code review checklist.
    • A reviewer for a PR merge is enforced by policy.
    • A linter is set up.

    The PR and issue template content can be adjusted according to the product’s purpose.

    Additionally, the person who creates the repository might need to share repository access with appropriate teams (this may require admin access).

    Items to consider when developing the CHT Product

    To ensure quality, the CHT Products should also follow the guidelines below:

    CI/CD

    • Repository runs GitHub Actions CI with automated build and test on each PR.

    Testing

    • Unit tests and successful builds for PR merges are set up.
    • Unit tests cover the majority of the code.
    • If applicable, integration tests run to test the solution e2e.

    Observability

    • Application faults and errors are logged.
    • Logging configuration can be modified without code changes (eg: verbose mode).

    Medic GitHub repository FAQ

    Q: Who can create a repository?

    A: Anyone under Medic GitHub organization.

    A: If what you are working on is temporary and just for you then it is fine to create a repository under your personal account (it is the equivalent of having a script on your local machine), as long as it contains an Open-Source Software License. However, default to the Medic account so the other team members can collaborate on it.

    Q: When to make a repo public vs private?

    A: Repositories should be public unless there is very good reason to make it private (e.g. the repository contains partner details that cannot be disclosed to public). Always keep in mind that it is much easier to start public than change to public later.

    Q: When to create a new repository vs adding a directory in existing (monolithic)?

    A: It depends on the nature of the code. Some things to consider are: is the new code and the old code dependent, don’t make sense on their own, must be versioned together, etc. If not, default to a new repo to reduce complexity.

    More info

    This policy was inspired by Microsoft’s Engineering Fundamentals Checklist.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/code/static-analysis/index.html b/contribute/code/static-analysis/index.html index 4f87eebaa6..8215e7e0b8 100644 --- a/contribute/code/static-analysis/index.html +++ b/contribute/code/static-analysis/index.html @@ -1,9 +1,9 @@ -Static Analysis | Community Health Toolkit +Static Analysis | Community Health Toolkit

    Static Analysis

    Guidelines for static analysis of CHT code.

    eslint

    All code must pass an eslint check which runs early in the CI cycle and uses the standard medic eslint configuration.

    Sonar

    Sonar static analysis supports development by providing feedback on code quality and security issues. Sonar analysis must pass on all new code.

    SonarCloud can be enabled on any public repo in the medic organization.

    Workflow

    During development

    While writing code, the SonarLint plugin can be used to get real-time code analysis in your IDE. This is useful to avoid committing code with Sonar issues in the first place.

    PR Analysis

    SonarCloud is integrated with the CHT GitHub repositories and runs on every pull request. The results are posted as a comment on the pull request. If the Sonar analysis fails the quality check, the pull request will be blocked from merging.

    What should I do if Sonar finds an issue?

    When Sonar flags an issue with the code in your pull request, use this decision tree to determine the proper mitigation:

    1. If the issue is a genuine concern that should be addressed:
      1. Fix it and push the updated code to your PR. The PR will automatically be unblocked once the Sonar analysis succeeds.
    2. If the issue is a “false-positive” (i.e. Sonar has flagged some particular code as violating a rule, but it does not make sense to apply the rule in that context):
      1. If the rule is one that should not be applied to any CHT code:
        1. Remove the rule from the default Quality Profile.
      2. If it does not make sense to apply the rule to this particular code, you can do one of the following:
        1. Completely ignore Sonar issues on that line of code by adding the // NOSONAR comment to the end of the line.
        2. Completely ignore Sonar issues for that block of code.
        3. Update the .sonarcloud.properties to ignore that rule for that particular file.
        4. Update the .sonarcloud.properties to ignore all rules for that particular file (useful if the file has been copied from an external dependency).

    Adding a new repo to SonarCloud

    1. Add a .sonarcloud.properties file to the repository with your desired repo-level configuration.
    2. In the GitHub UI, navigate to the settings for the medic org > Third-party Access > GitHub Apps
    3. Find SonarCloud and click the Configure button.
    4. In the Repository access section, select your desired repository from the drop-down and click Save.
    5. You will be automatically redirected to the SonarCloud UI where you can configure the repo-level settings.
    6. Use the + button and choose Analyze new project.
    7. Select the repo from the list and click Set Up.
    8. In the SonarCloud configuration, disable summary comments in GitHub PRs.

    New Code Definition

    When setting up a new repository in SonarCloud, you will be asked to define what is considered to be “new code”. This is used to determine which code in the default branch is considered “new” (affects reporting of issues, etc). The new code definition is not applied to Sonar analysis of a PR. In that case, only the changes in the PR are considered “new”.

    Consult the documentation for more details on the options available. For projects that do not use Gradle or Maven for version management, the Number of days option is recommended (since Previous version would require maintaining a version number in the .sonarcloud.properties file).

    If you are using the CHT Way quality gate (or a similar zero-tolerance quality gate) it is recommended to set Number of days = 1. With a zero-tolerance quality gate, only issue-free code can be merged to the default branch. So, there is no need to check for issues accumulated over time. Also, having a higher Number of days opens up a greater opportunity for Sonar to introduce a new rule that will fail some code previously added to the default branch (code that is only included in the latest analysis because of the configured Number of days).

    Sonar Configuration

    Broadly speaking, Sonar configuration is separated into repo-level and org-level configuration.

    Repo-level configuration

    Each repository can include a .sonarcloud.properties file in the root directory.

    This file must specify the path to the source code in the repository as well as which source files should be considered to be test code. See the documentation for more details.

    # Path to sources
    + Create project issue

    Static Analysis

    Guidelines for static analysis of CHT code.

    eslint

    All code must pass an eslint check which runs early in the CI cycle and uses the standard medic eslint configuration.

    Sonar

    Sonar static analysis supports development by providing feedback on code quality and security issues. Sonar analysis must pass on all new code.

    SonarCloud can be enabled on any public repo in the medic organization.

    Workflow

    During development

    While writing code, the SonarLint plugin can be used to get real-time code analysis in your IDE. This is useful to avoid committing code with Sonar issues in the first place.

    PR Analysis

    SonarCloud is integrated with the CHT GitHub repositories and runs on every pull request. The results are posted as a comment on the pull request. If the Sonar analysis fails the quality check, the pull request will be blocked from merging.

    What should I do if Sonar finds an issue?

    When Sonar flags an issue with the code in your pull request, use this decision tree to determine the proper mitigation:

    1. If the issue is a genuine concern that should be addressed:
      1. Fix it and push the updated code to your PR. The PR will automatically be unblocked once the Sonar analysis succeeds.
    2. If the issue is a “false-positive” (i.e. Sonar has flagged some particular code as violating a rule, but it does not make sense to apply the rule in that context):
      1. If the rule is one that should not be applied to any CHT code:
        1. Remove the rule from the default Quality Profile.
      2. If it does not make sense to apply the rule to this particular code, you can do one of the following:
        1. Completely ignore Sonar issues on that line of code by adding the // NOSONAR comment to the end of the line.
        2. Completely ignore Sonar issues for that block of code.
        3. Update the .sonarcloud.properties to ignore that rule for that particular file.
        4. Update the .sonarcloud.properties to ignore all rules for that particular file (useful if the file has been copied from an external dependency).

    Adding a new repo to SonarCloud

    1. Add a .sonarcloud.properties file to the repository with your desired repo-level configuration.
    2. In the GitHub UI, navigate to the settings for the medic org > Third-party Access > GitHub Apps
    3. Find SonarCloud and click the Configure button.
    4. In the Repository access section, select your desired repository from the drop-down and click Save.
    5. You will be automatically redirected to the SonarCloud UI where you can configure the repo-level settings.
    6. Use the + button and choose Analyze new project.
    7. Select the repo from the list and click Set Up.
    8. In the SonarCloud configuration, disable summary comments in GitHub PRs.

    New Code Definition

    When setting up a new repository in SonarCloud, you will be asked to define what is considered to be “new code”. This is used to determine which code in the default branch is considered “new” (affects reporting of issues, etc). The new code definition is not applied to Sonar analysis of a PR. In that case, only the changes in the PR are considered “new”.

    Consult the documentation for more details on the options available. For projects that do not use Gradle or Maven for version management, the Number of days option is recommended (since Previous version would require maintaining a version number in the .sonarcloud.properties file).

    If you are using the CHT Way quality gate (or a similar zero-tolerance quality gate) it is recommended to set Number of days = 1. With a zero-tolerance quality gate, only issue-free code can be merged to the default branch. So, there is no need to check for issues accumulated over time. Also, having a higher Number of days opens up a greater opportunity for Sonar to introduce a new rule that will fail some code previously added to the default branch (code that is only included in the latest analysis because of the configured Number of days).

    Sonar Configuration

    Broadly speaking, Sonar configuration is separated into repo-level and org-level configuration.

    Repo-level configuration

    Each repository can include a .sonarcloud.properties file in the root directory.

    This file must specify the path to the source code in the repository as well as which source files should be considered to be test code. See the documentation for more details.

    # Path to sources
     sonar.sources=.
     # Can have multiple comma-separated entries
     sonar.exclusions=**/test*/**/*
    @@ -322,7 +322,8 @@
     sonar.issue.ignore.multicriteria.e2.ruleKey=javascript:S2699
     sonar.issue.ignore.multicriteria.e2.resourceKey=**/config.js
     

    Org-level configuration

    Organization-level configuration must be made by an authorized user in the SonarCloud UI.

    Quality Gates

    Quality gates are used to define the criteria that must be met for a Sonar analysis to be considered “passing”. The Sonar way quality gate provides an example of a useful configuration. However, this gate config is not ideal for CHT code. Instead, the default quality gate for the Medic organization is the CHT Way. It has the following metrics:

    MetricOperatorValue
    Duplicated Lines (%)is greater than6.0%
    Issuesis greater than0
    Reliability Ratingis worse thanA
    Security Hotspots Reviewedis less than100%
    Security Ratingis worse thanA
    Quality Profiles

    The quality profiles are the lists of rules that will be applied for the various supported languages. By default, we use the Sonar Way quality profile for each language as it provides sensible defaults and is actively maintained receiving updates with new rules and bug fixes as they are added to Sonar.

    Modifying rule parameters

    To modify a rule parameter (e.g. change the allowed level of complexity for a function according to javascript:S3776):

    1. Open a cht-docs PR to record your rule modification in the list below. This allows us to track the history of rule changes and record for posterity the discussions about them.
    2. If not already using a custom quality profile, use the SonarCloud UI to create one that extends the Sonar Way profile.
      1. Make sure to set the new quality profile as the default for that language, if desired.
    3. Open the rule in question in the SonarCloud UI and use the Change button associated with your quality profile to set your custom parameter value for the rule.
    Adding a rule

    To include a new rule in the code analysis, add it to the quality profile:

    1. Open a cht-docs PR to record your rule addition in the list below. This allows us to track the history of rule changes and record for posterity the discussions about them.
    2. If not already using a custom quality profile, use the SonarCloud UI to create one that extends the Sonar Way profile.
      1. Make sure to set the new quality profile as the default for that language, if desired.
    3. Open the rule in question in the SonarCloud UI and use the Activate button to activate the rule in your quality profile
    Removing a rule

    Removing a rule should only be done as a last resort. It is not possible to remove a rule inherited from the Sonar Way profile while at the same time still extending that profile. So, future updates to the Sonar Way profile will not be applied to your custom profile after a rule has been removed.

    1. Open a cht-docs PR to record your rule removal in the list below. This allows us to track the history of rule changes and record for posterity the discussions about them.
    2. If not already using a custom quality profile, use the SonarCloud UI to copy (not extend) the Sonar Way profile into a new profile.
      1. Make sure to set the new quality profile as the default for that language, if desired.
    3. Open the rule in question in the SonarCloud UI and use the Activate button to activate the rule in your quality profile
    Custom CHT Quality Profiles

    Java:

    • CHT Way (default) extends Sonar Way
      • Modified:
        • S107 - Functions should not have too many parameters
          • threshold 7 -> 4
        • S3776 - Cognitive Complexity of functions should not be too high
          • threshold 15 -> 5

    JavaScript:

    • CHT Way (default) extends Sonar Way
      • Modified:
        • S107 - Functions should not have too many parameters
          • threshold 7 -> 4
        • S3776 - Cognitive Complexity of functions should not be too high
          • threshold 15 -> 5
      • Disabled
        • S2699 - Tests should include assertions
          • Disabled due of rigidity of the rule when detecting expect imports and calls to imported functions that have assertions

    Python:

    • CHT Way (default) extends Sonar Way
      • Modified:
        • S107 - Functions should not have too many parameters
          • threshold 7 -> 4
        • S3776 - Cognitive Complexity of functions should not be too high
          • threshold 15 -> 5

    TypeScript:

    • CHT Way (default) extends Sonar Way
      • Modified:
        • S107 - Functions should not have too many parameters
          • threshold 7 -> 4
        • S3776 - Cognitive Complexity of functions should not be too high
          • threshold 15 -> 5
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/code/style-guide/index.html b/contribute/code/style-guide/index.html index dff6d000cd..0710a921cf 100644 --- a/contribute/code/style-guide/index.html +++ b/contribute/code/style-guide/index.html @@ -1,9 +1,9 @@ -Coding Style Guide | Community Health Toolkit +Coding Style Guide | Community Health Toolkit

    Coding Style Guide

    Guidelines for writing code

    Language

    Prefer JavaScript or TypeScript wherever possible, including in webapps, on the server, and for scripting. This is because:

    • every developer on the team knows it already, so we can all maintain it together,
    • it makes it easy to write cross-platform code and we have developers on all major operating systems,
    • it has a vast number of libraries that are easy to include, and
    • it’s easy to unit test

    Exceptions to this can be made on a case-by-case basis, but the decision must be made collectively before coding has begun to avoid having to rewrite.

    Styles

    This is a guide, not a law - use your discretion. Mostly based on Felix Geisendörfer’s guide with our own tweaks.

    Indention

    Use 2 spaces for indenting your code and swear an oath to never mix tabs and + Create project issue

    Coding Style Guide

    Guidelines for writing code

    Language

    Prefer JavaScript or TypeScript wherever possible, including in webapps, on the server, and for scripting. This is because:

    • every developer on the team knows it already, so we can all maintain it together,
    • it makes it easy to write cross-platform code and we have developers on all major operating systems,
    • it has a vast number of libraries that are easy to include, and
    • it’s easy to unit test

    Exceptions to this can be made on a case-by-case basis, but the decision must be made collectively before coding has begun to avoid having to rewrite.

    Styles

    This is a guide, not a law - use your discretion. Mostly based on Felix Geisendörfer’s guide with our own tweaks.

    Indention

    Use 2 spaces for indenting your code and swear an oath to never mix tabs and spaces - a special kind of hell is awaiting you otherwise.

    Newlines

    Use UNIX-style newlines (\n), and a newline character as the last character of a file. Windows-style newlines (\r\n) are forbidden inside any repository.

    No trailing whitespace

    Just like you brush your teeth after every meal, you clean up any trailing whitespace in your JS files before committing. Otherwise the rotten smell of @@ -560,7 +560,8 @@ FIRST_NAME: Mona LAST_NAME: Octocat

    See the full documentation on Github’s site.

    Third Party Actions

    Actions allow us to leverage code written by others to do tasks at build time. The concept is similar to NPM and packages.

    Follow the github actions best practices for security purposes. The main points in the security best practices documents are

    1. Pin actions to a full length commit SHA so any malicious or buggy updates are not silently included
    2. Audit the source code of the action
    3. Pin actions to a tag only if you trust the creator
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/contribute/code/using-npm/index.html b/contribute/code/using-npm/index.html index d5f0118c82..a4a36d8222 100644 --- a/contribute/code/using-npm/index.html +++ b/contribute/code/using-npm/index.html @@ -1,9 +1,9 @@ -Using NPM | Community Health Toolkit +Using NPM | Community Health Toolkit

    Using NPM

    Quick guide to using NPM

    npm Orgs

    We use npm Orgs to organize our npm packages. It provides a centralized way + Create project issue

    Using NPM

    Quick guide to using NPM

    npm Orgs

    We use npm Orgs to organize our npm packages. It provides a centralized way to manage a team’s published npm packages and permissions. Here are some guidelines when using this service.

    See npm’s Orgs docs for more information.

    Our organization is medic or using npm’s notation, @medic.

    We also created @medicmobile but it’s not currently in use, it was created to reserve the namespace.

    Adding a Package

    When you publish an npm module on npmjs.com, add it to the developers team @@ -319,7 +319,8 @@ registry.

    For example if you fork moment and you can’t get your changes merged upstream and need to publish a new package then modify the package name (in package.json) to specify a organizational scope, like @medic/moment and publish it.

    For more info see Publishing an Org Scoped Package.

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/contribute/code/workflow/index.html b/contribute/code/workflow/index.html index cdbe785e60..a0a5c8e76c 100644 --- a/contribute/code/workflow/index.html +++ b/contribute/code/workflow/index.html @@ -1,9 +1,9 @@ -Development Workflow | Community Health Toolkit +Development Workflow | Community Health Toolkit

    Development Workflow

    Overview of the development workflow

    Code

    Writing

    Where possible, follow our coding style guide.

    Aim for self-documenting code. Where code cannot be made self-documenting add commenting. Usually comments are useful when they explain why some code exists, and should not be explaining what some code is doing.

    Pushing Code & Opening Pull Requests

    Never push commits directly to the main branch (main or master). Always use a pull request.

    • If your code is in a regular pull request, it is assumed to be done and only needing a review and testing as checks before merging. It is best to request a reviewer, but otherwise anyone may freely review your PR.
    • If your code is in a draft PR, it is assumed to be a work-in-progress where collaboration is welcome, but best to communicate about specifics before assuming anything is complete.
    • If you have pushed code to a remote branch without a pull request, it is assumed to be a work-in-progress where collaboration is unexpected.

    A good workflow would be to work locally, pushing to a remote branch as you make progress, possibly open a draft PR for some initial collaboration on tricky parts, and once everything is done, convert the draft PR to a regular PR to be reviewed.

    Once your pull request has been approved, it can be merged to the main branch by anyone with write access to the repository. When merging a PR, avoid the “Create a merge commit” option. Merge commits in the main branch cause the history of the branch to be non-linear and make it more difficult to understand exactly when a code change was introduced. Instead, use the “Squash and merge” option to combine the commits in the PR into a single commit on the main branch. Alternatively, you can use the “Rebase and merge” option if you want all the commits in the PR to be preserved in the main branch (this should only be used in special cases). See below for instructions on how to format your commit messages.

    Code reviews

    Guidelines

    The author and reviewer should use this guide to code reviewing.

    Suggestions

    When doing a code review aim to be extremely clear. This helps things move quickly and avoids lost time in misunderstandings. One especially useful GitHub feature for doing this is suggesting a change. Consider the following example code:

    contacts.map( (c) => { return c.id });
    + Create documentation issue
    + Create project issue

    Development Workflow

    Overview of the development workflow

    Code

    Writing

    Where possible, follow our coding style guide.

    Aim for self-documenting code. Where code cannot be made self-documenting add commenting. Usually comments are useful when they explain why some code exists, and should not be explaining what some code is doing.

    Pushing Code & Opening Pull Requests

    Never push commits directly to the main branch (main or master). Always use a pull request.

    • If your code is in a regular pull request, it is assumed to be done and only needing a review and testing as checks before merging. It is best to request a reviewer, but otherwise anyone may freely review your PR.
    • If your code is in a draft PR, it is assumed to be a work-in-progress where collaboration is welcome, but best to communicate about specifics before assuming anything is complete.
    • If you have pushed code to a remote branch without a pull request, it is assumed to be a work-in-progress where collaboration is unexpected.

    A good workflow would be to work locally, pushing to a remote branch as you make progress, possibly open a draft PR for some initial collaboration on tricky parts, and once everything is done, convert the draft PR to a regular PR to be reviewed.

    Once your pull request has been approved, it can be merged to the main branch by anyone with write access to the repository. When merging a PR, avoid the “Create a merge commit” option. Merge commits in the main branch cause the history of the branch to be non-linear and make it more difficult to understand exactly when a code change was introduced. Instead, use the “Squash and merge” option to combine the commits in the PR into a single commit on the main branch. Alternatively, you can use the “Rebase and merge” option if you want all the commits in the PR to be preserved in the main branch (this should only be used in special cases). See below for instructions on how to format your commit messages.

    Code reviews

    Guidelines

    The author and reviewer should use this guide to code reviewing.

    Suggestions

    When doing a code review aim to be extremely clear. This helps things move quickly and avoids lost time in misunderstandings. One especially useful GitHub feature for doing this is suggesting a change. Consider the following example code:

    contacts.map( (c) => { return c.id });
     

    The function body can be abbreviated. In a review you can leave a comment asking for the change, which would likely involve writing up a comment trying to have the author change it to the following:

    contacts.map( c => c.id);
     

    That means leaving a comment, having the author read and understand it, and then making and pushing up a change, hopefully matching your review expectations.

    To be clear and save all that back-and-forth though, you can make a code suggestion directly in your review, which will let the author simply click a button to accept the change (and have it automatically applied as a commit by GitHub).

    GitHub review suggest change

    Timeliness

    Timely code reviews are important to getting improvements into the hands of users faster and allowing developers to stay focused on the task at hand and see it through to production.

    Code reviews should be completed within 24 hours of assignment (excluding weekends and holidays). In some cases, a code review may not be possible if a larger discussion needs to be had for design choices or solution objectives, but even in cases like those, some feedback is still to be expected within 24 hours.

    Updating The Issue With What You Actually Did

    Add labels to the GitHub issue as needed. At this stage, the two to look out for are:

    • Breaking change
    • UI/UX

    Add a comment to the GitHub issue with what the final change actually was. This is important for multiple cases including:

    • Non-technical people may not understand the conversation thread on the issue. GitHub is a place that developers work, but it is also common to send non-technical people links to issues in GitHub.
    • The QA team should have a quick way to know where to start testing.
    • Issues with a lot of discussion of alternative solutions need a clear resolution and indication of which route was taken.

    Options for doing this:

    • Attach a short video - these are usually very well received and can often help people understand what happened much more clearly than a text description.
    • Screenshots - pictures with big arrows on them can quickly convey important things to look at. If you start to need multiple screenshots consider the video option instead.
    • Write up a few sentences - be sure to consider a non-technical audience when writing this.

    An example of a good thorough comment/template is as follows:

    ### Testing
     
    @@ -383,7 +383,8 @@
     Test failed :x:
     The ticket needs further development.
     @<developer's name>

    A great way to facilitate discussion and collaboration is with a Draft PR.

    Once you’re confident that the change is complete and ready to be merged:

    1. Submit a PR for each of the repositories. Each PR message and description will become the commit message and description so keep the message concise, describe what and why rather than how, and link to the issue in the description (eg: “medic/cht-core#123”).
    2. Wait for the builds to succeed and ensure there are no conflicts with the the main branch so the PR can be merged.
    3. Pick one Reviewer for the PR and work with them until the code passes review. In some special cases more than one Reviewer may be necessary, but be specific about additional Reviewers and ensure you really need each of their additional reviews for a good reason. Remember, anyone can collaborate on PRs even if they aren’t an official Reviewer. If you add a QA Engineer as a Reviewer, briefly comment in the ticket about what kind of testing review you expect from that engineer.

    Once all PRs have been approved:

    1. Write a useful commit message in the PR using the commit message format.
    2. Click the button to “Squash and Merge” the PR.
    3. If a backport is required cherry-pick the merged commit back to the release branches it’s required in.
    4. Ensure the issue is added to the appropriate release milestone, which is the earliest semver version the change will be released in. This ensures it will be included in the release notes.
    5. Once all PRs have been merged, close the issue. This will automatically move it to “Done”.

    Done

    Issues in this column are complete, all code has been merged into the main branch and/or release branches, and are ready for release.

    -

    \ No newline at end of file +

    \ No newline at end of file diff --git a/contribute/docs/index.html b/contribute/docs/index.html index 896bd520a6..0f8adf21fb 100644 --- a/contribute/docs/index.html +++ b/contribute/docs/index.html @@ -1,9 +1,9 @@ -Contributing Documentation | Community Health Toolkit +Contributing Documentation | Community Health Toolkit

    Contributing Documentation

    How to contribute to documentation for the CHT

    This Community Health Toolkit documentation site is the primary resource for partners, app designers, and developers looking to learn about, as well as build digital health apps using the CHT. The documentation site includes product feature overviews, design resources, implementer guides, and technical reference documentation.

    Documentation is a collaborative effort among all community members. Contributors play an important role in:

    • Making suggestions for improvements
    • Reporting and correcting mistakes
    • Updating text and examples for clarity
    • Authoring guides and tutorials

    The CHT community welcomes first-time contributors and experts alike. All comment, questions, and ideas are welcome!


    Documentation Workflow

    How to contribute to the CHT tools and documentation

    Documentation Style Guide

    Editorial guidelines for writing documentation

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/docs/index.xml b/contribute/docs/index.xml index 1f3b85475c..174d2bb40d 100644 --- a/contribute/docs/index.xml +++ b/contribute/docs/index.xml @@ -1,639 +1,3 @@ -Community Health Toolkit – Contributing Documentationhttps://docs.communityhealthtoolkit.org/contribute/docs/Recent content in Contributing Documentation on Community Health ToolkitHugo -- gohugo.ioenContribute: Documentation Workflowhttps://docs.communityhealthtoolkit.org/contribute/docs/workflow/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/docs/workflow/ -<h2 id="getting-started">Getting Started</h2> -<p>Anyone can contribute to CHT documentation by opening an issue in the <a href="https://github.com/medic/cht-docs/issues"><code>cht-docs</code></a> repo or by using the “Edit this page” or “Create documentation issue” links in the upper right corner of your window.</p> -<h3 id="basics">Basics</h3> -<ul> -<li>It is helpful to be comfortable with <a href="https://git-scm.com/doc/ext">git</a> and <a href="https://github.com/">GitHub</a> to contribute to the CHT community.</li> -<li>The documentation source is in <a href="https://github.com/medic/cht-docs">GitHub</a>. The content pages are in the <code>/content/en/</code> directory.</li> -<li>Documentation is written in <a href="https://www.markdownguide.org/">Markdown</a>.</li> -<li>The CHT site build uses <a href="https://gohugo.io/">Hugo</a>. You can also setup a <a href="https://github.com/medic/cht-docs/blob/main/README.md">local clone</a>.</li> -</ul> -<h2 id="writing-documentation">Writing Documentation</h2> -<p>A high degree of importance is put on consistency and usability of CHT documentation so that it is accessible and understood by a wide audience. The CHT <a href="https://docs.communityhealthtoolkit.org/contribute/docs/style-guide/">documentation style guide</a> will help to write documentation in the most consistent and useful way.</p> -<h2 id="commits-to-github">Commits to GitHub</h2> -<p>The main branch is <code>main</code> which must be kept stable since it is deployed to the doc site. All documentation changes should be done in a branch with a Pull Request when ready for review. This means that a maintainer has signed off on the change before it hits the main branch.</p> -<p>Format your commit messages according to the Git convention where the first line should be a short title/summary (50 characters or so) with more details in a separate paragraph (if needed).</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Every commit message should be able to complete the following sentence: -When applied, this commit will: {YOUR COMMIT MESSAGE} -</div> -<h2 id="creating-a-pull-request">Creating a Pull Request</h2> -<p>When your branch is ready for review, create a <a href="https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request">Pull Request</a>. If you know who you&rsquo;d like to review the PR, you can assign them directly. If you are unsure, you can leave it to the maintainers to handle the PR.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -If the PR is part of an open issue in cht-core, add the <a href="https://github.com/medic/cht-docs/labels/Blocked%3A%20waiting%20on%20AT"><code>Blocked: waiting on AT</code></a> label so that the PR isn’t accidentally merged prematurely, before the issue is acceptance tested and complete. -</div> -<h2 id="reviewing-pull-requests">Reviewing Pull Requests</h2> -<p>In general, reviewers should:</p> -<ol> -<li>Read the PR description to understand the changes made, as well as any linked issues</li> -<li>Review any comments by other reviewers</li> -<li>Select the <strong>Files changed</strong> tab to see the files and lines changed</li> -<li>Click the <strong>+</strong> beside the line you want to comment on. To select multiple lines at once, click the <strong>+</strong> of the top line of the selection, drag down to the bottom line, and release.</li> -<li>Add any comments you have about the line and click either <strong>Add single comment</strong> (if you want to post the comment without a review) or <strong>Start a review</strong> (if you have multiple comments to make).</li> -<li>When finished, click <strong>Review changes</strong> at the top of the page. Here, you can add a summary of your review, approve the PR, comment or request changes as needed.</li> -<li>Once all comments have been resolved, or changes are satisfactory, <strong>Merge pull request</strong> to complete the updates, and delete the branch.</li> -</ol> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -<p>Additional Tips for Reviewers:</p> -<ul> -<li>Use the <a href="https://docs.communityhealthtoolkit.org/contribute/docs/style-guide/">Style Guide</a> to maintain documentation quality</li> -<li>Compare content to pages within the same section and encourage consistency</li> -<li>Be empathetic to the author, commenting on positive aspects of PRs as well as changes</li> -<li>Ask clarifying questions where needed to avoid further confusion</li> -</ul> -</div>Contribute: Documentation Style Guidehttps://docs.communityhealthtoolkit.org/contribute/docs/style-guide/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/docs/style-guide/ -<p>This style guide provides a set of editorial guidelines for anyone writing documentation for Community Health Toolkit projects. These are guidelines, not rules. Use your best judgment.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -This documentation site does not involve release management and acceptance testing. Help us maintain the quality of our documentation by submitting a pull request (PR) with any suggested changes. One of the repository&rsquo;s maintainers will review the PR, request additional changes as needed, and merge the PR when it is ready. -</div> -<h2 id="language">Language</h2> -<p>Documentation for the Community Health Toolkit is written is American English.</p> -<h2 id="general-guidelines-and-best-practices">General guidelines and best practices</h2> -<p>This section contains suggested best practices for clear, concise, and consistent content.</p> -<h3 id="present-tense">Present tense</h3> -<table> -<thead> -<tr> -<th>Do</th> -<th>Don&rsquo;t</th> -</tr> -</thead> -<tbody> -<tr> -<td>CouchDB converts this to a properly hashed password when you save.</td> -<td>CouchDB will convert this to a properly hashed password on save.</td> -</tr> -</tbody> -</table> -<p>Exception: Use future or past tense if it is required to convey the correct meaning.</p> -<h3 id="active-voice">Active voice</h3> -<table> -<thead> -<tr> -<th>Do</th> -<th>Don&rsquo;t</th> -</tr> -</thead> -<tbody> -<tr> -<td>Stop everything and delete the medic DB to clear your database.</td> -<td>Clear your db by stopping everything and deleting the medic DB.</td> -</tr> -<tr> -<td>Replicate your local production database into a new medic database to bootstrap your data.</td> -<td>Bootstrap your data by replicating your local PROD DB into a new medic database.</td> -</tr> -</tbody> -</table> -<p>Exception: Use passive voice if active voice leads to an awkward construction.</p> -<h3 id="simple-and-direct-language">Simple and direct language</h3> -<p>Use simple and direct language. Avoid using unnecessary phrases, such as saying &ldquo;please.&rdquo;</p> -<table> -<thead> -<tr> -<th>Do</th> -<th>Don&rsquo;t</th> -</tr> -</thead> -<tbody> -<tr> -<td>To create a database, &hellip;</td> -<td>In order to create a database, &hellip;</td> -</tr> -<tr> -<td>See the configuration file.</td> -<td>Please see the configuration file.</td> -</tr> -<tr> -<td>View the logs.</td> -<td>With this next command, we&rsquo;ll view the logs.</td> -</tr> -</tbody> -</table> -<h3 id="address-the-reader-as-you">Address the reader as &ldquo;you&rdquo;</h3> -<table> -<thead> -<tr> -<th>Do</th> -<th>Don&rsquo;t</th> -</tr> -</thead> -<tbody> -<tr> -<td>You can create a database by &hellip;</td> -<td>We&rsquo;ll create a database by &hellip;</td> -</tr> -<tr> -<td>In the preceding output, you can see&hellip;</td> -<td>In the preceding output, we can see &hellip;</td> -</tr> -</tbody> -</table> -<h3 id="latin-phrases">Latin phrases</h3> -<p>Prefer English terms over Latin abbreviations.</p> -<table> -<thead> -<tr> -<th>Do</th> -<th>Don&rsquo;t</th> -</tr> -</thead> -<tbody> -<tr> -<td>For example, &hellip;</td> -<td>e.g., &hellip;</td> -</tr> -<tr> -<td>That is, &hellip;</td> -<td>i.e., &hellip;</td> -</tr> -</tbody> -</table> -<p>Exception: Use &ldquo;etc.&rdquo; for et cetera.</p> -<h2 id="practices-to-avoid">Practices to avoid</h2> -<h3 id="using-we">Using &ldquo;we&rdquo;</h3> -<p>Using &ldquo;we&rdquo; in a sentence can be confusing, because the reader might not know -whether they&rsquo;re part of the &ldquo;we&rdquo; you&rsquo;re describing.</p> -<table> -<thead> -<tr> -<th>Do</th> -<th>Don&rsquo;t</th> -</tr> -</thead> -<tbody> -<tr> -<td>Version 3.0 includes &hellip;</td> -<td>In version 3.0, we have added &hellip;</td> -</tr> -<tr> -<td>Medic provides a new feature to reduce the time to load contacts.</td> -<td>We made several changes to reduce the time to load contacts.</td> -</tr> -<tr> -<td>This page teaches you how to use cht-gateway.</td> -<td>In this page, we are going to learn about cht-gateway.</td> -</tr> -</tbody> -</table> -<h3 id="using-jargon-and-idioms">Using jargon and idioms</h3> -<p>Some readers speak English as a second language. Avoid jargon and idioms to help them understand better.</p> -<table> -<thead> -<tr> -<th>Do</th> -<th>Don&rsquo;t</th> -</tr> -</thead> -<tbody> -<tr> -<td>To get started, &hellip;</td> -<td>To get up and running with no fuss, &hellip;</td> -</tr> -<tr> -<td>Internally, &hellip;</td> -<td>Under the hood, &hellip;</td> -</tr> -<tr> -<td>Create a new database.</td> -<td>Turn up a new database.</td> -</tr> -</tbody> -</table> -<h3 id="using-statements-about-the-future">Using statements about the future</h3> -<p>Avoid giving hints about the future. If you need to talk about -an alpha or beta feature, put the text under a heading that identifies it as alpha or beta -information.</p> -<h3 id="using-statements-that-will-soon-be-out-of-date">Using statements that will soon be out of date</h3> -<p>Avoid words like &ldquo;currently&rdquo; and &ldquo;new.&rdquo; A feature that is new today might not be -considered new in a few months.</p> -<table> -<thead> -<tr> -<th>Do</th> -<th>Don&rsquo;t</th> -</tr> -</thead> -<tbody> -<tr> -<td>In version 3.4, &hellip;</td> -<td>In the current version, &hellip;</td> -</tr> -<tr> -<td>The Log user statistics feature provides &hellip;</td> -<td>The new Log user statistics feature provides &hellip;</td> -</tr> -</tbody> -</table> -<h2 id="cross-referencing-content">Cross-referencing content</h2> -<p>Connecting readers to related content in different pages is an important aspect of documentation. There are three ways this can be done in the doc site:</p> -<ol> -<li> -<p><strong>Inline links</strong>: a portion of any narrative text can link to another page. This should done using the markdown link notation.</p> -<p>For example, the text <code>linking documents is a [foundational reason for the web existing in the first place](https://en.wikipedia.org/wiki/Hypertext)!</code> yields: &ldquo;linking documents is a <a href="https://en.wikipedia.org/wiki/Hypertext">foundational reason for the web existing in the first place</a>!&rdquo;</p> -</li> -<li> -<p><strong>See Also</strong>: the <code>see-also</code> shortcode is available to connect to an important concept within the documentation site. The link will be more prominent to the reader by having a common prefix and shown on a separate line.</p> -<p>For example, <code>{{&lt; see-also page=&quot;design/icons&quot; &gt;}}</code> will show as seen here: -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/design/icons/">Icon Library</a></p> -</p> -<p>You can also make the callout say &ldquo;Read More&rdquo; with the <code>prefix</code> tag: <code>{{&lt; see-also prefix=&quot;Read More&quot; page=&quot;design/icons&quot; &gt;}}</code>. This will show as seen here: -<p><em>Read More</em>: <a href="https://docs.communityhealthtoolkit.org/design/icons/">Icon Library</a></p> -</p> -<p>A custom title and anchor can be provided as well. For example, <code>{{&lt; see-also page=&quot;design/icons&quot; title=&quot;Learn about the Icon Library&quot; anchor=&quot;about-the-icon-library&quot; &gt;}}</code>, will show as: -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/design/icons/#about-the-icon-library">Learn about the Icon Library</a></p> -</p> -<p>Please use <code>see-also</code> when referencing <em>related topics</em> , as seen in <a href="https://docs.communityhealthtoolkit.org/apps/concepts/workflows/">Workflows</a>, and use <code>read-more</code> when referencing the <em>same topic</em> in more depth, as in the <a href="https://docs.communityhealthtoolkit.org/">Home Page</a>.</p> -</li> -<li> -<p><strong>Related Content</strong>: Pages within the documentation site are often closely related, but are separated by the type of content. For instance, a topic may be described in the features, have an implementation guide, and have best practices in the design system. To make this linkage easier for documentation writers and readers, a &ldquo;Related Content&rdquo; section can be shown at the bottom of the page. Each page defines it&rsquo;s own related content as <code>relatedContent</code> in its front matter. For example, a page with the following front matter would have two pages shown as <em>Related Content</em>.</p> -<pre tabindex="0"><code>--- -title: Messaging -relatedContent: &gt; -apps/guides/messaging/ -design/apps/ ---- -</code></pre></li> -</ol> -<h3 id="avoid-broken-links">Avoid broken links</h3> -<p>To avoid broken links always use <code>ref</code> or <code>relref</code> shortcodes for internal references with the full path for the page. Check out the <a href="https://gohugo.io/content-management/cross-references/">Hugo documentation for cross-references</a> for more details.</p> -<p>For example, <code>[Icon Library]({{&lt; relref &quot;design/icons&quot; &gt;}})</code> yields &ldquo;<a href="https://docs.communityhealthtoolkit.org/design/icons/">Icon Library</a>&rdquo;. Using the full path will avoid ambiguous references if a new page of the same is created.</p> -<h3 id="link-paragraphs-not-titles">Link paragraphs, not titles</h3> -<p>Whether using <code>ref</code> ,<code>relref</code> or inline links, do not link a title:</p> -<table> -<thead> -<tr> -<th>Do</th> -<th>Don&rsquo;t</th> -</tr> -</thead> -<tbody> -<tr> -<td><code> Read more about [InnoDB here](https://en.wikipedia.org/wiki/InnoDB).</code></td> -<td><code>## [InnoDB here](https://en.wikipedia.org/wiki/InnoDB)</code></td> -</tr> -<tr> -<td><code>The [Icon Library]({{&lt; relref &quot;design/icons&quot; &gt;}}) has many great icons.</code></td> -<td><code>## [Icon Library]({{&lt; relref &quot;design/icons&quot; &gt;}})</code></td> -</tr> -</tbody> -</table> -<h2 id="formatting-standards">Formatting standards</h2> -<h3 id="use-markdown-notation">Use Markdown Notation</h3> -<p>Documentation pages should be written in <a href="https://www.markdownguide.org/">Markdown notation</a>, and not contain HTML tags whenever possible.</p> -<table> -<thead> -<tr> -<th>Style</th> -<th>Do</th> -<th>Don&rsquo;t</th> -</tr> -</thead> -<tbody> -<tr> -<td><em>italic</em></td> -<td><code>_italic_</code> or <code>*italic*</code></td> -<td><code>&lt;i&gt;italic&lt;/i&gt;</code> or <code>&lt;em&gt;bold&lt;/em&gt;</code></td> -</tr> -<tr> -<td><strong>bold</strong></td> -<td><code>**bold**</code></td> -<td><code>&lt;b&gt;bold&lt;/b&gt;</code> or <code>&lt;strong&gt;bold&lt;/strong&gt;</code></td> -</tr> -<tr> -<td>table</td> -<td>`</td> -<td>&hellip;</td> -</tr> -</tbody> -</table> -<h3 id="tabular-schedules">Tabular schedules</h3> -<p>Displaying the occurrence of events over time in a workflow is often done using a table. To keep these consistent we recommend using the tabular schedule format.</p> -<p>For example, here is a sample vaccination schedules:</p> -<div class="schedule"></div> -<table> -<thead> -<tr> -<th></th> -<th>6m</th> -<th>12m</th> -<th>18m</th> -<th>2y</th> -<th>2.5y</th> -<th>3y</th> -<th>3.5y</th> -<th>4y</th> -<th>4.5y</th> -<th>5y</th> -</tr> -</thead> -<tbody> -<tr> -<td>Deworming</td> -<td></td> -<td></td> -<td>X</td> -<td>X</td> -<td>X</td> -<td>X</td> -<td></td> -<td>X</td> -<td>X</td> -<td>X</td> -</tr> -<tr> -<td>Vitamin A</td> -<td>X</td> -<td>X</td> -<td>X</td> -<td>X</td> -<td>X</td> -<td>X</td> -<td>X</td> -<td>X</td> -<td>X</td> -<td>X</td> -</tr> -</tbody> -</table> -<p>To achieve this use a markdown table with the letter X (<code>X</code>) to mark events, leaving cells empty when no action is needed. The shortcode <code>{{% schedule %}}</code> is used before and after the markdown table so that built-in styling can be applied. Here is the code for the above example:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span>{{% schedule %}} -</span></span><span style="display:flex;"><span>|| 6m | 12m | 18m | 2y | 2.5y | 3y | 3.5y | 4y | 4.5y | 5y | -</span></span><span style="display:flex;"><span>|------------|--|--|--|--|--|--|--|--|--|--| -</span></span><span style="display:flex;"><span>| Deworming | | | X | X | X | X | | X | X | X | -</span></span><span style="display:flex;"><span>| Vitamin A | X | X | X | X | X | X | X | X | X | X | -</span></span><span style="display:flex;"><span>{{% /schedule %}} -</span></span></code></pre></div> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -<p>The opening <code> -<div class="schedule"></div> -<p></code> and closing <code></code> shortcode must come before and after the markdown table respectively in order for it to correctly style the table</p></p> -</div> -<h3 id="grammar-and-punctuation-in-headers">Grammar and punctuation in headers</h3> -<p>Use title case for page <code>title</code>, and sentence case for <code>linkTitle</code> description. Do not end titles with periods.</p> -<h3 id="angle-brackets-for-placeholders">Angle brackets for placeholders</h3> -<p>Use angle brackets for placeholders. Tell the reader what a placeholder -represents.</p> -<ol> -<li>Create a file named <code>&lt;project_name&gt;-medic-os-compose.yml</code>. Where <code>&lt;project_name&gt;</code> is the name of one of your project.</li> -</ol> -<h3 id="bold-for-user-interface-elements">Bold for user interface elements</h3> -<table> -<thead> -<tr> -<th>Do</th> -<th>Don&rsquo;t</th> -</tr> -</thead> -<tbody> -<tr> -<td>Click <strong>Fork</strong>.</td> -<td>Click &ldquo;Fork&rdquo;.</td> -</tr> -<tr> -<td>Select <strong>Other</strong>.</td> -<td>Select &lsquo;Other&rsquo;.</td> -</tr> -</tbody> -</table> -<h3 id="italics-to-define-or-introduce-new-terms">Italics to define or introduce new terms</h3> -<table> -<thead> -<tr> -<th>Do</th> -<th>Don&rsquo;t</th> -</tr> -</thead> -<tbody> -<tr> -<td>By default CouchDB runs in <em>admin party</em> mode, which means you do not need users to read or edit any data.</td> -<td>By default CouchDB runs in &ldquo;admin party&rdquo; mode, which means you do not need users to read or edit any data.</td> -</tr> -<tr> -<td>The <em>keys</em> in <code>.properties</code> files are referred to as <em>terms</em>.</td> -<td>The &ldquo;keys&rdquo; in .properties files are referred to as <strong>terms</strong>.</td> -</tr> -</tbody> -</table> -<h3 id="code-style-for-filenames-directories-and-paths">Code style for filenames, directories, and paths</h3> -<table> -<thead> -<tr> -<th>Do</th> -<th>Don&rsquo;t</th> -</tr> -</thead> -<tbody> -<tr> -<td>Open <code>messages-en.properties</code> file.</td> -<td>Open messages-en.properties file.</td> -</tr> -<tr> -<td>The file is located in <code>/config/standard</code> directory.</td> -<td>The file is located in /config/standard directory.</td> -</tr> -<tr> -<td>Create <code>medic/translations/messages-en.properties</code> file.</td> -<td>Create medic/translations/messages-en.properties file.</td> -</tr> -</tbody> -</table> -<h3 id="british-standard-for-punctuation-inside-quotes">British standard for punctuation inside quotes</h3> -<table> -<thead> -<tr> -<th>Do</th> -<th>Don&rsquo;t</th> -</tr> -</thead> -<tbody> -<tr> -<td>The doc in the database is a &ldquo;record&rdquo;.</td> -<td>The doc in the database is a &ldquo;record.&rdquo;</td> -</tr> -<tr> -<td>The copy is called a &ldquo;fork&rdquo;.</td> -<td>The copy is called a &ldquo;fork.&rdquo;</td> -</tr> -</tbody> -</table> -<h3 id="number-formatting">Number formatting</h3> -<p>Avoid the use of comma or period as thousands separator since it can be confused for a decimal point in some countries. Either use no separator for small numbers or a unicode <em>Thin Space</em> which is often <a href="https://en.wikipedia.org/wiki/Decimal_separator#Digit_grouping">recommended for international documents</a>.</p> -<p>There&rsquo;s a shortcode <code>format-number</code> which will replace <code>_</code> with <em>Thin Space</em> to simplify formatting in this way.</p> -<table> -<thead> -<tr> -<th>Do</th> -<th>Don&rsquo;t</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>{{&lt; format-number 10_000 &gt;}}</code></td> -<td>10,000</td> -</tr> -<tr> -<td><code>{{&lt; format-number 10_000 &gt;}}</code></td> -<td>10.000</td> -</tr> -<tr> -<td><code>{{&lt; format-number 1_000_000 &gt;}}</code></td> -<td>1000000</td> -</tr> -</tbody> -</table> -<h3 id="notes-and-tips">Notes and tips</h3> -<p>Make notes and tips stand out by using blockquote styling.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -This is a sample note. -</div> -<p><code>{{% alert title=&quot;Note&quot; %}} This is a sample note. {{% /alert %}}</code></p> -<h3 id="images">Images</h3> -<p>The <a href="https://www.markdownguide.org/basic-syntax/#images-1">image markdown syntax</a> can be used for images, but if any styling is required use the built-in <code>figure</code> shortcode. With the <code>figure</code> shortcode <a href="https://gohugo.io/content-management/shortcodes/#figure">many fields are configurable</a>, and the position and size can be responsive with <a href="https://getbootstrap.com/docs/4.0/layout/grid/#responsive-classes">Bootstrap grid classes</a>. You should avoid using the HTML <code>img</code> tag in the documentation.</p> -<table> -<thead> -<tr> -<th>Do</th> -<th>Don&rsquo;t</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>{{&lt; figure src=&quot;image.png&quot; class=&quot;right col-6 col-lg-3&quot; &gt;}}</code></td> -<td><code>&lt;img src=&quot;image.png&quot; width=&quot;30%&quot; align=&quot;right&quot;&gt;</code></td> -</tr> -<tr> -<td><code>{{&lt; figure src=&quot;image.png&quot; class=&quot;right col-6 col-lg-3&quot; &gt;}}</code></td> -<td><code>&lt;img src=&quot;image.png&quot; style=&quot;width:30%; align:right;&quot;&gt;</code></td> -</tr> -</tbody> -</table> -<p>It is good practice for the image to link to the image file so that a larger version can be viewed easily. This can be done using the <code>link</code> attribute with the <code>figure</code> shortcode, which is less error prone than adding a link to the markdown image notation.</p> -<table> -<thead> -<tr> -<th>Do</th> -<th>Don&rsquo;t</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>{{&lt; figure src=&quot;image.png&quot; link=&quot;image.png&quot; alt=&quot;Alt text&quot; title=&quot;Image Title&quot; &gt;}}</code></td> -<td><code>[![Alt text](image.png &quot;Image Title&quot;)](image.png)</code></td> -</tr> -</tbody> -</table> -<h3 id="indicating-location-of-items-on-the-screen">Indicating location of items on the screen</h3> -<table> -<thead> -<tr> -<th>Do</th> -<th>Don&rsquo;t</th> -</tr> -</thead> -<tbody> -<tr> -<td>right-hand side</td> -<td>right hand side</td> -</tr> -<tr> -<td>left-hand</td> -<td>left hand</td> -</tr> -</tbody> -</table> -<h3 id="where-to-place-your-images">Where to place your images</h3> -<p>To ensure your images are loaded on the docsite in the right format,place them in a folder that is named similar to your .md file. For example in the scenario below, the image is linked in the style-guide.md file, thus it is placed in the style-guide folder. -<figure><a href="where-to-place-images.png"> -<img src="where-to-place-images.png"/> </a> -</figure> -</p> -<h3 id="videos">Videos</h3> -<p>When embedding videos, use the <code>youtube</code> shortcode to embed a responsive YouTube video player.</p> -<p>Copy the YouTube video ID that follows <code>v=</code> in the video’s URL and pass it to the <code>youtube</code> shortcode. For instance, with <code>https://www.youtube.com/watch?v=pFEFIY_SA7M</code> the shortcode would be:</p> -<p><code>{{&lt; youtube pFEFIY_SA7M &gt;}}</code></p> -<p>And would display as seen here:</p> -<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"> -<iframe src="https://www.youtube.com/embed/pFEFIY_SA7M" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe> -</div> -<h2 id="inline-code-formatting">Inline code formatting</h2> -<h3 id="code-style-for-inline-code-and-commands">Code style for inline code and commands</h3> -<p>For inline code in an HTML document, use the ``` tag. In a Markdown -document, use the backtick (`).</p> -<table> -<thead> -<tr> -<th>Do</th> -<th>Don&rsquo;t</th> -</tr> -</thead> -<tbody> -<tr> -<td>The <code>npm run build-dev-watch</code> command builds and deploys the webapp.</td> -<td>The &ldquo;npm run build-dev-watch&rdquo; command creates a Deployment.</td> -</tr> -<tr> -<td>To upload the configuration from your current directory, use <code>cht --local</code>.</td> -<td>To upload the configuration from your current directory, use &ldquo;cht &ndash;local&rdquo;.</td> -</tr> -<tr> -<td>Enclose code samples with triple backticks. <code>(```)</code></td> -<td>Enclose code samples with any other syntax.</td> -</tr> -</tbody> -</table> -<h2 id="code-snippet-formatting">Code snippet formatting</h2> -<h3 id="dont-include-the-command-prompt">Don&rsquo;t include the command prompt</h3> -<table> -<thead> -<tr> -<th>Do</th> -<th>Don&rsquo;t</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>export COUCH_NODE_NAME=couchdb@127.0.0.1</code></td> -<td><code>$ export COUCH_NODE_NAME=couchdb@127.0.0.1</code></td> -</tr> -</tbody> -</table> -<h3 id="separate-commands-from-output">Separate commands from output</h3> -<p>Verify the security settings on CouchDB:</p> -<pre tabindex="0"><code>curl http://localhost:5984 -</code></pre><p>The output is similar to this:</p> -<pre tabindex="0"><code>{&#34;error&#34;:&#34;unauthorized&#34;,&#34;reason&#34;:&#34;Authentication required.&#34;} -</code></pre><h2 id="community-health-toolkit-word-list">Community Health Toolkit word list</h2> -<p>A list of terms and words to be used consistently across the site.</p> -<table> -<thead> -<tr> -<th>Term</th> -<th>Usage</th> -</tr> -</thead> -<tbody> -<tr> -<td>Docker</td> -<td>Docker should always be capitalized.</td> -</tr> -<tr> -<td>Community Health Toolkit</td> -<td>Community Health Toolkit should always be capitalized.</td> -</tr> -<tr> -<td>CHT</td> -<td>Acronym for &ldquo;Community Health Toolkit&rdquo;.</td> -</tr> -<tr> -<td>CouchDB</td> -<td>No space between Couch and DB. Do not use Couchdb, Couch DB or other variations.</td> -</tr> -</tbody> -</table> \ No newline at end of file +Contributing Documentation on Community Health Toolkithttps://docs.communityhealthtoolkit.org/contribute/docs/Recent content in Contributing Documentation on Community Health ToolkitHugo -- gohugo.ioenDocumentation Workflowhttps://docs.communityhealthtoolkit.org/contribute/docs/workflow/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/docs/workflow/Getting Started Anyone can contribute to CHT documentation by opening an issue in the cht-docs repo or by using the “Edit this page” or “Create documentation issue” links in the upper right corner of your window. +Basics It is helpful to be comfortable with git and GitHub to contribute to the CHT community. The documentation source is in GitHub. The content pages are in the /content/en/ directory. Documentation is written in Markdown.Documentation Style Guidehttps://docs.communityhealthtoolkit.org/contribute/docs/style-guide/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/docs/style-guide/This style guide provides a set of editorial guidelines for anyone writing documentation for Community Health Toolkit projects. These are guidelines, not rules. Use your best judgment. +Note This documentation site does not involve release management and acceptance testing. Help us maintain the quality of our documentation by submitting a pull request (PR) with any suggested changes. One of the repository&rsquo;s maintainers will review the PR, request additional changes as needed, and merge the PR when it is ready. \ No newline at end of file diff --git a/contribute/docs/style-guide/index.html b/contribute/docs/style-guide/index.html index 556a5ae065..f2544adfce 100644 --- a/contribute/docs/style-guide/index.html +++ b/contribute/docs/style-guide/index.html @@ -1,9 +1,9 @@ -Documentation Style Guide | Community Health Toolkit +Documentation Style Guide | Community Health Toolkit

    Documentation Style Guide

    Editorial guidelines for writing documentation

    This style guide provides a set of editorial guidelines for anyone writing documentation for Community Health Toolkit projects. These are guidelines, not rules. Use your best judgment.

    Language

    Documentation for the Community Health Toolkit is written is American English.

    General guidelines and best practices

    This section contains suggested best practices for clear, concise, and consistent content.

    Present tense

    DoDon’t
    CouchDB converts this to a properly hashed password when you save.CouchDB will convert this to a properly hashed password on save.

    Exception: Use future or past tense if it is required to convey the correct meaning.

    Active voice

    DoDon’t
    Stop everything and delete the medic DB to clear your database.Clear your db by stopping everything and deleting the medic DB.
    Replicate your local production database into a new medic database to bootstrap your data.Bootstrap your data by replicating your local PROD DB into a new medic database.

    Exception: Use passive voice if active voice leads to an awkward construction.

    Simple and direct language

    Use simple and direct language. Avoid using unnecessary phrases, such as saying “please.”

    DoDon’t
    To create a database, …In order to create a database, …
    See the configuration file.Please see the configuration file.
    View the logs.With this next command, we’ll view the logs.

    Address the reader as “you”

    DoDon’t
    You can create a database by …We’ll create a database by …
    In the preceding output, you can see…In the preceding output, we can see …

    Latin phrases

    Prefer English terms over Latin abbreviations.

    DoDon’t
    For example, …e.g., …
    That is, …i.e., …

    Exception: Use “etc.” for et cetera.

    Practices to avoid

    Using “we”

    Using “we” in a sentence can be confusing, because the reader might not know + Create project issue

    Documentation Style Guide

    Editorial guidelines for writing documentation

    This style guide provides a set of editorial guidelines for anyone writing documentation for Community Health Toolkit projects. These are guidelines, not rules. Use your best judgment.

    Language

    Documentation for the Community Health Toolkit is written is American English.

    General guidelines and best practices

    This section contains suggested best practices for clear, concise, and consistent content.

    Present tense

    DoDon’t
    CouchDB converts this to a properly hashed password when you save.CouchDB will convert this to a properly hashed password on save.

    Exception: Use future or past tense if it is required to convey the correct meaning.

    Active voice

    DoDon’t
    Stop everything and delete the medic DB to clear your database.Clear your db by stopping everything and deleting the medic DB.
    Replicate your local production database into a new medic database to bootstrap your data.Bootstrap your data by replicating your local PROD DB into a new medic database.

    Exception: Use passive voice if active voice leads to an awkward construction.

    Simple and direct language

    Use simple and direct language. Avoid using unnecessary phrases, such as saying “please.”

    DoDon’t
    To create a database, …In order to create a database, …
    See the configuration file.Please see the configuration file.
    View the logs.With this next command, we’ll view the logs.

    Address the reader as “you”

    DoDon’t
    You can create a database by …We’ll create a database by …
    In the preceding output, you can see…In the preceding output, we can see …

    Latin phrases

    Prefer English terms over Latin abbreviations.

    DoDon’t
    For example, …e.g., …
    That is, …i.e., …

    Exception: Use “etc.” for et cetera.

    Practices to avoid

    Using “we”

    Using “we” in a sentence can be confusing, because the reader might not know whether they’re part of the “we” you’re describing.

    DoDon’t
    Version 3.0 includes …In version 3.0, we have added …
    Medic provides a new feature to reduce the time to load contacts.We made several changes to reduce the time to load contacts.
    This page teaches you how to use cht-gateway.In this page, we are going to learn about cht-gateway.

    Using jargon and idioms

    Some readers speak English as a second language. Avoid jargon and idioms to help them understand better.

    DoDon’t
    To get started, …To get up and running with no fuss, …
    Internally, …Under the hood, …
    Create a new database.Turn up a new database.

    Using statements about the future

    Avoid giving hints about the future. If you need to talk about an alpha or beta feature, put the text under a heading that identifies it as alpha or beta information.

    Using statements that will soon be out of date

    Avoid words like “currently” and “new.” A feature that is new today might not be @@ -321,7 +321,8 @@ document, use the backtick (`).

    DoDon’t
    The npm run build-dev-watch command builds and deploys the webapp.The “npm run build-dev-watch” command creates a Deployment.
    To upload the configuration from your current directory, use cht --local.To upload the configuration from your current directory, use “cht –local”.
    Enclose code samples with triple backticks. (```)Enclose code samples with any other syntax.

    Code snippet formatting

    Don’t include the command prompt

    DoDon’t
    export COUCH_NODE_NAME=couchdb@127.0.0.1$ export COUCH_NODE_NAME=couchdb@127.0.0.1

    Separate commands from output

    Verify the security settings on CouchDB:

    curl http://localhost:5984
     

    The output is similar to this:

    {"error":"unauthorized","reason":"Authentication required."}
     

    Community Health Toolkit word list

    A list of terms and words to be used consistently across the site.

    TermUsage
    DockerDocker should always be capitalized.
    Community Health ToolkitCommunity Health Toolkit should always be capitalized.
    CHTAcronym for “Community Health Toolkit”.
    CouchDBNo space between Couch and DB. Do not use Couchdb, Couch DB or other variations.
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/contribute/docs/workflow/index.html b/contribute/docs/workflow/index.html index f54d952f19..64593903f8 100644 --- a/contribute/docs/workflow/index.html +++ b/contribute/docs/workflow/index.html @@ -1,9 +1,9 @@ -Documentation Workflow | Community Health Toolkit +Documentation Workflow | Community Health Toolkit

    Documentation Workflow

    How to contribute to the CHT tools and documentation

    Getting Started

    Anyone can contribute to CHT documentation by opening an issue in the cht-docs repo or by using the “Edit this page” or “Create documentation issue” links in the upper right corner of your window.

    Basics

    • It is helpful to be comfortable with git and GitHub to contribute to the CHT community.
    • The documentation source is in GitHub. The content pages are in the /content/en/ directory.
    • Documentation is written in Markdown.
    • The CHT site build uses Hugo. You can also setup a local clone.

    Writing Documentation

    A high degree of importance is put on consistency and usability of CHT documentation so that it is accessible and understood by a wide audience. The CHT documentation style guide will help to write documentation in the most consistent and useful way.

    Commits to GitHub

    The main branch is main which must be kept stable since it is deployed to the doc site. All documentation changes should be done in a branch with a Pull Request when ready for review. This means that a maintainer has signed off on the change before it hits the main branch.

    Format your commit messages according to the Git convention where the first line should be a short title/summary (50 characters or so) with more details in a separate paragraph (if needed).

    Documentation Workflow

    How to contribute to the CHT tools and documentation

    Getting Started

    Anyone can contribute to CHT documentation by opening an issue in the cht-docs repo or by using the “Edit this page” or “Create documentation issue” links in the upper right corner of your window.

    Basics

    • It is helpful to be comfortable with git and GitHub to contribute to the CHT community.
    • The documentation source is in GitHub. The content pages are in the /content/en/ directory.
    • Documentation is written in Markdown.
    • The CHT site build uses Hugo. You can also setup a local clone.

    Writing Documentation

    A high degree of importance is put on consistency and usability of CHT documentation so that it is accessible and understood by a wide audience. The CHT documentation style guide will help to write documentation in the most consistent and useful way.

    Commits to GitHub

    The main branch is main which must be kept stable since it is deployed to the doc site. All documentation changes should be done in a branch with a Pull Request when ready for review. This means that a maintainer has signed off on the change before it hits the main branch.

    Format your commit messages according to the Git convention where the first line should be a short title/summary (50 characters or so) with more details in a separate paragraph (if needed).

    Creating a Pull Request

    When your branch is ready for review, create a Pull Request. If you know who you’d like to review the PR, you can assign them directly. If you are unsure, you can leave it to the maintainers to handle the PR.

    Reviewing Pull Requests

    In general, reviewers should:

    1. Read the PR description to understand the changes made, as well as any linked issues
    2. Review any comments by other reviewers
    3. Select the Files changed tab to see the files and lines changed
    4. Click the + beside the line you want to comment on. To select multiple lines at once, click the + of the top line of the selection, drag down to the bottom line, and release.
    5. Add any comments you have about the line and click either Add single comment (if you want to post the comment without a review) or Start a review (if you have multiple comments to make).
    6. When finished, click Review changes at the top of the page. Here, you can add a summary of your review, approve the PR, comment or request changes as needed.
    7. Once all comments have been resolved, or changes are satisfactory, Merge pull request to complete the updates, and delete the branch.
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/index.html b/contribute/index.html index dd194834d1..f3cf4425f5 100644 --- a/contribute/index.html +++ b/contribute/index.html @@ -1,9 +1,9 @@ -Contributing to the Community Health Toolkit | Community Health Toolkit +Contributing to the Community Health Toolkit | Community Health Toolkit
    \ No newline at end of file diff --git a/contribute/index.xml b/contribute/index.xml index a97056e251..dd4fc58359 100644 --- a/contribute/index.xml +++ b/contribute/index.xml @@ -1 +1,4 @@ -Community Health Toolkit – Contributing to the Community Health Toolkithttps://docs.communityhealthtoolkit.org/contribute/Recent content in Contributing to the Community Health Toolkit on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file +Contributing to the Community Health Toolkit on Community Health Toolkithttps://docs.communityhealthtoolkit.org/contribute/Recent content in Contributing to the Community Health Toolkit on Community Health ToolkitHugo -- gohugo.ioenCHT Technology Radarshttps://docs.communityhealthtoolkit.org/contribute/tech-radar/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/tech-radar/It is essential for a development toolkit such as the Community Health Toolkit to constantly improve and keep track with the latest useful innovations. It is important to openly look for innovations and new technologies and to question established technologies and methods every now and then. +To enhance visibility and clarity on the technology choices, the technological strategy, and the available CHT features and tools, we leverage a framework called Technology Radar.Code of Conducthttps://docs.communityhealthtoolkit.org/contribute/code-of-conduct/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code-of-conduct/All maintainers and contributors in this community are required to act according to the following Code of Conduct. These guidelines help steer our interactions and help us provide and ensure a safe environment for everyone. +Our Standards Examples of behavior that contributes to creating a positive environment include: +Using welcoming and inclusive language Being respectful of differing viewpoints and experiences Gracefully accepting constructive criticism Focusing on what is best for the community Showing empathy towards other community members Examples of unacceptable behavior by participants include: \ No newline at end of file diff --git a/contribute/medic/index.html b/contribute/medic/index.html index 7038b12af5..b36188a9ea 100644 --- a/contribute/medic/index.html +++ b/contribute/medic/index.html @@ -1,9 +1,9 @@ -At Medic | Community Health Toolkit +At Medic | Community Health Toolkit

    At Medic

    Compilation of guidelines and product development processes used at Medic

    Medic works with CHT partners and health workers to design, build, and maintain the Community Health Toolkit and its open source tools. Medic’s Product team manages the entire software development life-cycle to understand problems, capture requirements, design and build modular software systems, and document everything along the way.

    This section contains internal processes, best practices and guidelines used so far in the Product team at Medic.

    Also, improvements and ideas for this doc site are always welcome!


    Product Development Process

    Explanation of the Continuous Discovery process used for product development at Medic

    Product Team Core Competencies

    These core competencies are intended to guide Product Teammates in how everyone shows up for each other every day. They are to be used in hiring new teammates, teammate feedback, and regular manager check ins.

    Onboarding

    Onboarding resources for new contributors

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/medic/index.xml b/contribute/medic/index.xml index c717e850af..5b801d1155 100644 --- a/contribute/medic/index.xml +++ b/contribute/medic/index.xml @@ -1,35 +1 @@ -Community Health Toolkit – At Medichttps://docs.communityhealthtoolkit.org/contribute/medic/Recent content in At Medic on Community Health ToolkitHugo -- gohugo.ioenContribute: Product Development Processhttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ -<p>The Medic Product Team operates in a way that is most commonly referred to as <a href="https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/continuous-discovery-overview/">Continuous Discovery</a>. That process helps us to focus on the impact of our work and keep teammates well-aligned with the people who use the CHT.</p> -<p>To allow teammates to truly learn from and empathize with those using the CHT, the Medic Product team has cross-functional subteams, called <a href="https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/focused-groups/">Focused Working Groups</a>, each aligned with different user types. Each group is focused on a specific set of users of the CHT and has an aligned roadmap. Roadmaps are made up of defined initiatives, which map to goals and strategies. There is a big emphasis on working towards outcomes (not only <em>outputs</em>). Product teammates work together, across disciplines, to understand opportunities for improvement, find solutions that will have an impact, and then build, deploy, and measure them.</p> -<p>As part of responsible maintainers of a software system, software engineers also dedicate time to performing <a href="https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/code-health/">Code Health</a> tasks. That ensures updates, upgrades, code enhancements, and small bugs are still handled while the team works on highly focused user-facing objectives. -Each Focused Working Group has defined Objectives and Key Results (OKRs), which set their focus for a given quarter. These, in turn, are derived from the Product Team&rsquo;s yearly Objective Goals Strategies Measurements (OGSMs). The <a href="https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/schedule-of-events/">Schedule of Activities</a> has more on the timing of all this through out any given year.</p>Contribute: Product Team Core Competencieshttps://docs.communityhealthtoolkit.org/contribute/medic/product-core-competencies/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-core-competencies/ -<table> -<thead> -<tr> -<th>Core Competency</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td><strong>Reliable</strong></td> -<td>Sets and communicates clear expectations about when something needs to/will be done and does it without prompting.</td> -</tr> -<tr> -<td><strong>Team Player</strong></td> -<td>Acts in the best interest of the team and actively looks for ways to help the team. Makes time to support teammates to be successful.</td> -</tr> -<tr> -<td><strong>Growth Mindset</strong></td> -<td>Always seeking to improve. Open minded, teachable, and coachable.</td> -</tr> -<tr> -<td><strong>Proactive</strong></td> -<td>Sees things that need doing and takes action to keep things moving and make the team successful.</td> -</tr> -<tr> -<td><strong>Effective Communicator</strong></td> -<td>Communicates regularly, openly, and effectively using the appropriate channels.</td> -</tr> -</tbody> -</table>Contribute: Onboardinghttps://docs.communityhealthtoolkit.org/contribute/medic/onboarding/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/ \ No newline at end of file +At Medic on Community Health Toolkithttps://docs.communityhealthtoolkit.org/contribute/medic/Recent content in At Medic on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file diff --git a/contribute/medic/onboarding/all-the-things/index.html b/contribute/medic/onboarding/all-the-things/index.html index 90841b09a4..d88ec2bc60 100644 --- a/contribute/medic/onboarding/all-the-things/index.html +++ b/contribute/medic/onboarding/all-the-things/index.html @@ -1,9 +1,9 @@ -All The Things | Community Health Toolkit +All The Things | Community Health Toolkit

    All The Things

    Wide range of topics and resources to support the onboarding

    This page is meant to serve as a point of conversation, with a wide range of topics to be discussed when joining Medic or starting as a contributor. Many things are not in any particular order. The goal is to convey a general “lay of the land” so someone starting can see a lot of what’s out there without having to be surprised each day as new things pop up.

    First, some general context…

    What is the CHT?

    Teams at Medic

    Meet the Medic team!

    • Product
    • Programs
    • Internal Operations
    • External Affairs

    Lifecycle of a CHT Application being built

    1. Programs team starts relationship with an organization.
    2. Service designers and app developers figure out how they want their system to work.
    3. App developers take latest version of the CHT and build the app for the organization.
    4. Android flavor deployed to get branded app onto CHW devices / deployment.
    5. Dashboards are set up in Klipfolio, Superset or Grafana.
    6. Monitoring and alerting are set up with the CHT Watchdog.
    7. Go!
    8. App developers make ongoing enhancements.
    9. App upgraded as new versions of CHT are available.

    CHT Academy

    • Fantastic way to understand how certain features of the CHT work.

    Now, all the things…

    People/Team

    Distributed team

    • Without some effort, it’s easy for things to feel lonely or isolated.
    • Default to asynchronous communication.
    • Respect teammate timezones (including your own!).

    Expensify

    • Reach out to Internal Operations team for guidance on how to submit expenses and get refunded. When submitting expenses, follow up to make sure things get through.

    Funding

    • Restricted - Clicktime. Getting our ClickTime timesheets submitted on time is vital to source the projects adequately. If necessary, take extra steps to remind yourself of submitting these on a monthly basis. If you have any question about filling in the reports properly, reach out to the finance team.
    • Unrestricted

    Travel

    • Team Meetups are a great way to build relations with your team! These are usually planned weeks ahead; if you feel comfortable joining, please do!
    • Focused Working Groups team members may sometimes organize in-person meetups to meet with the people they serve. It’s highly recommended to join those trips to get more connected to the team and the mission!

    Meetings

    • There a few calls where you will be required to join. We know that depending on your timezone, you might need to adjust your calendar to be able to attend and we provide great flexibility to do so.
    • Organization-wide calls are recorded.
    • No meetings on Fridays, as we consider Fridays as Deep Work days!
    • Retrospective sessions
    • Daily Standups available in 2 timezones (pick the one that suits you best)
    • Weekly Focused Working Groups meetings
    • Weekly 1-to-1s with your manager

    Process

    Development

    GitHub

    Quality Assistance

    • High emphasis on automation
    • We moved from manual AT (acceptance testing) and release testing to fully automated
    • We leverage quality assistance
      • Faster start-to-live
      • Avoiding silos and shifting of responsibilities (coding and quality).

    SRE (Site Reliability Engineering)

    • Support
    • Ticketing system: only GitHub
    • Not on-call
    • We’re offline first, so not every outage calls for immediate action/resolution.

    Product

    CHT Forum

    • We keep the forum active. It’s a great place to talk with people working with the CHT.
    • Encourage teammates to post and answer questions there instead of Slack when the community might benefit
    • Expecting you to be proactive and support the team with checking forum posts and helping when questions arise

    Partners

    • Medic-hosted
    • Self-hosted
    • Technical partners

    Product Development Process

    Technology Radars

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/medic/onboarding/daily-updates/index.html b/contribute/medic/onboarding/daily-updates/index.html index 8d02ef41e6..8701ba443c 100644 --- a/contribute/medic/onboarding/daily-updates/index.html +++ b/contribute/medic/onboarding/daily-updates/index.html @@ -1,9 +1,9 @@ -Daily Updates | Community Health Toolkit +Daily Updates | Community Health Toolkit

    Daily Updates

    How the Product Team shares daily updates

    What

    In our fully remote environment, staying connected and informed is crucial. One of the ways we can ensure transparency, and collaboration, and keep everyone in the loop is by utilizing the #product-dailies channel in Slack for sharing daily updates.

    Who

    All the Product Team members are expected to share daily updates in Slack (#product-dailies channel). Due to the nature of their work, Leadership Product colleagues might share less regular, but more consolidated updates.

    Why are daily updates essential?

    Here’s why it’s relevant:

    • Transparency: By sharing our daily activities, progress, and challenges, we create a transparent work environment. It allows everyone to understand what’s happening across the team, fostering trust and alignment.

    • Collaboration: Daily updates enable us to collaborate more effectively. When we know what each team member is working on, it’s easier to identify opportunities for support, provide feedback, or even discover potential collaborations.

    • Accountability: Sharing daily updates holds us all accountable for our tasks and goals. It helps us track progress and ensure that we’re moving forward on our projects. Plus, it motivates us to stay focused and productive.

    • Remote Communication: In a fully remote setup, communication can sometimes be a challenge. The #product-dailies channel serves as a centralized hub for communication, making it easier for us to stay connected despite the physical distance.

    • Celebrating Wins and Learning from Challenges: Sharing daily updates isn’t just about reporting tasks; it’s also about celebrating our wins, big or small, and learning from the challenges we encounter. It allows us to acknowledge achievements, share best practices, and support each other in overcoming obstacles.

    • Improves team morale: Team status updates don’t single out blocked team members. Plus, everyone feels like they are contributing to the team.

    By embracing the #product-dailies channel and actively participating in sharing our daily updates, we strengthen our team collaboration, enhance transparency, and ultimately drive our collective success.

    What this looks like in practice?

    Daily updates in the #product-dailies Slack channel are mandatory for the whole Product Team.

    You can find some examples of prompts to answer in your daily update below (Feel free to answer how many you wish. Also, feel free to find another prompts that make more sense to you at the moment when you write your update):

    • Are there any blockers or impediments preventing you from doing your work?
    • What are your commitments for the day? (if you choose to share your update at the beginning of your day)
    • What were your achievements of the day? (if you choose to share your update at the end of your day)

    Bad update vs. good update examples

    Bad updateGood update
    Stuck on the login bug all day.Investigated a login bug that prevented authentication; the root cause is that Safari doesn’t support the validation library. Tomorrow I’m going to find a solution that supports all browsers and add automation tests.
    I had 8 meetings today and didn’t have time to do anything else.The 1:1s with the engineers today were so exciting! We are planning the Q2 Goals, and we had interesting discussions on how to separate Goals from Grow areas. We finally agreed to use an approach inspired by Lattice’s suggestion (with a link). I will use the same approach to set up my Goals tomorrow.
    Continuing to work on the same thing as yesterday.Making progress on [ticket number], today I addressed the testing, by adding 75% of unit test coverage of the new feature.
    Hey team, today was pretty busy. I did some coding, had a meeting, and did some research. I’m calling it a day nowToday, I completed authentication logic for backend API endpoints and fixed a token validation bug. Collaborated with Roman to refine the user login interface, aligning it with our styling guidelines. Also, provided development updates in the squad traffic lights meeting.
    - Did Easy Llama course
    - Met with Phil
    - Worked on #8819
    - Did Easy Llama course
    - Talked with Phil about project boards and Product/Programs work. We settled on having one board and labels on tickets to help Programs teammates view/filter as they need to.
    - Worked on #8819 and got the refactoring done on the UI code. Next up will be to add a few fields to the API so I can show the data on the screen. UI tests have been frustrating taking a long time to run locally

    Frequently Asked Questions

    Q: What if the channel becomes too noisy?

    A: We all work towards the same goals and mission, and the daily updates you share help everyone feel assured that they are on the same page and reduce the chances of misunderstandings and miscommunication. For that reason and all the benefits we listed in the Why section, we can’t consider updates noise, but bits and pieces of the amazing work we do together! You can always skim through updates that are too technical, for example, or that you are already aware of.

    Q: Why would other teams care about my updates?

    A: Because we should collaborate and care about our team, not just about our individual work. Your updates may bring new opportunities, ideas, concerns, feedback, and more. It also aligns well with being a good communicator who’s helping us improve the team’s accountability.

    Q: Are the daily updates mandatory?

    A: Yes, they are mandatory and also a brilliant opportunity to engage your colleagues, to motivate them, and enhance our collective output. This isn’t just for authority’s sake – if everyone takes daily updates seriously, we will be more productive, aligned and connected as a team.

    Q: What happens if I don’t share updates daily?

    A: The goal is maintaining efficient collaboration and ensuring we’re all moving towards our objectives. By not communicating with the team, we are affecting collaboration and transparency, at the very least. For each of us, our work is not only about the technical stuff but also about being a good team player, effective communicator, and more. Failure to provide updates may affect your evaluation, potentially impacting opportunities for growth and advancement within the team.

    Q: How often should I post updates in the daily channel?

    A: At least once per day, but for some people, more than one update could also work. For instance, their commitments for the day and then what they actually achieved.

    Q: What is the best time to post updates on the daily channel?

    A: Whatever works best for you. But, usually, posting the update could be either one of the first activities you complete or one of the last things you do before leaving for the day. Here’s some interesting reading on the topic.

    Q: How is this measured in my performance review?

    A: Managers will share regular feedback throughout the year about the quality and consistency of your updates. Preferably, by the time performance reviews happen, you will already have received plenty of feedback and identified areas for growth that you committed to working on. Effective communication and reliability are part of our core competencies, and accountability is a big deal we are trying to get better at as a team, so status update consistency will be part of the calibration toward those competencies.

    Q: How detailed should my updates be?

    A: We shared some examples of updates and some prompts that can help you draft your daily updates. Keep in mind that status updates demand clarity and specific information that is valuable and informs others.

    Q: What should I do if I have no significant updates for the day?

    A: If you don’t have significant updates about what you did, you can always share updates about how you feel about it. Maybe a task was simply straightforward and it doesn’t require many updates. Or maybe there were a number of difficult, unforeseen issues that came up, and you didn’t progress as much as you wanted. Maybe you had a super productive day. Tell the team how you feel; the channel is a safe space to speak honestly! (Pro tip: Feelings Wheel)

    Q: Is it okay to post challenges or roadblocks in the #product-dailies?

    A: Absolutely, someone can help you after reading your update. If you think of someone in particular who could give you a hand, just tag them!

    Q: Should I respond to other team members’ updates in the daily channel?

    A: If they tag you or you want to reply to something specific about their updates, it is highly encouraged to do so! You can also react to their messages with an emoji if you want to.

    Q: What sort of feedback should I expect on my updates?

    A: Managers should provide direct, regular, both positive and constructive and helpful feedback on status updates provided by the team members.

    Q: I always forget to share my daily updates. How can I remember to share them?

    A: Tips for remembering to share the daily updates:

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/medic/onboarding/index.html b/contribute/medic/onboarding/index.html index 016cacfc48..e4c28e11dd 100644 --- a/contribute/medic/onboarding/index.html +++ b/contribute/medic/onboarding/index.html @@ -1,9 +1,9 @@ -Onboarding | Community Health Toolkit +Onboarding | Community Health Toolkit

    Onboarding

    Onboarding resources for new contributors

    Product Team

    Introduction to Product Team

    All The Things

    Wide range of topics and resources to support the onboarding

    Technical Resources

    Resources to get started as a contributor to the CHT

    Team Meetings

    How the Product full-team meetings run

    Daily Updates

    How the Product Team shares daily updates

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/medic/onboarding/index.xml b/contribute/medic/onboarding/index.xml index b949095b22..888f75258d 100644 --- a/contribute/medic/onboarding/index.xml +++ b/contribute/medic/onboarding/index.xml @@ -1,418 +1,3 @@ -Community Health Toolkit – Onboardinghttps://docs.communityhealthtoolkit.org/contribute/medic/onboarding/Recent content in Onboarding on Community Health ToolkitHugo -- gohugo.ioenContribute: Product Teamhttps://docs.communityhealthtoolkit.org/contribute/medic/onboarding/product-team/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/product-team/ -<p>The goal of this page is to convey a general “lay of the land” so someone starting can see a lot of what’s out there related to Product Team without having to be surprised each day as new things pop up.</p> -<h2 id="about-product-team">About Product Team</h2> -<p>The Product Team manages the entire software development life-cycle to understand problems, capture requirements, design and build modular software systems, and document everything along the way. -We achieve this by working with CHT partners and health workers to design, build, and maintain the Community Health Toolkit and its open source tools.</p> -<h2 id="composition">Composition</h2> -<p>The Product Team comprises a Community Team and 4 Focused Working Groups, each focusing on outcomes to serve different people.</p> -<h3 id="focused-working-groups">Focused Working Groups</h3> -<p>The Product Team operates in a way that is most commonly referred to as Continuous Discovery. That process helps us to focus on the impact of our work and keep teammates well-aligned with the people who use the CHT. Read more about the process <a href="https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/">here</a>.</p> -<p>Each Focused Working Group serves to different user types to understand opportunities for improvement, find solutions that will have an impact, and then build, deploy, and measure them. Find out more about the different Focused Working Groups <a href="https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/focused-groups/">here</a>.</p> -<h3 id="community-team">Community Team</h3> -<p>The community team builds and supports the CHT community of practitioners to ensure that community members can actively use and contribute to the CHT.</p> -<p>Key users: Core Developers, App Developers, digital health implementers, research organizations, ministries of health, academic institutions and individual contributors.</p> -<p>Team members:</p> -<ul> -<li>Director of Community.</li> -<li>Technical Writer.</li> -<li>Developer Advocate.</li> -<li>Community Manager.</li> -</ul> -<p>CHT Round-up calls: Monthly calls aimed at engaging the CHT community, sharing CHT product updates and getting valuable feedback from the CHT community. See previous Round-up calls <a href="https://www.youtube.com/playlist?list=PLutu6_ZOg77dO_aqf4Zh1ck59YnHzWuPI">here</a>.</p>Contribute: All The Thingshttps://docs.communityhealthtoolkit.org/contribute/medic/onboarding/all-the-things/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/all-the-things/ -<p>This page is meant to serve as a point of conversation, with a wide range of topics to be discussed when joining Medic or starting as a contributor. Many things are not in any particular order. The goal is to convey a general “lay of the land” so someone starting can see a lot of what’s out there without having to be surprised each day as new things pop up.</p> -<h2 id="first-some-general-context">First, some general context…</h2> -<h3 id="what-is-the-cht">What is the CHT?</h3> -<ul> -<li>Why the <a href="https://docs.communityhealthtoolkit.org/why-the-cht/">CHT (Community Health Toolkit)</a>?</li> -<li>The <a href="https://docs.communityhealthtoolkit.org/core/">CHT Core Framework</a> - App that can be accessed in the <a href="https://docs.communityhealthtoolkit.org/core/overview/architecture/#cht-web-application">browser</a> or as <a href="https://docs.communityhealthtoolkit.org/core/overview/pwa/">PWA</a> or <a href="https://docs.communityhealthtoolkit.org/core/overview/architecture/#cht-android">native Android app</a>.</li> -<li><a href="https://docs.communityhealthtoolkit.org/core/overview/offline-first/">Offline-First</a> - for real</li> -<li><a href="https://docs.communityhealthtoolkit.org/core/overview/architecture/">Architecture of the CHT</a></li> -<li><a href="https://docs.communityhealthtoolkit.org/">Community</a> of people and organizations. This is where we document all the things about the CHT Framework. Bookmark it as you primary source of reference.</li> -</ul> -<h3 id="teams-at-medic">Teams at Medic</h3> -<p>Meet the <a href="https://medic.org/team/">Medic team</a>!</p> -<ul> -<li><a href="https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/product-team/">Product</a></li> -<li>Programs</li> -<li>Internal Operations</li> -<li>External Affairs</li> -</ul> -<h3 id="lifecycle-of-a-cht-applicationhttpsdocscommunityhealthtoolkitorgrunning-programscht-lifecycle-being-built">Lifecycle of a <a href="https://docs.communityhealthtoolkit.org/running-programs/#cht-lifecycle">CHT Application</a> being built</h3> -<ol> -<li>Programs team starts relationship with an organization.</li> -<li>Service designers and app developers figure out how they want their system to work.</li> -<li>App developers take latest version of the CHT and build the app for the organization.</li> -<li>Android flavor deployed to get branded app onto CHW devices / deployment.</li> -<li>Dashboards are set up in Klipfolio, Superset or Grafana.</li> -<li>Monitoring and alerting are set up with the CHT Watchdog.</li> -<li>Go!</li> -<li>App developers make ongoing enhancements.</li> -<li>App upgraded as new versions of CHT are available.</li> -</ol> -<h3 id="cht-academy">CHT Academy</h3> -<ul> -<li><a href="https://academy.communityhealthtoolkit.org/">Fantastic way</a> to understand how certain features of the CHT work.</li> -</ul> -<h2 id="now-all-the-things">Now, all the things…</h2> -<h3 id="peopleteam">People/Team</h3> -<h4 id="distributed-team">Distributed team</h4> -<ul> -<li>Without some effort, it’s easy for things to feel lonely or isolated.</li> -<li>Default to asynchronous communication.</li> -<li>Respect teammate timezones (including your own!).</li> -</ul> -<h4 id="expensify">Expensify</h4> -<ul> -<li>Reach out to Internal Operations team for guidance on how to submit expenses and get refunded. When submitting expenses, follow up to make sure things get through.</li> -</ul> -<h4 id="funding">Funding</h4> -<ul> -<li>Restricted - Clicktime. Getting our ClickTime timesheets submitted on time is vital to source the projects adequately. If necessary, take extra steps to remind yourself of submitting these on a monthly basis. If you have any question about filling in the reports properly, reach out to the finance team.</li> -<li>Unrestricted</li> -</ul> -<h4 id="travel">Travel</h4> -<ul> -<li>Team Meetups are a great way to build relations with your team! These are usually planned weeks ahead; if you feel comfortable joining, please do!</li> -<li>Focused Working Groups team members may sometimes organize in-person meetups to meet with the people they serve. It&rsquo;s highly recommended to join those trips to get more connected to the team and the mission!</li> -</ul> -<h4 id="meetings">Meetings</h4> -<ul> -<li>There a few calls where you will be required to join. We know that depending on your timezone, you might need to adjust your calendar to be able to attend and we provide great flexibility to do so.</li> -<li>Organization-wide calls are recorded.</li> -<li>No meetings on Fridays, as we consider Fridays as Deep Work days!</li> -<li>Retrospective sessions</li> -<li>Daily Standups available in 2 timezones (pick the one that suits you best)</li> -<li>Weekly Focused Working Groups meetings</li> -<li>Weekly 1-to-1s with your manager</li> -</ul> -<h3 id="process">Process</h3> -<h4 id="development">Development</h4> -<ul> -<li><a href="https://docs.communityhealthtoolkit.org/contribute/code/workflow/">Current development process</a>. Keep in mind to involve <a href="https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/quality-assistance/">Quality Assistance</a> from the start.</li> -<li><a href="https://docs.communityhealthtoolkit.org/contribute/code/releasing/">Releasing</a></li> -<li>Backwards compatibility matters a lot, so CHWs can keep using the app and delivery care to their community without interruptions.</li> -<li>It can feel slow at times, but we’re making a lot of progress here. See below about how Focused Working Groups work.</li> -<li>Quality matters a lot!</li> -<li><a href="https://docs.communityhealthtoolkit.org/core/overview/data-flows-for-analytics/">Data Flow</a></li> -<li><a href="https://docs.communityhealthtoolkit.org/hosting/monitoring/">Monitoring &amp; Alerting</a></li> -<li>The main repositories to look at: -<ul> -<li><a href="https://github.com/medic/cht-core">cht-core</a></li> -<li><a href="https://github.com/medic/cht-conf">cht-conf</a></li> -<li><a href="https://github.com/medic/cht-android">cht-android</a></li> -<li><a href="https://github.com/medic/cht-sync">cht-sync</a></li> -<li><a href="https://github.com/medic/cht-pipeline">cht-pipeline</a></li> -<li><a href="https://github.com/medic/couch2pg">couch2pg</a></li> -<li><a href="https://github.com/medic/cht-watchdog">cht-watchdog</a></li> -</ul> -</li> -<li>Continuous Integration (CI) with GitHub Actions</li> -<li>No Continuous Deployment, as no SaaS setup</li> -<li>Sonar for <a href="https://docs.communityhealthtoolkit.org/contribute/code/static-analysis/">Code Static Analysis</a></li> -<li><a href="https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/technical-resources/">Technical resources &amp; learning material</a> for CHT contributors.</li> -</ul> -<h4 id="github">GitHub</h4> -<ul> -<li>Tons of things happen here.</li> -<li>Recommendation: <a href="https://medic.slack.com/archives/C024KTGRW/p1617308776092600">Set up your reminders/notifications</a></li> -<li>A few important boards: -<ul> -<li><a href="https://github.com/orgs/medic/projects/38">SRE Engineering</a></li> -<li><a href="https://github.com/orgs/medic/projects/19">SRE Support</a></li> -<li><a href="https://github.com/orgs/medic/projects/134/views/11">Ecosystem Workstream</a></li> -<li><a href="https://github.com/orgs/medic/projects/134/views/3">Allies Workstream</a></li> -<li><a href="https://github.com/orgs/medic/projects/134/views/2">Care Teams Workstream</a></li> -<li><a href="https://github.com/orgs/medic/projects/134/views/26">Infrastructure Workstream</a></li> -<li><a href="https://github.com/orgs/medic/projects/134/views/12">Test Automation</a></li> -</ul> -</li> -</ul> -<h4 id="quality-assistance">Quality Assistance</h4> -<ul> -<li>High emphasis on automation</li> -<li>We moved from manual AT (acceptance testing) and release testing to fully automated</li> -<li>We leverage <a href="https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/quality-assistance/">quality assistance</a> -<ul> -<li>Faster start-to-live</li> -<li>Avoiding silos and shifting of responsibilities (coding and quality).</li> -</ul> -</li> -</ul> -<h4 id="sre-site-reliability-engineering">SRE (Site Reliability Engineering)</h4> -<ul> -<li>Support</li> -<li>Ticketing system: only GitHub</li> -<li>Not on-call</li> -<li>We’re offline first, so not every outage calls for immediate action/resolution.</li> -</ul> -<h3 id="product">Product</h3> -<h4 id="cht-forumhttpsforumcommunityhealthtoolkitorg"><a href="https://forum.communityhealthtoolkit.org/">CHT Forum</a></h4> -<ul> -<li>We keep the forum active. It&rsquo;s a great place to talk with people working with the CHT.</li> -<li>Encourage teammates to post and answer questions there instead of Slack when the community might benefit</li> -<li>Expecting you to be proactive and support the team with checking forum posts and helping when questions arise</li> -</ul> -<h4 id="partners">Partners</h4> -<ul> -<li>Medic-hosted</li> -<li>Self-hosted</li> -<li>Technical partners</li> -</ul> -<h4 id="product-development-process">Product Development Process</h4> -<ul> -<li>At Medic, we follow <a href="https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/continuous-discovery-overview/">Continuous Discovery</a> as a <a href="https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/">Product Development Process</a> -<ul> -<li>Trying to follow closely with the <a href="https://www.producttalk.org/2021/05/continuous-discovery-habits/">Continuous Discovery Habits</a> book by Teresa Torres</li> -<li><a href="https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/focused-groups/">Focused Working Groups</a> within the product team to focus on specific groups of users -<ul> -<li>Allies</li> -<li>Care Teams</li> -<li>Infrastructure</li> -<li>Ecosystem</li> -</ul> -</li> -</ul> -</li> -</ul> -<h4 id="technology-radars">Technology Radars</h4> -<ul> -<li>A <a href="https://docs.communityhealthtoolkit.org/contribute/tech-radar/">Technology Radar</a> is a compilation of technologies and their adoption status in the context of the CHT. When in doubt of using a certain technology or feature of the CHT, check the radars for their adoption status. -<ul> -<li><a href="https://docs.communityhealthtoolkit.org/cht-tech-radar-contributors/index.html">CHT Technology Radar for Contributors</a></li> -<li><a href="https://docs.communityhealthtoolkit.org/cht-tech-radar-implementers/index.html">CHT Technology Radar for Implementers</a>.</li> -</ul> -</li> -</ul>Contribute: Technical Resourceshttps://docs.communityhealthtoolkit.org/contribute/medic/onboarding/technical-resources/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/technical-resources/ -<div class="alert alert-info" role="alert"> -<h4 class="alert-heading">Note</h4> -This page also contains Medic internal documentation links that are only accessible to Medic team members. -</div> -<p>This page contains materials that will help a CHT Contributor to master all the technical details they need to be successful when building tools related to the CHT. For Medic team members, this content is complimentary on a technical level to the Onboarding documents shared by the Internal Operations team. -Keep in mind that this is a living document, and every contributor is encouraged to add to it when identifying learning opportunities that can set them up for success with the CHT. The main goal here is to provide the technical people who are new to the CHT with a list of resources that could be beneficial for them in both the short and long term. However, the expectation is not to complete every course listed in this document but instead to share some resources that other contributors found helpful. It is valid if you prefer to use other training material, as long as you cover the skill/topic during the onboarding. That means you will need to learn JavaScript, TypeScript, Docker, CouchDB, and others mentioned in the “Skills” column, as you will use them in your day-to-day activities, and the list provided in the “Resources” column is meant to support you in this. It is OK if you prefer to learn with alternative resources.</p> -<div class="alert alert-info" role="alert"> -<h4 class="alert-heading">Note</h4> -Some of the suggestions below could be for internal courses and/or material created and maintained by other teammates at Medic, free resources on the internet, or paid courses, which for Medic teammates could be considered as part of the <a href="https://www.notion.so/Professional-Development-f5fc299ecf984dacbd65100af100ed14">Professional Development Benefit</a> at Medic. -</div> -<h2 id="exercises">Exercises</h2> -<p>There is a Slack channel <em>#exercism</em> where engineers and tech enthusiasts meet and discuss JavaScript and TypeScript exercises from <a href="https://exercism.org/">exercism.org</a> on a weekly basis. The primary goal of this channel is to discuss with your teammates and have a fun, open discussion on how the provided solutions can be improved, while also having the opportunity to connect with people from other teams. Even though this is optional, it is highly encouraged to join and participate frequently.</p> -<h2 id="technical-resources">Technical Resources</h2> -<table> -<thead> -<tr> -<th>Skill</th> -<th>Resources</th> -</tr> -</thead> -<tbody> -<tr> -<td><strong>CHT Local Environment and Docs</strong></td> -<td><a href="https://docs.communityhealthtoolkit.org/contribute/code/core/dev-environment/">Set up your local development environment</a> <br> <a href="https://docs.communityhealthtoolkit.org/">Go through CHT Documentation</a> <br> <a href="https://docs.communityhealthtoolkit.org/contribute/code/workflow/">Get familiar with our Development Workflow</a> <br><a href="https://docs.communityhealthtoolkit.org/core/overview/architecture/">Architecture of CHT Instances</a> <br> <br> Internal resources: <br> <a href="https://drive.google.com/drive/folders/1PTe8RH59TPBNYKoKzlZ_ZwMlQedKRGlx">App building for techies crash course with Ashley Jones</a> <br> <a href="https://www.notion.so/medicmobile/CHT-Forum-Internal-Guide-c2d1988a116244b6b17b3aea284ff8ee">Where to ask questions about the CHT?</a></td> -</tr> -<tr> -<td><strong>JavaScript</strong></td> -<td><a href="https://eloquentjavascript.net/">Eloquent JavaScript</a> <br> <a href="https://www.udacity.com/course/asynchronous-javascript-requests--ud109">Asynchronous JavaScript Requests</a> <br> <a href="https://github.com/getify/You-Dont-Know-JS">You Don&rsquo;t Know JS</a></td> -</tr> -<tr> -<td><strong>TypeScript</strong></td> -<td><a href="https://www.typescriptlang.org/docs/">TypeScript Documentation</a></td> -</tr> -<tr> -<td><strong>Docker</strong></td> -<td><a href="https://twitter.com/iximiuz/status/1423984739514454033?s=21">Containers and Docker Mega Thread</a> <br> <a href="https://www.udemy.com/course/docker-mastery/">Docker mastery course</a> <br> <a href="https://diamol.net/">Learn Docker in a month of lunches</a> <br> <a href="https://codewithmosh.com/p/the-ultimate-docker-course">The Ultimate Docker course</a></td> -</tr> -<tr> -<td><strong>CouchDB and PouchDB</strong></td> -<td><a href="https://docs.couchdb.org/en/stable/intro/index.html">CouchDB 2.3 introduction user guide docs</a> <br> <a href="https://www.udemy.com/course/understanding-couchdb/">Understanding CouchDB 3</a> <br> <a href="https://pouchdb.com/guides/">Introduction to PouchDB</a></td> -</tr> -<tr> -<td><strong>NodeJS and NPM</strong></td> -<td><a href="https://www.udemy.com/course/the-complete-nodejs-developer-course-2/">The Complete Node.JS developer course</a> <br> <a href="https://www.udemy.com/course/understanding-npm/">Understanding NPM - Node.js Package Manager</a></td> -</tr> -<tr> -<td><strong>Angular</strong></td> -<td><a href="https://www.udemy.com/course/learning-angular/">Angular 12 - Complete Beginner&rsquo;s Guide 2021</a></td> -</tr> -<tr> -<td><strong>Offline First</strong></td> -<td><a href="https://www.youtube.com/playlist?list=PLAwxTw4SYaPmTSxtOWyJVKTUaNBGze2ed">Offline Web Applications</a></td> -</tr> -<tr> -<td><strong>Web Architecture</strong></td> -<td><a href="https://medium.com/storyblocks-engineering/web-architecture-101-a3224e126947">Web Architecture 101</a></td> -</tr> -<tr> -<td><strong>Web Browsers</strong></td> -<td><a href="https://developer.chrome.com/blog/inside-browser-part1/">Inside look at modern web browsers, part I</a> <br> <a href="https://developer.chrome.com/blog/inside-browser-part2/">Inside look at modern web browsers, part II</a> <br> <a href="https://developer.chrome.com/blog/inside-browser-part3/">Inside look at modern web browsers, part III</a> <br> <a href="https://developer.chrome.com/blog/inside-browser-part4/">Inside look at modern web browsers, part IV</a></td> -</tr> -<tr> -<td><strong>Web Performance</strong></td> -<td><a href="https://hpbn.co/">High Performance Browser Networking</a></td> -</tr> -<tr> -<td><strong>PostgreSQL</strong></td> -<td><a href="https://www.postgresqltutorial.com/">PostgreSQL Tutorial</a> <br> <a href="https://www.postgresqltutorial.com/postgresql-cheat-sheet/">PostgreSQL Cheat Sheet</a> <br> <a href="https://www.postgresqltutorial.com/postgresql-views/">PostgreSQL Views Tutorial</a> <br> <a href="https://www.postgresql.org/docs/current/tutorial-views.html">PostgreSQL Views Documentation</a> <br> <a href="https://www.postgresqltutorial.com/postgresql-tutorial/postgresql-cte/">Common Table Expressions (CTEs)</a> <br> <a href="https://www.postgresql.org/docs/current/queries-with.html">PostgreSQL Queries with CTEs Documentation</a></td> -</tr> -<tr> -<td><strong>Superset</strong></td> -<td><a href="https://superset.apache.org/docs/intro/">Introduction to Superset</a> <br> <a href="https://superset.apache.org/docs/creating-charts-dashboards/creating-your-first-dashboard/">Creating Your First Dashboard</a></td> -</tr> -<tr> -<td><strong>DBT</strong></td> -<td><a href="https://docs.getdbt.com/docs/core/installation">DBT Core Manual Installation Guide</a> <br> <a href="https://www.youtube.com/playlist?list=PLohMhitTY9xuEVMpLG3xXhsKG9j2XCTeF">DBT Video Playlist</a></td> -</tr> -</tbody> -</table>Contribute: Team Meetingshttps://docs.communityhealthtoolkit.org/contribute/medic/onboarding/team-meetings/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/team-meetings/ -<h2 id="product-team-monthly-call">Product Team Monthly Call</h2> -<h3 id="why">Why</h3> -<p>Getting to see each other all in one place at the same time, even if only online, lets people get to be around each other, learn from each other, and hear other perspectives. We&rsquo;re real people! &hellip;terrific people too. This is a small space to spend some time around each other as a group. We&rsquo;re mostly trying to learn more about each other, celebrate recent successes, and cover the occasional big news or team-wide changes. It&rsquo;s mostly about being together to smile a bit.</p> -<h3 id="when">When</h3> -<p>Roughly the first Tuesday (or Wednesday, for New Zealand teammates) week of each month.</p> -<h3 id="who-is-there">Who is there</h3> -<p>The whole Product Team.</p> -<h3 id="who-facilitates-it">Who facilitates it</h3> -<p>Maybe you! We&rsquo;ll have a rotation of teammates leading the call. It doesn&rsquo;t mean you give a 2-hour speech at your teammates. It just means you help out with some basic logistics and guide us through the loose segments so it&rsquo;s not chaos.</p> -<h3 id="how-to-facilitate-it">How to facilitate it</h3> -<h4 id="preparations">Preparations</h4> -<ol> -<li>About 2 weeks before the call, open the <a href="https://docs.google.com/document/d/14AuJ7SerLuOPESBjQlJqpBtzwSAoVf5ykTT7fjyJBT0/edit#heading=h.tpmdaeagi9ap">Product Team Call</a> document and: -<ol> -<li>Add a new date token for &ldquo;question of the week&rdquo;.</li> -<li>Randomize the list of teammate names.</li> -<li>Put the name of whoever ended up first on the randomized list as the question of the week person.</li> -<li>Notify that person via Slack that they should think up a question and add it to the doc any time prior to the call. Special note: If we have a new teammate, they give to be first and set the question. Just modify the list to make it so.</li> -<li>The last randomized person on the list will be in charge of &ldquo;activity of the week&rdquo; (anything small and fun we can all do together. Past activities have included <a href="https://www.geoguessr.com/">GeoGuessr</a>, <a href="https://garticphone.com/">Gartic Phone</a>, and <a href="https://kahoot.com/">Kahoot trivia</a>).</li> -<li>Notify that person via Slack that they should find such an activity and be prepared to lead it for us all.</li> -</ol> -</li> -<li>Create a new Jamboard in the <a href="https://drive.google.com/drive/folders/1jV-8APqEQ85MOr1Irc4vRKRuKZOmI1r_">team meeting folder</a> -<ol> -<li>Make the Jamboard accessible for all teammates</li> -<li>Link it in the agenda under &ldquo;Celebrations and good fun stuff&rdquo;</li> -<li>Send a message to the whole team in Slack so they know the jamboard is available and can start putting fun things on it, and also that the agenda is open now for team-wide or org information to add.</li> -</ol> -</li> -</ol> -<h4 id="running-the-call-on-the-day-of">Running the call on the day-of</h4> -<ol> -<li>Start the call on time (mostly). We can&rsquo;t sit around forever, but this is mostly a fun thing, so it&rsquo;s ok if people are just chatting for a couple minutes while waiting for stragglers.</li> -<li>Say hello and all that.</li> -<li>Do the question of the week activity. -<ol> -<li>Sometimes people get the order mixed up. Be prepared to nudge people if needed.</li> -</ol> -</li> -<li>Move to the regular agenda. If there are special items, someone&rsquo;s name should be on it already.</li> -<li>Do the Jamboard.</li> -<li>Ask for someone to mark the cards as you call them out. Enough people seem to take joy in this, and it&rsquo;ll be a thing all on its own!</li> -<li>Note which card you&rsquo;re looking at, read it, and ask if anyone (or the author) wants to add more. Sometimes it&rsquo;s quick stuff, sometimes it&rsquo;s big fun things everyone wants to spend more time on. Cool.</li> -<li>Pass the mic to the activity of the week person.</li> -<li>Fin! (It&rsquo;s ok if we finish early. We need to try hard not to finish late; it&rsquo;s evening for some teammates!).</li> -</ol> -<h2 id="product-team-social-time">Product Team Social Time</h2> -<p>People appreciate social time, so we will use this weekly time to connect with each other rather than share updates. It is a perfect place to discuss a (non) technical issue you are encountering and ask for support from colleagues.</p> -<p>The Product Team Social Time is optional, but highly encouraged. You can choose the schedule that works best for you (2 calls on Mondays 10AM GMT and 9PM GMT).</p> -<h3 id="frequently-asked-questions">Frequently Asked Questions</h3> -<h4 id="q-what-if-im-alone-in-the-product-social-time">Q: What if I&rsquo;m alone in the product social time?</h4> -<p>A: You don’t need to wait for more than 5 or 10 minutes if no one else shows up.</p> -<h4 id="q-how-frequently-should-i-go-to-the-product-social-time">Q: How frequently should I go to the product social time?</h4> -<p>A: You are not required to join this call, but it is highly encouraged as we’d like teammates to connect not only via Slack or GitHub.</p> -<h4 id="q-is-the-product-social-time-considered-as-part-of-my-performance-review">Q: Is the product social time considered as part of my performance review?</h4> -<p>A: No, it is not considered a part of the performance review.</p>Contribute: Daily Updateshttps://docs.communityhealthtoolkit.org/contribute/medic/onboarding/daily-updates/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/daily-updates/ -<h2 id="what">What</h2> -<p>In our fully remote environment, staying connected and informed is crucial. One of the ways we can ensure transparency, and collaboration, and keep everyone in the loop is by utilizing the <em>#product-dailies</em> channel in Slack for sharing daily updates.</p> -<h2 id="who">Who</h2> -<p>All the Product Team members are expected to share daily updates in Slack (<em>#product-dailies</em> channel). Due to the nature of their work, Leadership Product colleagues might share less regular, but more consolidated updates.</p> -<h2 id="why-are-daily-updates-essential">Why are daily updates essential?</h2> -<p>Here&rsquo;s why it&rsquo;s relevant:</p> -<ul> -<li> -<p><strong>Transparency</strong>: By sharing our daily activities, progress, and challenges, we create a transparent work environment. It allows everyone to understand what&rsquo;s happening across the team, fostering trust and alignment.</p> -</li> -<li> -<p><strong>Collaboration</strong>: Daily updates enable us to collaborate more effectively. When we know what each team member is working on, it&rsquo;s easier to identify opportunities for support, provide feedback, or even discover potential collaborations.</p> -</li> -<li> -<p><strong>Accountability</strong>: Sharing daily updates holds us all accountable for our tasks and goals. It helps us track progress and ensure that we&rsquo;re moving forward on our projects. Plus, it motivates us to stay focused and productive.</p> -</li> -<li> -<p><strong>Remote Communication</strong>: In a fully remote setup, communication can sometimes be a challenge. The <em>#product-dailies</em> channel serves as a centralized hub for communication, making it easier for us to stay connected despite the physical distance.</p> -</li> -<li> -<p><strong>Celebrating Wins and Learning from Challenges</strong>: Sharing daily updates isn&rsquo;t just about reporting tasks; it&rsquo;s also about celebrating our wins, big or small, and learning from the challenges we encounter. It allows us to acknowledge achievements, share best practices, and support each other in overcoming obstacles.</p> -</li> -<li> -<p><strong>Improves team morale</strong>: Team status updates don’t single out blocked team members. Plus, everyone feels like they are contributing to the team.</p> -</li> -</ul> -<p>By embracing the <em>#product-dailies</em> channel and actively participating in sharing our daily updates, we strengthen our team collaboration, enhance transparency, and ultimately drive our collective success.</p> -<h2 id="what-this-looks-like-in-practice">What this looks like in practice?</h2> -<p>Daily updates in the <em>#product-dailies</em> Slack channel are <em>mandatory</em> for the whole Product Team.</p> -<p>You can find some examples of prompts to answer in your daily update below (Feel free to answer how many you wish. Also, feel free to find another prompts that make more sense to you at the moment when you write your update):</p> -<ul> -<li>Are there any blockers or impediments preventing you from doing your work?</li> -<li>What are your commitments for the day? (if you choose to share your update at the beginning of your day)</li> -<li>What were your achievements of the day? (if you choose to share your update at the end of your day)</li> -</ul> -<h3 id="bad-update-vs-good-update-examples">Bad update vs. good update examples</h3> -<table> -<thead> -<tr> -<th>Bad update</th> -<th>Good update</th> -</tr> -</thead> -<tbody> -<tr> -<td>Stuck on the login bug all day.</td> -<td>Investigated a login bug that prevented authentication; the root cause is that Safari doesn’t support the validation library. Tomorrow I’m going to find a solution that supports all browsers and add automation tests.</td> -</tr> -<tr> -<td>I had 8 meetings today and didn’t have time to do anything else.</td> -<td>The 1:1s with the engineers today were so exciting! We are planning the Q2 Goals, and we had interesting discussions on how to separate Goals from Grow areas. We finally agreed to use an approach inspired by Lattice&rsquo;s suggestion (with a link). I will use the same approach to set up my Goals tomorrow.</td> -</tr> -<tr> -<td>Continuing to work on the same thing as yesterday.</td> -<td>Making progress on [ticket number], today I addressed the testing, by adding 75% of unit test coverage of the new feature.</td> -</tr> -<tr> -<td>Hey team, today was pretty busy. I did some coding, had a meeting, and did some research. I&rsquo;m calling it a day now</td> -<td>Today, I completed authentication logic for backend API endpoints and fixed a token validation bug. Collaborated with Roman to refine the user login interface, aligning it with our styling guidelines. Also, provided development updates in the squad traffic lights meeting.</td> -</tr> -<tr> -<td>- Did Easy Llama course <br> - Met with Phil <br> - Worked on #8819</td> -<td>- <del>Did Easy Llama course</del> <br> - Talked with Phil about project boards and Product/Programs work. We settled on having one board and labels on tickets to help Programs teammates view/filter as they need to. <br> - Worked on #8819 and got the refactoring done on the UI code. Next up will be to add a few fields to the API so I can show the data on the screen. UI tests have been frustrating taking a long time to run locally</td> -</tr> -</tbody> -</table> -<h2 id="frequently-asked-questions">Frequently Asked Questions</h2> -<h3 id="q-what-if-the-channel-becomes-too-noisy">Q: What if the channel becomes too noisy?</h3> -<p>A: We all work towards the same goals and mission, and the daily updates you share help everyone feel assured that they are on the same page and reduce the chances of misunderstandings and miscommunication. For that reason and all the benefits we listed in the <a href="#why-are-daily-updates-essential">Why section</a>, we can’t consider updates noise, but bits and pieces of the amazing work we do together! You can always skim through updates that are too technical, for example, or that you are already aware of.</p> -<h3 id="q-why-would-other-teams-care-about-my-updates">Q: Why would other teams care about my updates?</h3> -<p>A: Because we should collaborate and care about our team, not just about our individual work. Your updates may bring new opportunities, ideas, concerns, feedback, and more. It also aligns well with being a good communicator who’s helping us improve the team’s accountability.</p> -<h3 id="q-are-the-daily-updates-mandatory">Q: Are the daily updates mandatory?</h3> -<p>A: Yes, they are mandatory and also a brilliant opportunity to engage your colleagues, to motivate them, and enhance our collective output. This isn’t just for authority’s sake – if everyone takes daily updates seriously, we will be more productive, aligned and connected as a team.</p> -<h3 id="q-what-happens-if-i-dont-share-updates-daily">Q: What happens if I don’t share updates daily?</h3> -<p>A: The goal is maintaining efficient collaboration and ensuring we&rsquo;re all moving towards our objectives. By not communicating with the team, we are affecting collaboration and transparency, at the very least. For each of us, our work is not only about the technical stuff but also about being a good team player, effective communicator, and <a href="https://docs.communityhealthtoolkit.org/contribute/medic/product-core-competencies/">more</a>. Failure to provide updates may affect your evaluation, potentially impacting opportunities for growth and advancement within the team.</p> -<h3 id="q-how-often-should-i-post-updates-in-the-daily-channel">Q: How often should I post updates in the daily channel?</h3> -<p>A: At least once per day, but for some people, more than one update could also work. For instance, their commitments for the day and then what they actually achieved.</p> -<h3 id="q-what-is-the-best-time-to-post-updates-on-the-daily-channel">Q: What is the best time to post updates on the daily channel?</h3> -<p>A: Whatever works best for you. But, usually, posting the update could be either one of the first activities you complete or one of the last things you do before leaving for the day. Here’s some interesting <a href="https://blog.xmtp.com/goodbye-standups-hello-wrap-ups/">reading</a> on the topic.</p> -<h3 id="q-how-is-this-measured-in-my-performance-review">Q: How is this measured in my performance review?</h3> -<p>A: Managers will share regular feedback throughout the year about the quality and consistency of your updates. Preferably, by the time performance reviews happen, you will already have received plenty of feedback and identified areas for growth that you committed to working on. Effective communication and reliability are part of our core competencies, and accountability is a big deal we are trying to get better at as a team, so status update consistency will be part of the calibration toward those competencies.</p> -<h3 id="q-how-detailed-should-my-updates-be">Q: How detailed should my updates be?</h3> -<p>A: We shared some <a href="#bad-update-vs-good-update-examples">examples of updates</a> and some prompts that can help you draft your daily updates. Keep in mind that status updates demand clarity and specific information that is valuable and informs others.</p> -<h3 id="q-what-should-i-do-if-i-have-no-significant-updates-for-the-day">Q: What should I do if I have no significant updates for the day?</h3> -<p>A: If you don’t have significant updates about what you did, you can always share updates about how you feel about it. Maybe a task was simply straightforward and it doesn’t require many updates. Or maybe there were a number of difficult, unforeseen issues that came up, and you didn’t progress as much as you wanted. Maybe you had a super productive day. Tell the team how you feel; the channel is a safe space to speak honestly! (<em>Pro tip</em>: <a href="https://feelingswheel.com/">Feelings Wheel</a>)</p> -<h3 id="q-is-it-okay-to-post-challenges-or-roadblocks-in-the-product-dailies">Q: Is it okay to post challenges or roadblocks in the <em>#product-dailies</em>?</h3> -<p>A: Absolutely, someone can help you after reading your update. If you think of someone in particular who could give you a hand, just tag them!</p> -<h3 id="q-should-i-respond-to-other-team-members-updates-in-the-daily-channel">Q: Should I respond to other team members&rsquo; updates in the daily channel?</h3> -<p>A: If they tag you or you want to reply to something specific about their updates, it is highly encouraged to do so! You can also react to their messages with an emoji if you want to.</p> -<h3 id="q-what-sort-of-feedback-should-i-expect-on-my-updates">Q: What sort of feedback should I expect on my updates?</h3> -<p>A: Managers should provide direct, regular, both positive and constructive and helpful feedback on status updates provided by the team members.</p> -<h3 id="q-i-always-forget-to-share-my-daily-updates-how-can-i-remember-to-share-them">Q: I always forget to share my daily updates. How can I remember to share them?</h3> -<p>A: Tips for remembering to share the daily updates:</p> -<ul> -<li><a href="https://support.google.com/calendar/answer/9901136?hl=en&amp;co=GENIE.Platform%3DAndroid">Create a Recurrent Task in Google Calendar</a></li> -<li><a href="https://slack.com/intl/en-gb/help/articles/208423427-Set-a-reminder">Set a Reminder in Slack</a></li> -<li><a href="https://jamesclear.com/habit-stacking">How to Build New Habits by Taking Advantage of Old Ones</a></li> -</ul> \ No newline at end of file +Onboarding on Community Health Toolkithttps://docs.communityhealthtoolkit.org/contribute/medic/onboarding/Recent content in Onboarding on Community Health ToolkitHugo -- gohugo.ioenProduct Teamhttps://docs.communityhealthtoolkit.org/contribute/medic/onboarding/product-team/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/product-team/The goal of this page is to convey a general “lay of the land” so someone starting can see a lot of what’s out there related to Product Team without having to be surprised each day as new things pop up. +About Product Team The Product Team manages the entire software development life-cycle to understand problems, capture requirements, design and build modular software systems, and document everything along the way. We achieve this by working with CHT partners and health workers to design, build, and maintain the Community Health Toolkit and its open source tools.All The Thingshttps://docs.communityhealthtoolkit.org/contribute/medic/onboarding/all-the-things/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/all-the-things/This page is meant to serve as a point of conversation, with a wide range of topics to be discussed when joining Medic or starting as a contributor. Many things are not in any particular order. The goal is to convey a general “lay of the land” so someone starting can see a lot of what’s out there without having to be surprised each day as new things pop up.Technical Resourceshttps://docs.communityhealthtoolkit.org/contribute/medic/onboarding/technical-resources/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/technical-resources/Note This page also contains Medic internal documentation links that are only accessible to Medic team members. This page contains materials that will help a CHT Contributor to master all the technical details they need to be successful when building tools related to the CHT. For Medic team members, this content is complimentary on a technical level to the Onboarding documents shared by the Internal Operations team. Keep in mind that this is a living document, and every contributor is encouraged to add to it when identifying learning opportunities that can set them up for success with the CHT.Team Meetingshttps://docs.communityhealthtoolkit.org/contribute/medic/onboarding/team-meetings/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/team-meetings/Product Team Monthly Call Why Getting to see each other all in one place at the same time, even if only online, lets people get to be around each other, learn from each other, and hear other perspectives. We&rsquo;re real people! &hellip;terrific people too. This is a small space to spend some time around each other as a group. We&rsquo;re mostly trying to learn more about each other, celebrate recent successes, and cover the occasional big news or team-wide changes.Daily Updateshttps://docs.communityhealthtoolkit.org/contribute/medic/onboarding/daily-updates/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/daily-updates/What In our fully remote environment, staying connected and informed is crucial. One of the ways we can ensure transparency, and collaboration, and keep everyone in the loop is by utilizing the #product-dailies channel in Slack for sharing daily updates. +Who All the Product Team members are expected to share daily updates in Slack (#product-dailies channel). Due to the nature of their work, Leadership Product colleagues might share less regular, but more consolidated updates. \ No newline at end of file diff --git a/contribute/medic/onboarding/product-team/index.html b/contribute/medic/onboarding/product-team/index.html index 331c5924c5..818a5921d0 100644 --- a/contribute/medic/onboarding/product-team/index.html +++ b/contribute/medic/onboarding/product-team/index.html @@ -1,9 +1,9 @@ -Product Team | Community Health Toolkit +Product Team | Community Health Toolkit

    Product Team

    Introduction to Product Team

    The goal of this page is to convey a general “lay of the land” so someone starting can see a lot of what’s out there related to Product Team without having to be surprised each day as new things pop up.

    About Product Team

    The Product Team manages the entire software development life-cycle to understand problems, capture requirements, design and build modular software systems, and document everything along the way. + Create project issue

    Product Team

    Introduction to Product Team

    The goal of this page is to convey a general “lay of the land” so someone starting can see a lot of what’s out there related to Product Team without having to be surprised each day as new things pop up.

    About Product Team

    The Product Team manages the entire software development life-cycle to understand problems, capture requirements, design and build modular software systems, and document everything along the way. We achieve this by working with CHT partners and health workers to design, build, and maintain the Community Health Toolkit and its open source tools.

    Composition

    The Product Team comprises a Community Team and 4 Focused Working Groups, each focusing on outcomes to serve different people.

    Focused Working Groups

    The Product Team operates in a way that is most commonly referred to as Continuous Discovery. That process helps us to focus on the impact of our work and keep teammates well-aligned with the people who use the CHT. Read more about the process here.

    Each Focused Working Group serves to different user types to understand opportunities for improvement, find solutions that will have an impact, and then build, deploy, and measure them. Find out more about the different Focused Working Groups here.

    Community Team

    The community team builds and supports the CHT community of practitioners to ensure that community members can actively use and contribute to the CHT.

    Key users: Core Developers, App Developers, digital health implementers, research organizations, ministries of health, academic institutions and individual contributors.

    Team members:

    • Director of Community.
    • Technical Writer.
    • Developer Advocate.
    • Community Manager.

    CHT Round-up calls: Monthly calls aimed at engaging the CHT community, sharing CHT product updates and getting valuable feedback from the CHT community. See previous Round-up calls here.

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/contribute/medic/onboarding/team-meetings/index.html b/contribute/medic/onboarding/team-meetings/index.html index 26e5a7ae5e..095e9f3ecc 100644 --- a/contribute/medic/onboarding/team-meetings/index.html +++ b/contribute/medic/onboarding/team-meetings/index.html @@ -1,9 +1,9 @@ -Team Meetings | Community Health Toolkit +Team Meetings | Community Health Toolkit

    Team Meetings

    How the Product full-team meetings run

    Product Team Monthly Call

    Why

    Getting to see each other all in one place at the same time, even if only online, lets people get to be around each other, learn from each other, and hear other perspectives. We’re real people! …terrific people too. This is a small space to spend some time around each other as a group. We’re mostly trying to learn more about each other, celebrate recent successes, and cover the occasional big news or team-wide changes. It’s mostly about being together to smile a bit.

    When

    Roughly the first Tuesday (or Wednesday, for New Zealand teammates) week of each month.

    Who is there

    The whole Product Team.

    Who facilitates it

    Maybe you! We’ll have a rotation of teammates leading the call. It doesn’t mean you give a 2-hour speech at your teammates. It just means you help out with some basic logistics and guide us through the loose segments so it’s not chaos.

    How to facilitate it

    Preparations

    1. About 2 weeks before the call, open the Product Team Call document and:
      1. Add a new date token for “question of the week”.
      2. Randomize the list of teammate names.
      3. Put the name of whoever ended up first on the randomized list as the question of the week person.
      4. Notify that person via Slack that they should think up a question and add it to the doc any time prior to the call. Special note: If we have a new teammate, they give to be first and set the question. Just modify the list to make it so.
      5. The last randomized person on the list will be in charge of “activity of the week” (anything small and fun we can all do together. Past activities have included GeoGuessr, Gartic Phone, and Kahoot trivia).
      6. Notify that person via Slack that they should find such an activity and be prepared to lead it for us all.
    2. Create a new Jamboard in the team meeting folder
      1. Make the Jamboard accessible for all teammates
      2. Link it in the agenda under “Celebrations and good fun stuff”
      3. Send a message to the whole team in Slack so they know the jamboard is available and can start putting fun things on it, and also that the agenda is open now for team-wide or org information to add.

    Running the call on the day-of

    1. Start the call on time (mostly). We can’t sit around forever, but this is mostly a fun thing, so it’s ok if people are just chatting for a couple minutes while waiting for stragglers.
    2. Say hello and all that.
    3. Do the question of the week activity.
      1. Sometimes people get the order mixed up. Be prepared to nudge people if needed.
    4. Move to the regular agenda. If there are special items, someone’s name should be on it already.
    5. Do the Jamboard.
    6. Ask for someone to mark the cards as you call them out. Enough people seem to take joy in this, and it’ll be a thing all on its own!
    7. Note which card you’re looking at, read it, and ask if anyone (or the author) wants to add more. Sometimes it’s quick stuff, sometimes it’s big fun things everyone wants to spend more time on. Cool.
    8. Pass the mic to the activity of the week person.
    9. Fin! (It’s ok if we finish early. We need to try hard not to finish late; it’s evening for some teammates!).

    Product Team Social Time

    People appreciate social time, so we will use this weekly time to connect with each other rather than share updates. It is a perfect place to discuss a (non) technical issue you are encountering and ask for support from colleagues.

    The Product Team Social Time is optional, but highly encouraged. You can choose the schedule that works best for you (2 calls on Mondays 10AM GMT and 9PM GMT).

    Frequently Asked Questions

    Q: What if I’m alone in the product social time?

    A: You don’t need to wait for more than 5 or 10 minutes if no one else shows up.

    Q: How frequently should I go to the product social time?

    A: You are not required to join this call, but it is highly encouraged as we’d like teammates to connect not only via Slack or GitHub.

    Q: Is the product social time considered as part of my performance review?

    A: No, it is not considered a part of the performance review.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/medic/onboarding/technical-resources/index.html b/contribute/medic/onboarding/technical-resources/index.html index 4e9e7820ec..5ad098347d 100644 --- a/contribute/medic/onboarding/technical-resources/index.html +++ b/contribute/medic/onboarding/technical-resources/index.html @@ -1,9 +1,9 @@ -Technical Resources | Community Health Toolkit +Technical Resources | Community Health Toolkit

    Technical Resources

    Resources to get started as a contributor to the CHT

    This page contains materials that will help a CHT Contributor to master all the technical details they need to be successful when building tools related to the CHT. For Medic team members, this content is complimentary on a technical level to the Onboarding documents shared by the Internal Operations team. + Create project issue

    Technical Resources

    Resources to get started as a contributor to the CHT

    This page contains materials that will help a CHT Contributor to master all the technical details they need to be successful when building tools related to the CHT. For Medic team members, this content is complimentary on a technical level to the Onboarding documents shared by the Internal Operations team. Keep in mind that this is a living document, and every contributor is encouraged to add to it when identifying learning opportunities that can set them up for success with the CHT. The main goal here is to provide the technical people who are new to the CHT with a list of resources that could be beneficial for them in both the short and long term. However, the expectation is not to complete every course listed in this document but instead to share some resources that other contributors found helpful. It is valid if you prefer to use other training material, as long as you cover the skill/topic during the onboarding. That means you will need to learn JavaScript, TypeScript, Docker, CouchDB, and others mentioned in the “Skills” column, as you will use them in your day-to-day activities, and the list provided in the “Resources” column is meant to support you in this. It is OK if you prefer to learn with alternative resources.

    Exercises

    There is a Slack channel #exercism where engineers and tech enthusiasts meet and discuss JavaScript and TypeScript exercises from exercism.org on a weekly basis. The primary goal of this channel is to discuss with your teammates and have a fun, open discussion on how the provided solutions can be improved, while also having the opportunity to connect with people from other teams. Even though this is optional, it is highly encouraged to join and participate frequently.

    Technical Resources

    SkillResources
    CHT Local Environment and DocsSet up your local development environment
    Go through CHT Documentation
    Get familiar with our Development Workflow
    Architecture of CHT Instances

    Internal resources:
    App building for techies crash course with Ashley Jones
    Where to ask questions about the CHT?
    JavaScriptEloquent JavaScript
    Asynchronous JavaScript Requests
    You Don’t Know JS
    TypeScriptTypeScript Documentation
    DockerContainers and Docker Mega Thread
    Docker mastery course
    Learn Docker in a month of lunches
    The Ultimate Docker course
    CouchDB and PouchDBCouchDB 2.3 introduction user guide docs
    Understanding CouchDB 3
    Introduction to PouchDB
    NodeJS and NPMThe Complete Node.JS developer course
    Understanding NPM - Node.js Package Manager
    AngularAngular 12 - Complete Beginner’s Guide 2021
    Offline FirstOffline Web Applications
    Web ArchitectureWeb Architecture 101
    Web BrowsersInside look at modern web browsers, part I
    Inside look at modern web browsers, part II
    Inside look at modern web browsers, part III
    Inside look at modern web browsers, part IV
    Web PerformanceHigh Performance Browser Networking
    PostgreSQLPostgreSQL Tutorial
    PostgreSQL Cheat Sheet
    PostgreSQL Views Tutorial
    PostgreSQL Views Documentation
    Common Table Expressions (CTEs)
    PostgreSQL Queries with CTEs Documentation
    SupersetIntroduction to Superset
    Creating Your First Dashboard
    DBTDBT Core Manual Installation Guide
    DBT Video Playlist
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/contribute/medic/product-core-competencies/index.html b/contribute/medic/product-core-competencies/index.html index 751f34ae71..f5c8f63dee 100644 --- a/contribute/medic/product-core-competencies/index.html +++ b/contribute/medic/product-core-competencies/index.html @@ -1,9 +1,9 @@ -Product Team Core Competencies | Community Health Toolkit +Product Team Core Competencies | Community Health Toolkit

    Product Team Core Competencies

    These core competencies are intended to guide Product Teammates in how everyone shows up for each other every day. They are to be used in hiring new teammates, teammate feedback, and regular manager check ins.
    Core CompetencyDescription
    ReliableSets and communicates clear expectations about when something needs to/will be done and does it without prompting.
    Team PlayerActs in the best interest of the team and actively looks for ways to help the team. Makes time to support teammates to be successful.
    Growth MindsetAlways seeking to improve. Open minded, teachable, and coachable.
    ProactiveSees things that need doing and takes action to keep things moving and make the team successful.
    Effective CommunicatorCommunicates regularly, openly, and effectively using the appropriate channels.
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/medic/product-core-competencies/index.xml b/contribute/medic/product-core-competencies/index.xml index d3e8f06e9f..1e010b51e5 100644 --- a/contribute/medic/product-core-competencies/index.xml +++ b/contribute/medic/product-core-competencies/index.xml @@ -1 +1 @@ -Community Health Toolkit – Product Team Core Competencieshttps://docs.communityhealthtoolkit.org/contribute/medic/product-core-competencies/Recent content in Product Team Core Competencies on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file +Product Team Core Competencies on Community Health Toolkithttps://docs.communityhealthtoolkit.org/contribute/medic/product-core-competencies/Recent content in Product Team Core Competencies on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file diff --git a/contribute/medic/product-development-process/code-health/index.html b/contribute/medic/product-development-process/code-health/index.html index 2ae604afd5..ad3b9412a9 100644 --- a/contribute/medic/product-development-process/code-health/index.html +++ b/contribute/medic/product-development-process/code-health/index.html @@ -1,9 +1,9 @@ -Code Health | Community Health Toolkit +Code Health | Community Health Toolkit

    Code Health

    How to keep the code and systems up-to-date

    Healthy code is code that is easy to understand and update. Over time all code becomes less healthy as dependencies become out of date, new language features or code style rules are introduced, and changes are made that add complexity. This is sometimes referred to as technical debt as it makes it progressively harder to make new code changes. No project is ever completely healthy, so we need an ongoing process to improve the least healthy parts of the code so the CHT can be sustainable long term.

    A good way to differentiate code health improvements from other changes is if it’s something that a user won’t notice, then it’s code health. Because no user will ever ask for the change to be made they need to be managed outside of the usual prioritisation process.

    A good guideline is to spend 25% of your time improving code health, which can be split up into three distinct categories: Tidying, Cards, and Projects.

    Tidying

    These are the smallest improvements and should be part of your work every week. Engineers can decide what tidying they want to prioritize and don’t need any approval to do the work, but it will need to go through Code Review to ensure a teammate agrees it’s an improvement.

    If the tidying is unrelated to other issues you’re working on then to make the review easy you should create a new issue, branch, and PR for the tidying work. However if the tidying will conflict or block development of your other work, then use your best judgement about how to proceed in whatever way is easiest for you and your reviewer. Some options are: a) in the same commit if the change is small, b) a separate commit in the same branch if the changes are codependent, c) two branches with one referencing the other.

    Examples of appropriate tidying are increasing code reuse, improving readability, reducing complexity, updating a dependency, removing deprecated calls, and increasing test coverage.

    Tidying should take around 10% of your time, or 4 hours a week.

    Cards

    These are mid-sized improvements and are worked on every quarter. Focused Groups and Product Leadership work together to decide which cards to prioritise and include in the FGs OKRs. The FG is then responsible for ensuring the improvements get scheduled and delivered just like other work.

    Cards should take around 10% of your time, or 6 days per quarter.

    How Cards work in practice

    A Focused Working Group is building a feature aligned with a quarterly OKR. When the feature is shipped, you may need to wait a week to see results from CHWs. The PM/PO goes through a list of tickets for code health and reviews them with the team. After aligning with the PM/PO, software developers and QA engineers start working on those tickets, tracking progress on the GitHub board.

    During this time almost all attention, from all engineers in the Focused Working Group, is on heads-down coding on code health tasks. There may still be some other tasks going on like user interviews or initial measurements of the newly deployed feature.

    At the end of the week, the team realigns around work towards the chosen outcome. Code health work should be merged and the full focus of the team is together with the PM/PO on delivering improvements for users. It may be the case that a code health task cannot yet be merged. If that is the case, other developers on the Focused Working Group should assist to get it complete so everyone can stay together in what is being worked on.

    Projects

    Projects are large refactors that are therefore expensive and risky to undertake. These need to be discussed with Product and Medic leadership and will likely require deep analysis and prototyping to verify the approach.

    Examples of projects are upgrading from AngularJS to Angular, rewriting couch2pg as cht-sync, and switching from medic-os to containerized deployment.

    Projects should take around 5% of your time, or one project every few years.

    More info

    This policy was inspired by a blog post about technical debt at Shopify.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/medic/product-development-process/continuous-discovery-overview/index.html b/contribute/medic/product-development-process/continuous-discovery-overview/index.html index 334b39d261..d693b68894 100644 --- a/contribute/medic/product-development-process/continuous-discovery-overview/index.html +++ b/contribute/medic/product-development-process/continuous-discovery-overview/index.html @@ -1,9 +1,9 @@ -Continuous Discovery Overview | Community Health Toolkit +Continuous Discovery Overview | Community Health Toolkit

    Continuous Discovery Overview

    Continuous Discovery basics that Medic Product teammates seek to follow

    The continuous discovery mindset is one that acknowledges that digital products are never “done” and can always be made better. The “discovery” aspect is that those working on the product should empathize with those who use it and operate with those people in mind, both for finding the most impactful areas of work and in how to measure impact.

    Teammates choose work with impact in mind and develop solutions with an expectation that people will use what is built and we can measure a positive change.

    Some key activities that are common in such a process are:

    1. Starting with a clear desired outcome.
    2. All teammates actively engaged in talking directly with people who use the software.
    3. Solutions designed with users in mind, with an understanding of their environment.
    4. Desire to deploy solutions quickly, measure the change, and work with partners to make further improvements.

    This quote from Teresa Torres sums up the process nicely:

    Good product discovery has a simple underlying structure: Start with an outcome, discover opportunities, discover solutions.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/medic/product-development-process/focused-groups/index.html b/contribute/medic/product-development-process/focused-groups/index.html index 2cba7a66bf..5fed2fb9c1 100644 --- a/contribute/medic/product-development-process/focused-groups/index.html +++ b/contribute/medic/product-development-process/focused-groups/index.html @@ -1,9 +1,9 @@ -Focused Working Groups | Community Health Toolkit +Focused Working Groups | Community Health Toolkit

    Focused Working Groups

    Cross-functional teams designed to better serve the community

    The Product team as a whole is focused on digital health tools helping to achieve Universal Health Coverage, and care reaching everyone when and where they need it. The team is made up of 4 different groups, with each group focusing on outcomes to serve different people.

    See the overall Product Team Roadmap.

    Care Teams

    Focused on building effective tools to provide care in the hardest-to-reach communities.

    Key users: Patients, Families, Caregivers, Community Health Workers, Supervisors.

    Team members:

    • Product Manager
    • Product Designer
    • UX Researchers
    • Software Developers
    • Quality Assurance Engineer

    Introduction to Care Teams Focused Working Group

    See Care Teams OKRs and current activities.

    Allies

    Focused on efficient scaling of health impact with digital health systems.

    Key users: App Admins, Implementing Partners, App Developers, Data Scientists.

    Team members:

    • Technical Product Owner
    • Product Designer
    • UX Researchers
    • Software Developers
    • Quality Assurance Engineer

    Introduction to Allies Focused Working Group

    See Allies OKRs and current activities.

    Ecosystem

    This Focused Working Group’s primary objective is to build on the Community Health Toolkit in collaboration with other members/partners in the digital health ecosystem (DHE). The team actively develops enhancements and promotes their implementation within the DHE.

    Key users: Data scientists, Integrators, Program Administrators and Implementing partners.

    Team members:

    • Technical Product Owner
    • Product Designer
    • UX Researchers
    • Software Developers
    • Quality Assurance Engineer

    Introduction to Ecosystem Focused Working Group

    See Ecosystem OKRs and current activities.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/medic/product-development-process/index.html b/contribute/medic/product-development-process/index.html index 5672e53d1a..6ea30ac581 100644 --- a/contribute/medic/product-development-process/index.html +++ b/contribute/medic/product-development-process/index.html @@ -1,9 +1,9 @@ -Product Development Process | Community Health Toolkit +Product Development Process | Community Health Toolkit

    Product Development Process

    Explanation of the Continuous Discovery process used for product development at Medic

    The Medic Product Team operates in a way that is most commonly referred to as Continuous Discovery. That process helps us to focus on the impact of our work and keep teammates well-aligned with the people who use the CHT.

    To allow teammates to truly learn from and empathize with those using the CHT, the Medic Product team has cross-functional subteams, called Focused Working Groups, each aligned with different user types. Each group is focused on a specific set of users of the CHT and has an aligned roadmap. Roadmaps are made up of defined initiatives, which map to goals and strategies. There is a big emphasis on working towards outcomes (not only outputs). Product teammates work together, across disciplines, to understand opportunities for improvement, find solutions that will have an impact, and then build, deploy, and measure them.

    As part of responsible maintainers of a software system, software engineers also dedicate time to performing Code Health tasks. That ensures updates, upgrades, code enhancements, and small bugs are still handled while the team works on highly focused user-facing objectives. + Create project issue

    Product Development Process

    Explanation of the Continuous Discovery process used for product development at Medic

    The Medic Product Team operates in a way that is most commonly referred to as Continuous Discovery. That process helps us to focus on the impact of our work and keep teammates well-aligned with the people who use the CHT.

    To allow teammates to truly learn from and empathize with those using the CHT, the Medic Product team has cross-functional subteams, called Focused Working Groups, each aligned with different user types. Each group is focused on a specific set of users of the CHT and has an aligned roadmap. Roadmaps are made up of defined initiatives, which map to goals and strategies. There is a big emphasis on working towards outcomes (not only outputs). Product teammates work together, across disciplines, to understand opportunities for improvement, find solutions that will have an impact, and then build, deploy, and measure them.

    As part of responsible maintainers of a software system, software engineers also dedicate time to performing Code Health tasks. That ensures updates, upgrades, code enhancements, and small bugs are still handled while the team works on highly focused user-facing objectives. Each Focused Working Group has defined Objectives and Key Results (OKRs), which set their focus for a given quarter. These, in turn, are derived from the Product Team’s yearly Objective Goals Strategies Measurements (OGSMs). The Schedule of Activities has more on the timing of all this through out any given year.


    Continuous Discovery Overview

    Continuous Discovery basics that Medic Product teammates seek to follow

    Focused Working Groups

    Cross-functional teams designed to better serve the community

    Schedule of Activities

    Schedule of activities for Focused Working Groups

    Product Trio

    What is a Product Trio

    UX Research Repository

    How to keep track of product research

    Quality Assistance

    How the Quality Assistance process works at Medic

    Code Health

    How to keep the code and systems up-to-date

    Transparency & Accountability

    How we make it easy for others to know what’s going on and getting done.

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/contribute/medic/product-development-process/index.xml b/contribute/medic/product-development-process/index.xml index de4bfb7c14..bb848499e7 100644 --- a/contribute/medic/product-development-process/index.xml +++ b/contribute/medic/product-development-process/index.xml @@ -1,160 +1,7 @@ -Community Health Toolkit – Product Development Processhttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/Recent content in Product Development Process on Community Health ToolkitHugo -- gohugo.ioenContribute: Continuous Discovery Overviewhttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/continuous-discovery-overview/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/continuous-discovery-overview/ -<p>The continuous discovery mindset is one that acknowledges that digital products are never &ldquo;done&rdquo; and can always be made better. The &ldquo;discovery&rdquo; aspect is that those working on the product should empathize with those who use it and operate with those people in mind, both for finding the most impactful areas of work and in how to measure impact.</p> -<p>Teammates choose work with impact in mind and develop solutions with an expectation that people will use what is built and we can measure a positive change.</p> -<p>Some key activities that are common in such a process are:</p> -<ol> -<li>Starting with a clear desired outcome.</li> -<li>All teammates actively engaged in talking directly with people who use the software.</li> -<li>Solutions designed with users in mind, with an understanding of their environment.</li> -<li>Desire to deploy solutions quickly, measure the change, and work with partners to make further improvements.</li> -</ol> -<p>This quote from <a href="https://www.producttalk.org/">Teresa Torres</a> sums up the process nicely:</p> -<blockquote> -<p>Good product discovery has a simple underlying structure: Start with an outcome, discover opportunities, discover solutions.</p> -</blockquote>Contribute: Focused Working Groupshttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/focused-groups/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/focused-groups/ -<p>The Product team as a whole is focused on digital health tools helping to achieve Universal Health Coverage, and care reaching everyone when and where they need it. The team is made up of 4 different groups, with each group focusing on outcomes to serve different people.</p> -<p>See the overall <a href="https://github.com/orgs/medic/projects/112/views/4/">Product Team Roadmap</a>.</p> -<h2 id="care-teams">Care Teams</h2> -<p>Focused on building effective tools to provide care in the hardest-to-reach communities.</p> -<p>Key users: Patients, Families, Caregivers, Community Health Workers, Supervisors.</p> -<p>Team members:</p> -<ul> -<li>Product Manager</li> -<li>Product Designer</li> -<li>UX Researchers</li> -<li>Software Developers</li> -<li>Quality Assurance Engineer</li> -</ul> -<p><a href="https://youtu.be/X49ML5AqnBM">Introduction to Care Teams Focused Working Group</a></p> -<p>See <a href="https://github.com/orgs/medic/projects/112/views/11">Care Teams OKRs</a> and <a href="https://github.com/orgs/medic/projects/134/views/2">current activities</a>.</p> -<h2 id="allies">Allies</h2> -<p>Focused on efficient scaling of health impact with digital health systems.</p> -<p>Key users: App Admins, Implementing Partners, App Developers, Data Scientists.</p> -<p>Team members:</p> -<ul> -<li>Technical Product Owner</li> -<li>Product Designer</li> -<li>UX Researchers</li> -<li>Software Developers</li> -<li>Quality Assurance Engineer</li> -</ul> -<p><a href="https://www.youtube.com/watch?v=dsc1XMdXhXs">Introduction to Allies Focused Working Group</a></p> -<p>See <a href="https://github.com/orgs/medic/projects/112/views/12">Allies OKRs</a> and <a href="https://github.com/orgs/medic/projects/134/views/3">current activities</a>.</p> -<h2 id="ecosystem">Ecosystem</h2> -<p>This Focused Working Group&rsquo;s primary objective is to build on the Community Health Toolkit in collaboration with other members/partners in the digital health ecosystem (DHE). The team actively develops enhancements and promotes their implementation within the DHE.</p> -<p>Key users: Data scientists, Integrators, Program Administrators and Implementing partners.</p> -<p>Team members:</p> -<ul> -<li>Technical Product Owner</li> -<li>Product Designer</li> -<li>UX Researchers</li> -<li>Software Developers</li> -<li>Quality Assurance Engineer</li> -</ul> -<p><a href="https://www.youtube.com/watch?v=R2Yd7_t4DbE">Introduction to Ecosystem Focused Working Group</a></p> -<p>See <a href="https://github.com/orgs/medic/projects/112/views/16">Ecosystem OKRs</a> and <a href="https://github.com/orgs/medic/projects/134/views/11">current activities</a>.</p>Contribute: Schedule of Activitieshttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/schedule-of-events/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/schedule-of-events/ -<h2 id="annually">Annually</h2> -<ul> -<li>Set goals (strategies and measures)</li> -<li>Identify desired outcomes the team will work towards</li> -</ul> -<h2 id="quarterly">Quarterly</h2> -<ul> -<li>Choose outcome(s) for Focused Working Groups to work towards</li> -<li>Outcome kickoff</li> -</ul> -<h2 id="monthly">Monthly</h2> -<ul> -<li>Communicate status and progress of work towards current outcome(s)</li> -<li>Recognize and establish certainty around time sensitive, project-initiated tasks</li> -</ul> -<h2 id="weekly">Weekly</h2> -<ul> -<li>Conduct interviews</li> -<li>Continued iterative work on identifying opportunities, weighing possible solutions, building, and measuring</li> -<li>Triage potential side-loaded opportunities</li> -</ul>Contribute: Product Triohttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/product-trio/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/product-trio/ -<p>A Product Trio refers to a product manager, a product designer and a software engineer working together to develop digital products in the scope of a Focused Working Group. Some Focused Working Groups have multiple software engineers contributing to the work. Additionally, other roles contribute to the Trio, such as UX Researcher and Quality Assuarance Engineer. In that sense, Product Trios are often a quartet or a quintet.</p>Contribute: UX Research Repositoryhttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/ -<p>The Medic Product team uses <a href="http://baserow.io">baserow.io</a> as a place to keep product research. Baserow is very similar to <a href="http://airtable.com">airtable.com</a>, <a href="http://getgrist.com">getgrist.com</a>, and Google Sheets.</p> -<p>The <em>UX Research Repository</em> is based off of concepts from WeWork’s <a href="https://tsharon.medium.com/democratizing-ux-670b95fbc07f">Polaris</a> project. You can see an example of the Polaris (which uses Airtable) <a href="https://www.airtable.com/universe/expShuhNMi0Oc0xpb/polaris-ux-nuggets">here</a>.</p> -<p>The Baserow interface can be seen below. The items in the list on the left are “tables” that we have created. On the right is the “Grid”. It looks just like a spreadsheet and you can have multiple views of the same grid.</p> -<p><img src="images/baserow.png" alt="The Baserow interface 👆🏼"></p> -<p>To gain access to the UX Research Repo, please reach out in the #product-owner-chatter Slack channel. The access to the repository is currently restricted to the Medic team.</p>Contribute: Quality Assistancehttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/quality-assistance/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/quality-assistance/ -<h2 id="goals">Goals</h2> -<ol> -<li>Software developers should have full ownership of what they are building, including quality.</li> -<li>The team still benefits from having the QA engineering mindset of QA engineers.</li> -<li>QA engineers automate more tests and processes.</li> -</ol> -<h2 id="in-short">In Short</h2> -<p>A software developer writes code and performs testing on that code where a QA engineer assists by recommending tests to perform and adding additional end-to-end tests.</p> -<h2 id="in-detail">In Detail</h2> -<p>A developer should be able to write code and release it when done. Doing that without testing would be reckless and it is expected that the developer also tests their code. The team wants both speed and quality where the former should not be done without the latter. This means slowing down a bit to make sure it’s right and checking your work when developing before it is done/reviewed/merged.</p> -<p>It isn’t always easy to test your own code though and it is common for a developer to have a happy-path mindset, more suited for thinking about what to build rather than how something might break. Fortunately there are QA engineers on the team who have the skills to think about the solution from the perspective of what might break.</p> -<p>It’s worth noting that the team also requires code reviews via GitHub pull requests. The intention of code reviews is not for the reviewer to test the code. The reviewer may optionally also do that, but it is expected that the author of the code has tested it fully before requesting a code review.</p> -<p>Once code is done, tested (by the developer) and reviewed, it is ready to merge and the developer can do so&hellip; almost (see <a href="#getting-there-in-milestones">Getting There in Milestones</a>).</p> -<h2 id="getting-there-in-milestones">Getting There in Milestones</h2> -<p>The aim is to get to a place where the developer of a change can do everything and merge without needing any extra permission. To make the process safer in getting to the final step, several parts will be followed. A process similar to what’s described <a href="#in-detail">In Detail</a> is followed, while retaining an “AT” step similar to what the current process.</p> -<p>The big difference to that AT step though will be the depth in which it is performed. Rather than being a single exhaustive step where all testing is performed, it will be more like a smoke test where a QA engineer gets a little extra creative in testing.</p> -<p>There may still be cases of a ticket getting coded up totally unrelated to Focused Working Group work, perhaps even unannounced to anyone. For work like this the traditional AT step is still appropriate for now. Still though, it is preferred for the software developer to get a QA engineer involved as early as possible; it helps us get the best contributions from everyone.</p> -<h2 id="an-example-of-qa-assistance-in-practice">An Example of QA Assistance in Practice</h2> -<p>Imagine a scenario where a Focused Working Group is going to change the display of the targets screen. The group would discuss the needs of the users and work out solutions, where the selected solution was to make this change. This discussion would involve a PM/PO, designer, developer, and QA engineer. The developer would be thinking about how to code the proposed solution and the QA engineer would be considering that as well as what might go wrong.</p> -<p>In the case where a UI change is being made, a design will be created. At this point the group can see what is to be built. When meeting and discussing the user interactions with the designer the QA engineer can get some early ideas around test scenarios.</p> -<p>As code is written and pushed to a branch the QA engineer and developer talk about what is changing. Any misconceptions are cleared up and the QA engineer can start to assist the developer by pointing out areas that may be important or non-obvious to test. This could be as simple as noting challenges of different screen sizes or as complex as specific configurations that may need extra attention for how targets work.</p> -<p>That conversation (ideally multiple of these) is the “assistance”. It’s where the value of the QA engineering mindset is achieved. The developer still owns and tests their work; they just have a QA engineer to talk to so they can feel confident in their own testing.</p> -<p>Depending on the change the developer may work with the QA engineer where the QA engineer can contribute some automated end-to-end testing to the branch. Things should feel collaborative and not a division of labor or handing things off.</p> -<p>Once the developer has finished writing the code they test it a bit more and open a PR for a code review. The reviewer should be able to review the code with an assumption that it is well-tested already and the focus of the review can be around the code and implementation choices.</p> -<p>If any changes need to be made during the review process, the developer makes those changes and re-executes any relevant testing.</p> -<p>At this point the ideal action to take would be that the developer merges the finished code and no AT step happens, as the quality is already baked in from the start and throughout. This is the ideal setup yet to be achieved. Here the ticket still goes to AT, but for a smaller last bit of smoke testing. Notice that it is not an exhaustive set of tests, it is a small bit of extra poking around by a QA engineer. This limited testing is possible because the developer said it is done, and they said that because they tested it, and they had a QA engineer assist them to reach that level of confidence in their own testing.</p> -<p>The last part here is to merge it. That extra poking around should be quick, so the developer should be ready to click the green button soon!</p> -<h2 id="but-but-but-some-questions-you-might-have">But, but, but!!! (some questions you might have)</h2> -<ul> -<li>Who checks if the right thing got built? – The Focused Working Group members should be aware of what’s being built, why, and if it’s coming together as expected. That’s not to be solely delegated to a QA engineer to do. Developers should be working with their teammates and showing their work (demos, screenshots, etc). This should feel like a team collaborating to build useful working software, not an assembly line of disassociated parts.</li> -<li>What if a developer is bad at testing? – That’s something to improve, not outsource to someone else. Even still, the QA engineer isn’t disappearing and they will still offer deeper advice on what tests the developer should perform.</li> -<li>What will QA engineers do if not doing manual acceptance testing? – Automating more. That can be in more end-to-end tests for better regression testing, automating mobile device testing, adding better structures to enable the whole team to automate better, improving CI pipeline, etc.</li> -</ul>Contribute: Code Healthhttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/code-health/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/code-health/ -<p>Healthy code is code that is easy to understand and update. Over time all code becomes less healthy as dependencies become out of date, new language features or code style rules are introduced, and changes are made that add complexity. This is sometimes referred to as technical debt as it makes it progressively harder to make new code changes. No project is ever completely healthy, so we need an ongoing process to improve the least healthy parts of the code so the CHT can be sustainable long term.</p> -<p>A good way to differentiate code health improvements from other changes is if it&rsquo;s something that a user won&rsquo;t notice, then it&rsquo;s code health. Because no user will ever ask for the change to be made they need to be managed outside of the usual prioritisation process.</p> -<p>A good guideline is to spend 25% of your time improving code health, which can be split up into three distinct categories: Tidying, Cards, and Projects.</p> -<h3 id="tidying">Tidying</h3> -<p>These are the smallest improvements and should be part of your work every week. Engineers can decide what tidying they want to prioritize and don&rsquo;t need any approval to do the work, but it will need to go through <a href="https://docs.communityhealthtoolkit.org/contribute/code/workflow/#code-reviews">Code Review</a> to ensure a teammate agrees it&rsquo;s an improvement.</p> -<p>If the tidying is unrelated to other issues you&rsquo;re working on then to make the review easy you should create a new issue, branch, and PR for the tidying work. However if the tidying will conflict or block development of your other work, then use your best judgement about how to proceed in whatever way is easiest for you and your reviewer. Some options are: a) in the same commit if the change is small, b) a separate commit in the same branch if the changes are codependent, c) two branches with one referencing the other.</p> -<p>Examples of appropriate tidying are increasing code reuse, improving readability, reducing complexity, updating a dependency, removing deprecated calls, and increasing test coverage.</p> -<p>Tidying should take around 10% of your time, or 4 hours a week.</p> -<h3 id="cards">Cards</h3> -<p>These are mid-sized improvements and are worked on every quarter. Focused Groups and Product Leadership work together to decide which cards to prioritise and include in the FGs OKRs. The FG is then responsible for ensuring the improvements get scheduled and delivered just like other work.</p> -<p>Cards should take around 10% of your time, or 6 days per quarter.</p> -<h4 id="how-cards-work-in-practice">How Cards work in practice</h4> -<p>A Focused Working Group is building a feature aligned with a quarterly OKR. When the feature is shipped, you may need to wait a week to see results from CHWs. The PM/PO goes through a list of tickets for code health and reviews them with the team. After aligning with the PM/PO, software developers and QA engineers start working on those tickets, tracking progress on the <a href="https://github.com/orgs/medic/projects/134/views/1">GitHub board</a>.</p> -<p>During this time almost all attention, from all engineers in the Focused Working Group, is on heads-down coding on code health tasks. There may still be some other tasks going on like user interviews or initial measurements of the newly deployed feature.</p> -<p>At the end of the week, the team realigns around work towards the chosen outcome. Code health work should be merged and the full focus of the team is together with the PM/PO on delivering improvements for users. It may be the case that a code health task cannot yet be merged. If that is the case, other developers on the Focused Working Group should assist to get it complete so everyone can stay together in what is being worked on.</p> -<h3 id="projects">Projects</h3> -<p>Projects are large refactors that are therefore expensive and risky to undertake. These need to be discussed with Product and Medic leadership and will likely require deep analysis and prototyping to verify the approach.</p> -<p>Examples of projects are upgrading from AngularJS to Angular, rewriting couch2pg as cht-sync, and switching from medic-os to containerized deployment.</p> -<p>Projects should take around 5% of your time, or one project every few years.</p> -<h2 id="more-info">More info</h2> -<p>This policy was inspired by a blog post about <a href="https://shopify.engineering/technical-debt-25-percent-rule">technical debt at Shopify</a>.</p>Contribute: Transparency & Accountabilityhttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/transparency-accountability/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/transparency-accountability/ -<p>It is important that people around Medic, our partners, and community can always see what is being worked on and that commitments are made and kept. There are a few key ways teammates are expected to operate to enable such transparency and accountability.</p> -<h2 id="github-project-boards">GitHub project boards</h2> -<p>Each FWG has a board where tickets are displayed and actively kept up-to-date. These boards are publicly readable and anyone should be able to see the items on the board, what state they are in, and have an expectation of when they will be done.</p> -<p>At the start of the week tickets should be in the &ldquo;In Progress&rdquo; state, which means we are committed to getting it done and into the &ldquo;Done&rdquo; state by the end of the week.</p> -<p>Boards are:</p> -<ul> -<li><a href="https://github.com/orgs/medic/projects/134/views/3">Allies</a></li> -<li><a href="https://github.com/orgs/medic/projects/259/views/14">Care Teams</a></li> -<li><a href="https://github.com/orgs/medic/projects/134/views/20">Community</a></li> -<li><a href="https://github.com/orgs/medic/projects/134/views/11">Ecosystem</a></li> -<li><a href="https://github.com/orgs/medic/projects/134/views/26">Infrastructure</a></li> -</ul> -<h2 id="ticket-updates">Ticket updates</h2> -<p>For any ticket in a state of &ldquo;In Progress&rdquo;, there should be visible progress within 24 hours. Some example scenarios include:</p> -<ol> -<li>A developer has started writing code. This is most likely case and is as easy as linking (draft PRs work great for this!) the code being written to the ticket so observers can see commits being made on a branch. Nobody wants to hassle you about how it&rsquo;s going. Let others self-serve on that.</li> -<li>Comments and &ldquo;thinking out loud&rdquo; on the ticket, especially when no new code is being pushed up. In such a distributed environment, it&rsquo;s hard to tell the difference between nothing happening and someone thinking through tough stuff. Put some of that tough stuff into a comment on the ticket. It lets others know it&rsquo;s tough and also makes it easier for others to help or learn from.</li> -<li>Work needs to happen, but other things came up. Make a quick comment so others know. It may mean that work won&rsquo;t get done this week, and if that&rsquo;s the case, it is probably for good reason. Silence makes it hard for anyone to understand that &ldquo;probably for good reason&rdquo; part though. Help others understand the trade-off you had to make.</li> -<li>Something is blocking progress. First, get that into a comment on the ticket, fast; people can help! Then use additional communication channels to get it unblocked even faster.</li> -</ol> -<h2 id="accountability">Accountability</h2> -<p>People inside and outside the team need to count on things getting done. That means both knowing what the team is doing and seeing that it got done.</p> -<p>In practice, those GitHub project boards have a status representing the commitments for the week. Things that start the week there need to end the week in the &ldquo;done&rdquo; column. This is about setting expectations and holding ourselves accountable to them.</p> -<p>There are times when things are more complicated than imagined, work gets blocked, etc. For those cases we may need to change and adjust on the fly. For the majority of the time though, the expectation is week-over-week we are making and meeting our commitments.</p> \ No newline at end of file +Product Development Process on Community Health Toolkithttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/Recent content in Product Development Process on Community Health ToolkitHugo -- gohugo.ioenContinuous Discovery Overviewhttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/continuous-discovery-overview/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/continuous-discovery-overview/The continuous discovery mindset is one that acknowledges that digital products are never &ldquo;done&rdquo; and can always be made better. The &ldquo;discovery&rdquo; aspect is that those working on the product should empathize with those who use it and operate with those people in mind, both for finding the most impactful areas of work and in how to measure impact. +Teammates choose work with impact in mind and develop solutions with an expectation that people will use what is built and we can measure a positive change.Focused Working Groupshttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/focused-groups/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/focused-groups/The Product team as a whole is focused on digital health tools helping to achieve Universal Health Coverage, and care reaching everyone when and where they need it. The team is made up of 4 different groups, with each group focusing on outcomes to serve different people. +See the overall Product Team Roadmap. +Care Teams Focused on building effective tools to provide care in the hardest-to-reach communities. +Key users: Patients, Families, Caregivers, Community Health Workers, Supervisors.Schedule of Activitieshttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/schedule-of-events/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/schedule-of-events/Annually Set goals (strategies and measures) Identify desired outcomes the team will work towards Quarterly Choose outcome(s) for Focused Working Groups to work towards Outcome kickoff Monthly Communicate status and progress of work towards current outcome(s) Recognize and establish certainty around time sensitive, project-initiated tasks Weekly Conduct interviews Continued iterative work on identifying opportunities, weighing possible solutions, building, and measuring Triage potential side-loaded opportunitiesQuality Assistancehttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/quality-assistance/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/quality-assistance/Goals Software developers should have full ownership of what they are building, including quality. The team still benefits from having the QA engineering mindset of QA engineers. QA engineers automate more tests and processes. In Short A software developer writes code and performs testing on that code where a QA engineer assists by recommending tests to perform and adding additional end-to-end tests. +In Detail A developer should be able to write code and release it when done.Code Healthhttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/code-health/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/code-health/Healthy code is code that is easy to understand and update. Over time all code becomes less healthy as dependencies become out of date, new language features or code style rules are introduced, and changes are made that add complexity. This is sometimes referred to as technical debt as it makes it progressively harder to make new code changes. No project is ever completely healthy, so we need an ongoing process to improve the least healthy parts of the code so the CHT can be sustainable long term.Transparency & Accountabilityhttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/transparency-accountability/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/transparency-accountability/It is important that people around Medic, our partners, and community can always see what is being worked on and that commitments are made and kept. There are a few key ways teammates are expected to operate to enable such transparency and accountability. +GitHub project boards Each FWG has a board where tickets are displayed and actively kept up-to-date. These boards are publicly readable and anyone should be able to see the items on the board, what state they are in, and have an expectation of when they will be done. \ No newline at end of file diff --git a/contribute/medic/product-development-process/product-trio/index.html b/contribute/medic/product-development-process/product-trio/index.html index aa20fb73d1..3d8f4ba7fa 100644 --- a/contribute/medic/product-development-process/product-trio/index.html +++ b/contribute/medic/product-development-process/product-trio/index.html @@ -1,9 +1,9 @@ -Product Trio | Community Health Toolkit +Product Trio | Community Health Toolkit

    Product Trio

    What is a Product Trio

    A Product Trio refers to a product manager, a product designer and a software engineer working together to develop digital products in the scope of a Focused Working Group. Some Focused Working Groups have multiple software engineers contributing to the work. Additionally, other roles contribute to the Trio, such as UX Researcher and Quality Assuarance Engineer. In that sense, Product Trios are often a quartet or a quintet.


    Product Trio Activities

    Common Activities of the Product Trio

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/medic/product-development-process/product-trio/index.xml b/contribute/medic/product-development-process/product-trio/index.xml index dc7786b6d8..628f82a3ae 100644 --- a/contribute/medic/product-development-process/product-trio/index.xml +++ b/contribute/medic/product-development-process/product-trio/index.xml @@ -1,33 +1,2 @@ -Community Health Toolkit – Product Triohttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/product-trio/Recent content in Product Trio on Community Health ToolkitHugo -- gohugo.ioenContribute: Product Trio Activitieshttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/product-trio/product-trio-activities/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/product-trio/product-trio-activities/ -<p>The following items represent common activities that members of a Product Trio will do in the Product Development Process. Depending on what is underway, some of these may happen each week, but many parts happen on-demand. For example, interviewing is an ongoing process of gathering information, but the group may not start building a new solution each week.</p> -<p>The following activities should be performed in order, by all trio members:</p> -<h2 id="empathize">Empathize</h2> -<ul> -<li><strong>Partner alignment</strong>: Understand initiatives Partners are planning in their roadmap and identify areas of overlap across health programs</li> -<li><strong>User interviews</strong>: Learn about end users’ recent experiences with the CHT and any workarounds they’ve used to uncover potential unknowns</li> -<li><strong>Learning agendas</strong>: Scoped interviews to learn more about specific topics of interest</li> -<li><strong>Partner working sessions</strong>: Understand how users arrived at their current issues and offer guidance in troubleshooting</li> -<li><strong>Service design feedback</strong>: Learn about feedback they have heard in the field</li> -<li><strong>Customer effort survey</strong>: Align with Community’s App Developer survey to understand the ease of use of doing key tasks with the CHT using a scale to understand the ease of each</li> -</ul> -<h2 id="define">Define</h2> -<ul> -<li><strong>Synthesis session</strong>: Nuggets collected from the Empathize phase are synthesized into Opportunity Statements, broken down into subcategories if necessary</li> -<li><strong>Define desired outcome</strong>: Align on what impact to achieve based on MoH needs and what the synthesis sessions have uncovered as our most impactful opportunity and how we might measure it</li> -</ul> -<h2 id="ideate">Ideate</h2> -<ul> -<li><strong>Story mapping</strong>: Brainstorm different ways we might achieve our desired outcome by arranging user stories to outline how they fit into the overall user experience from the first interaction to completing the overall user objective.</li> -<li><strong>Choose solution</strong>: Discuss story maps, vote for the solution that is likeliest to achieve desired outcome</li> -<li><strong>Validate with product leadership</strong>: Loop in product leadership for gut check and to ensure full alignment</li> -</ul> -<h2 id="prototype-and-test">Prototype and test</h2> -<ul> -<li><strong>Validate</strong> assumptions: Create mockups to usability test with end users</li> -<li><strong>Iterate</strong>: Update mockups as per user feedback</li> -</ul> -<h2 id="implement-and-measure">Implement and measure</h2> -<ul> -<li><strong>Build</strong>: Build the thing and get it deployed</li> -<li><strong>Measure</strong>: Use metrics previously defined to measure impact</li> -</ul> \ No newline at end of file +Product Trio on Community Health Toolkithttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/product-trio/Recent content in Product Trio on Community Health ToolkitHugo -- gohugo.ioenProduct Trio Activitieshttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/product-trio/product-trio-activities/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/product-trio/product-trio-activities/The following items represent common activities that members of a Product Trio will do in the Product Development Process. Depending on what is underway, some of these may happen each week, but many parts happen on-demand. For example, interviewing is an ongoing process of gathering information, but the group may not start building a new solution each week. +The following activities should be performed in order, by all trio members: \ No newline at end of file diff --git a/contribute/medic/product-development-process/product-trio/product-trio-activities/index.html b/contribute/medic/product-development-process/product-trio/product-trio-activities/index.html index db9cf39b65..18fe4ed16d 100644 --- a/contribute/medic/product-development-process/product-trio/product-trio-activities/index.html +++ b/contribute/medic/product-development-process/product-trio/product-trio-activities/index.html @@ -1,9 +1,9 @@ -Product Trio Activities | Community Health Toolkit +Product Trio Activities | Community Health Toolkit

    Product Trio Activities

    Common Activities of the Product Trio

    The following items represent common activities that members of a Product Trio will do in the Product Development Process. Depending on what is underway, some of these may happen each week, but many parts happen on-demand. For example, interviewing is an ongoing process of gathering information, but the group may not start building a new solution each week.

    The following activities should be performed in order, by all trio members:

    Empathize

    • Partner alignment: Understand initiatives Partners are planning in their roadmap and identify areas of overlap across health programs
    • User interviews: Learn about end users’ recent experiences with the CHT and any workarounds they’ve used to uncover potential unknowns
    • Learning agendas: Scoped interviews to learn more about specific topics of interest
    • Partner working sessions: Understand how users arrived at their current issues and offer guidance in troubleshooting
    • Service design feedback: Learn about feedback they have heard in the field
    • Customer effort survey: Align with Community’s App Developer survey to understand the ease of use of doing key tasks with the CHT using a scale to understand the ease of each

    Define

    • Synthesis session: Nuggets collected from the Empathize phase are synthesized into Opportunity Statements, broken down into subcategories if necessary
    • Define desired outcome: Align on what impact to achieve based on MoH needs and what the synthesis sessions have uncovered as our most impactful opportunity and how we might measure it

    Ideate

    • Story mapping: Brainstorm different ways we might achieve our desired outcome by arranging user stories to outline how they fit into the overall user experience from the first interaction to completing the overall user objective.
    • Choose solution: Discuss story maps, vote for the solution that is likeliest to achieve desired outcome
    • Validate with product leadership: Loop in product leadership for gut check and to ensure full alignment

    Prototype and test

    • Validate assumptions: Create mockups to usability test with end users
    • Iterate: Update mockups as per user feedback

    Implement and measure

    • Build: Build the thing and get it deployed
    • Measure: Use metrics previously defined to measure impact
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/medic/product-development-process/quality-assistance/index.html b/contribute/medic/product-development-process/quality-assistance/index.html index 750208db7f..9250e1a907 100644 --- a/contribute/medic/product-development-process/quality-assistance/index.html +++ b/contribute/medic/product-development-process/quality-assistance/index.html @@ -1,9 +1,9 @@ -Quality Assistance | Community Health Toolkit +Quality Assistance | Community Health Toolkit

    Quality Assistance

    How the Quality Assistance process works at Medic

    Goals

    1. Software developers should have full ownership of what they are building, including quality.
    2. The team still benefits from having the QA engineering mindset of QA engineers.
    3. QA engineers automate more tests and processes.

    In Short

    A software developer writes code and performs testing on that code where a QA engineer assists by recommending tests to perform and adding additional end-to-end tests.

    In Detail

    A developer should be able to write code and release it when done. Doing that without testing would be reckless and it is expected that the developer also tests their code. The team wants both speed and quality where the former should not be done without the latter. This means slowing down a bit to make sure it’s right and checking your work when developing before it is done/reviewed/merged.

    It isn’t always easy to test your own code though and it is common for a developer to have a happy-path mindset, more suited for thinking about what to build rather than how something might break. Fortunately there are QA engineers on the team who have the skills to think about the solution from the perspective of what might break.

    It’s worth noting that the team also requires code reviews via GitHub pull requests. The intention of code reviews is not for the reviewer to test the code. The reviewer may optionally also do that, but it is expected that the author of the code has tested it fully before requesting a code review.

    Once code is done, tested (by the developer) and reviewed, it is ready to merge and the developer can do so… almost (see Getting There in Milestones).

    Getting There in Milestones

    The aim is to get to a place where the developer of a change can do everything and merge without needing any extra permission. To make the process safer in getting to the final step, several parts will be followed. A process similar to what’s described In Detail is followed, while retaining an “AT” step similar to what the current process.

    The big difference to that AT step though will be the depth in which it is performed. Rather than being a single exhaustive step where all testing is performed, it will be more like a smoke test where a QA engineer gets a little extra creative in testing.

    There may still be cases of a ticket getting coded up totally unrelated to Focused Working Group work, perhaps even unannounced to anyone. For work like this the traditional AT step is still appropriate for now. Still though, it is preferred for the software developer to get a QA engineer involved as early as possible; it helps us get the best contributions from everyone.

    An Example of QA Assistance in Practice

    Imagine a scenario where a Focused Working Group is going to change the display of the targets screen. The group would discuss the needs of the users and work out solutions, where the selected solution was to make this change. This discussion would involve a PM/PO, designer, developer, and QA engineer. The developer would be thinking about how to code the proposed solution and the QA engineer would be considering that as well as what might go wrong.

    In the case where a UI change is being made, a design will be created. At this point the group can see what is to be built. When meeting and discussing the user interactions with the designer the QA engineer can get some early ideas around test scenarios.

    As code is written and pushed to a branch the QA engineer and developer talk about what is changing. Any misconceptions are cleared up and the QA engineer can start to assist the developer by pointing out areas that may be important or non-obvious to test. This could be as simple as noting challenges of different screen sizes or as complex as specific configurations that may need extra attention for how targets work.

    That conversation (ideally multiple of these) is the “assistance”. It’s where the value of the QA engineering mindset is achieved. The developer still owns and tests their work; they just have a QA engineer to talk to so they can feel confident in their own testing.

    Depending on the change the developer may work with the QA engineer where the QA engineer can contribute some automated end-to-end testing to the branch. Things should feel collaborative and not a division of labor or handing things off.

    Once the developer has finished writing the code they test it a bit more and open a PR for a code review. The reviewer should be able to review the code with an assumption that it is well-tested already and the focus of the review can be around the code and implementation choices.

    If any changes need to be made during the review process, the developer makes those changes and re-executes any relevant testing.

    At this point the ideal action to take would be that the developer merges the finished code and no AT step happens, as the quality is already baked in from the start and throughout. This is the ideal setup yet to be achieved. Here the ticket still goes to AT, but for a smaller last bit of smoke testing. Notice that it is not an exhaustive set of tests, it is a small bit of extra poking around by a QA engineer. This limited testing is possible because the developer said it is done, and they said that because they tested it, and they had a QA engineer assist them to reach that level of confidence in their own testing.

    The last part here is to merge it. That extra poking around should be quick, so the developer should be ready to click the green button soon!

    But, but, but!!! (some questions you might have)

    • Who checks if the right thing got built? – The Focused Working Group members should be aware of what’s being built, why, and if it’s coming together as expected. That’s not to be solely delegated to a QA engineer to do. Developers should be working with their teammates and showing their work (demos, screenshots, etc). This should feel like a team collaborating to build useful working software, not an assembly line of disassociated parts.
    • What if a developer is bad at testing? – That’s something to improve, not outsource to someone else. Even still, the QA engineer isn’t disappearing and they will still offer deeper advice on what tests the developer should perform.
    • What will QA engineers do if not doing manual acceptance testing? – Automating more. That can be in more end-to-end tests for better regression testing, automating mobile device testing, adding better structures to enable the whole team to automate better, improving CI pipeline, etc.
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/medic/product-development-process/schedule-of-events/index.html b/contribute/medic/product-development-process/schedule-of-events/index.html index 0c25c024c8..ab82664661 100644 --- a/contribute/medic/product-development-process/schedule-of-events/index.html +++ b/contribute/medic/product-development-process/schedule-of-events/index.html @@ -1,9 +1,9 @@ -Schedule of Activities | Community Health Toolkit +Schedule of Activities | Community Health Toolkit

    Schedule of Activities

    Schedule of activities for Focused Working Groups

    Annually

    • Set goals (strategies and measures)
    • Identify desired outcomes the team will work towards

    Quarterly

    • Choose outcome(s) for Focused Working Groups to work towards
    • Outcome kickoff

    Monthly

    • Communicate status and progress of work towards current outcome(s)
    • Recognize and establish certainty around time sensitive, project-initiated tasks

    Weekly

    • Conduct interviews
    • Continued iterative work on identifying opportunities, weighing possible solutions, building, and measuring
    • Triage potential side-loaded opportunities
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/medic/product-development-process/transparency-accountability/index.html b/contribute/medic/product-development-process/transparency-accountability/index.html index b7dd6925c3..d073823636 100644 --- a/contribute/medic/product-development-process/transparency-accountability/index.html +++ b/contribute/medic/product-development-process/transparency-accountability/index.html @@ -1,9 +1,9 @@ -Transparency & Accountability | Community Health Toolkit +Transparency & Accountability | Community Health Toolkit

    Transparency & Accountability

    How we make it easy for others to know what’s going on and getting done.

    It is important that people around Medic, our partners, and community can always see what is being worked on and that commitments are made and kept. There are a few key ways teammates are expected to operate to enable such transparency and accountability.

    GitHub project boards

    Each FWG has a board where tickets are displayed and actively kept up-to-date. These boards are publicly readable and anyone should be able to see the items on the board, what state they are in, and have an expectation of when they will be done.

    At the start of the week tickets should be in the “In Progress” state, which means we are committed to getting it done and into the “Done” state by the end of the week.

    Boards are:

    Ticket updates

    For any ticket in a state of “In Progress”, there should be visible progress within 24 hours. Some example scenarios include:

    1. A developer has started writing code. This is most likely case and is as easy as linking (draft PRs work great for this!) the code being written to the ticket so observers can see commits being made on a branch. Nobody wants to hassle you about how it’s going. Let others self-serve on that.
    2. Comments and “thinking out loud” on the ticket, especially when no new code is being pushed up. In such a distributed environment, it’s hard to tell the difference between nothing happening and someone thinking through tough stuff. Put some of that tough stuff into a comment on the ticket. It lets others know it’s tough and also makes it easier for others to help or learn from.
    3. Work needs to happen, but other things came up. Make a quick comment so others know. It may mean that work won’t get done this week, and if that’s the case, it is probably for good reason. Silence makes it hard for anyone to understand that “probably for good reason” part though. Help others understand the trade-off you had to make.
    4. Something is blocking progress. First, get that into a comment on the ticket, fast; people can help! Then use additional communication channels to get it unblocked even faster.

    Accountability

    People inside and outside the team need to count on things getting done. That means both knowing what the team is doing and seeing that it got done.

    In practice, those GitHub project boards have a status representing the commitments for the week. Things that start the week there need to end the week in the “done” column. This is about setting expectations and holding ourselves accountable to them.

    There are times when things are more complicated than imagined, work gets blocked, etc. For those cases we may need to change and adjust on the fly. For the majority of the time though, the expectation is week-over-week we are making and meeting our commitments.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/medic/product-development-process/ux-research-repo/about-interviews/index.html b/contribute/medic/product-development-process/ux-research-repo/about-interviews/index.html index 7b89652625..5583933e2f 100644 --- a/contribute/medic/product-development-process/ux-research-repo/about-interviews/index.html +++ b/contribute/medic/product-development-process/ux-research-repo/about-interviews/index.html @@ -1,9 +1,9 @@ -About Interviews | Community Health Toolkit +About Interviews | Community Health Toolkit

    About Interviews

    How to organize interviews in the UX Research Repository
    1. Be sure to record the interview (audio and/or video)
    2. Name the file like “2022-06-05 Interview with Julius Nyerere from MoH Furahi
    3. Upload the file to Google Drive here (private link).
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/medic/product-development-process/ux-research-repo/adding-nuggets/index.html b/contribute/medic/product-development-process/ux-research-repo/adding-nuggets/index.html index 8acb704382..40684a5007 100644 --- a/contribute/medic/product-development-process/ux-research-repo/adding-nuggets/index.html +++ b/contribute/medic/product-development-process/ux-research-repo/adding-nuggets/index.html @@ -1,9 +1,9 @@ -Adding Nuggets | Community Health Toolkit +Adding Nuggets | Community Health Toolkit

    Adding Nuggets

    How to add nuggets in the UX Research Repository

    The majority of research observations will come from interviews with CHT users. Here’s how you will enter these observations, called “Nuggets”, into the UX Research Repo.

    After conducting your interview, copy the video recording to Google Drive (more info here). You’ll need to reference this later.

    High Level Steps

    1. Add the Organization (of the person you interviewed) if it doesn’t already exist
    2. Create the Interview Subject if this is the first time we’ve interviewed or entered observations from this person.
    3. Create a record for the Sample of the recording.
    4. Add research observations a.k.a. “Nuggets”!

    Ready to see how this looks like? See a demo.

    To gain access to the UX Research Repo and browse through existing nuggets, please reach out in the #product-management Slack channel. The access to the repository is currently restricted to the Medic team.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/medic/product-development-process/ux-research-repo/index.html b/contribute/medic/product-development-process/ux-research-repo/index.html index b1a229f4cb..ad4d2e4a9e 100644 --- a/contribute/medic/product-development-process/ux-research-repo/index.html +++ b/contribute/medic/product-development-process/ux-research-repo/index.html @@ -1,9 +1,9 @@ -UX Research Repository | Community Health Toolkit +UX Research Repository | Community Health Toolkit

    UX Research Repository

    How to keep track of product research

    The Medic Product team uses baserow.io as a place to keep product research. Baserow is very similar to airtable.com, getgrist.com, and Google Sheets.

    The UX Research Repository is based off of concepts from WeWork’s Polaris project. You can see an example of the Polaris (which uses Airtable) here.

    The Baserow interface can be seen below. The items in the list on the left are “tables” that we have created. On the right is the “Grid”. It looks just like a spreadsheet and you can have multiple views of the same grid.

    The Baserow interface 👆🏼

    To gain access to the UX Research Repo, please reach out in the #product-owner-chatter Slack channel. The access to the repository is currently restricted to the Medic team.


    Key Concepts

    Baserow Key Concepts

    About Interviews

    How to organize interviews in the UX Research Repository

    Adding Nuggets

    How to add nuggets in the UX Research Repository

    Synthesizing Nuggets

    How to synthesize nuggets in the UX Research Repository to identify insights

    Publishing Insights

    How to share quarterly insights with the internal team and wider community

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/medic/product-development-process/ux-research-repo/index.xml b/contribute/medic/product-development-process/ux-research-repo/index.xml index c88b0a8340..107061a6e4 100644 --- a/contribute/medic/product-development-process/ux-research-repo/index.xml +++ b/contribute/medic/product-development-process/ux-research-repo/index.xml @@ -1,89 +1,8 @@ -Community Health Toolkit – UX Research Repositoryhttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/Recent content in UX Research Repository on Community Health ToolkitHugo -- gohugo.ioenContribute: Key Conceptshttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/key-concepts/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/key-concepts/ -<p>There are a number of Baserow key concepts to understand before diving in. These are specific to how we’ve chosen to implement the repository.</p> -<h2 id="interview-subject">Interview Subject</h2> -<p>This is the person that was interviewed or otherwise provided information/feedback.</p> -<p><strong>Example</strong>: <em>Julius Nyerere</em></p> -<h2 id="organization">Organization</h2> -<p>The company that the “Interview Subject” belongs to or associates with.</p> -<p><strong>Example</strong>: <em>MoH Furahi</em></p> -<h2 id="samples">Samples</h2> -<p>These are links to, and metadata about, the “Evidence”. These are typically audio or video recordings stored on google drive, but can also be links to PDFs, forum posts, etc..</p> -<p><strong>Example</strong>: <em>Audio recording of a generative interview with Julius on 9-June-2021</em></p> -<h2 id="nuggets">Nuggets</h2> -<p>An observation gathered through research or an atomic unit of research insight.</p> -<p><strong>Example</strong>: <em>Users can’t easily find who they’re looking for</em></p> -<h2 id="insights">Insights</h2> -<p>These are identified during quarterly synthesis sessions by grouping nuggets into themes and developing problem statements around them. Each individual nugget is then tagged to an insight.</p> -<p><strong>Example</strong>: <em>Julius tapped on the X icon many times before the window closed. He seemed frustrated.</em></p> -<h2 id="leads">Leads</h2> -<p>Product suggestions or requests that did <em>not</em> necessarily come directly from a product trio member interviewing or observing a user. These are often taken from forum posts or other 3rd party means.</p> -<p><strong>Example</strong>: <em>Forum Post: It would be nice if the CHT used comic sans font</em></p> -<p><img src="../images/concepts.png" alt="Baserow Concepts"></p>Contribute: About Interviewshttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/about-interviews/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/about-interviews/ -<ol> -<li>Be sure to record the interview (audio and/or video)</li> -<li>Name the file like “<em>2022-06-05 Interview with Julius Nyerere from MoH Furahi</em>”</li> -<li>Upload the file to Google Drive <a href="https://drive.google.com/drive/folders/1bcCxQYerwDcZHqAD3d0NGPxuDxLapadb">here</a> (private link).</li> -</ol>Contribute: Adding Nuggetshttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/adding-nuggets/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/adding-nuggets/ -<p>The majority of research observations will come from interviews with CHT users. Here’s how you will enter these observations, called “Nuggets”, into the UX Research Repo.</p> -<p>After conducting your interview, copy the video recording to Google Drive (more info <a href="https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/about-interviews/">here</a>). You’ll need to reference this later.</p> -<h2 id="high-level-steps">High Level Steps</h2> -<ol> -<li>Add the <strong>Organization</strong> (<em>of the person you interviewed</em>) if it doesn’t already exist</li> -<li>Create the <strong>Interview Subject</strong> if this is the first time we’ve interviewed or entered observations from this person.</li> -<li>Create a record for the <strong>Sample</strong> of the recording.</li> -<li>Add research observations a.k.a. “<strong>Nuggets”!</strong></li> -</ol> -<p>Ready to see how this looks like? See a <a href="https://drive.google.com/file/d/1YPXoba9gVmD7SP-X88PpJIsIVGvY86_G/view?usp=share_link">demo</a>.</p> -<p>To gain access to the UX Research Repo and browse through existing nuggets, please reach out in the #product-management Slack channel. The access to the repository is currently restricted to the Medic team.</p>Contribute: Synthesizing Nuggetshttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/synthesizing-nuggets/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/synthesizing-nuggets/ -<ol> -<li>Product owners review new Nuggets weekly -<ol> -<li>Deal with duplicates</li> -</ol> -</li> -<li>Product trio and stakeholders review Nuggets quarterly -<ol> -<li>Facilitator creates a new &ldquo;Grid&rdquo; on the Nuggets table with the date of the workshop</li> -<li>Facilitator applies filters to identify Nuggets to be reviewed -<ol> -<li>Date (last synthesis workshop to current date)</li> -<li>Focused group (Care or Allies)</li> -<li>Not already associated to an insight</li> -<li>Export filtered Nuggets to CSV</li> -</ol> -</li> -<li>Make copy of latest Miro board and import CSV as stickies</li> -<li>Product trio and stakeholders discuss and sort stickies into existing or new themes</li> -<li>Define problem statements per theme as insights</li> -<li>Return to Baserow and add insights to corresponding nuggets</li> -</ol> -</li> -</ol>Contribute: Publishing Insightshttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/publishing-insights/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/publishing-insights/ -<ol> -<li>Product owners and UXR review insights dashboard for each Focused Working Group in Klipfolio on a quarterly basis to share: -<ol> -<li>Number of interviews conducted</li> -<li>Common themes</li> -<li>Infographics of interviews to date</li> -<li>Table of insights and number of stories associated with each</li> -<li>Emerging themes</li> -</ol> -</li> -<li>Share on platforms -<ol> -<li>Internal -<ol> -<li>All hands</li> -<li>Slack</li> -</ol> -</li> -<li>External -<ol> -<li>CHT Round Up Call</li> -<li>Forum</li> -</ol> -</li> -</ol> -</li> -</ol> -<p>Here’s an example <a href="https://forum.communityhealthtoolkit.org/t/2023-q1-user-research-findings/2650">forum post</a>.</p> \ No newline at end of file +UX Research Repository on Community Health Toolkithttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/Recent content in UX Research Repository on Community Health ToolkitHugo -- gohugo.ioenKey Conceptshttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/key-concepts/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/key-concepts/There are a number of Baserow key concepts to understand before diving in. These are specific to how we’ve chosen to implement the repository. +Interview Subject This is the person that was interviewed or otherwise provided information/feedback. +Example: Julius Nyerere +Organization The company that the “Interview Subject” belongs to or associates with. +Example: MoH Furahi +Samples These are links to, and metadata about, the “Evidence”. These are typically audio or video recordings stored on google drive, but can also be links to PDFs, forum posts, etc.About Interviewshttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/about-interviews/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/about-interviews/ Be sure to record the interview (audio and/or video) Name the file like “2022-06-05 Interview with Julius Nyerere from MoH Furahi” Upload the file to Google Drive here (private link).Adding Nuggetshttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/adding-nuggets/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/adding-nuggets/The majority of research observations will come from interviews with CHT users. Here’s how you will enter these observations, called “Nuggets”, into the UX Research Repo. +After conducting your interview, copy the video recording to Google Drive (more info here). You’ll need to reference this later. +High Level Steps Add the Organization (of the person you interviewed) if it doesn’t already exist Create the Interview Subject if this is the first time we’ve interviewed or entered observations from this person.Synthesizing Nuggetshttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/synthesizing-nuggets/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/synthesizing-nuggets/ Product owners review new Nuggets weekly Deal with duplicates Product trio and stakeholders review Nuggets quarterly Facilitator creates a new &ldquo;Grid&rdquo; on the Nuggets table with the date of the workshop Facilitator applies filters to identify Nuggets to be reviewed Date (last synthesis workshop to current date) Focused group (Care or Allies) Not already associated to an insight Export filtered Nuggets to CSV Make copy of latest Miro board and import CSV as stickies Product trio and stakeholders discuss and sort stickies into existing or new themes Define problem statements per theme as insights Return to Baserow and add insights to corresponding nuggetsPublishing Insightshttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/publishing-insights/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/publishing-insights/Product owners and UXR review insights dashboard for each Focused Working Group in Klipfolio on a quarterly basis to share: Number of interviews conducted Common themes Infographics of interviews to date Table of insights and number of stories associated with each Emerging themes Share on platforms Internal All hands Slack External CHT Round Up Call Forum Here’s an example forum post. \ No newline at end of file diff --git a/contribute/medic/product-development-process/ux-research-repo/key-concepts/index.html b/contribute/medic/product-development-process/ux-research-repo/key-concepts/index.html index 92001d6c4c..9e40165770 100644 --- a/contribute/medic/product-development-process/ux-research-repo/key-concepts/index.html +++ b/contribute/medic/product-development-process/ux-research-repo/key-concepts/index.html @@ -1,9 +1,9 @@ -Key Concepts | Community Health Toolkit +Key Concepts | Community Health Toolkit

    Key Concepts

    Baserow Key Concepts

    There are a number of Baserow key concepts to understand before diving in. These are specific to how we’ve chosen to implement the repository.

    Interview Subject

    This is the person that was interviewed or otherwise provided information/feedback.

    Example: Julius Nyerere

    Organization

    The company that the “Interview Subject” belongs to or associates with.

    Example: MoH Furahi

    Samples

    These are links to, and metadata about, the “Evidence”. These are typically audio or video recordings stored on google drive, but can also be links to PDFs, forum posts, etc..

    Example: Audio recording of a generative interview with Julius on 9-June-2021

    Nuggets

    An observation gathered through research or an atomic unit of research insight.

    Example: Users can’t easily find who they’re looking for

    Insights

    These are identified during quarterly synthesis sessions by grouping nuggets into themes and developing problem statements around them. Each individual nugget is then tagged to an insight.

    Example: Julius tapped on the X icon many times before the window closed. He seemed frustrated.

    Leads

    Product suggestions or requests that did not necessarily come directly from a product trio member interviewing or observing a user. These are often taken from forum posts or other 3rd party means.

    Example: Forum Post: It would be nice if the CHT used comic sans font

    Baserow Concepts

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/medic/product-development-process/ux-research-repo/publishing-insights/index.html b/contribute/medic/product-development-process/ux-research-repo/publishing-insights/index.html index e030a971f1..881d2145ff 100644 --- a/contribute/medic/product-development-process/ux-research-repo/publishing-insights/index.html +++ b/contribute/medic/product-development-process/ux-research-repo/publishing-insights/index.html @@ -1,9 +1,9 @@ -Publishing Insights | Community Health Toolkit +Publishing Insights | Community Health Toolkit

    Publishing Insights

    How to share quarterly insights with the internal team and wider community
    1. Product owners and UXR review insights dashboard for each Focused Working Group in Klipfolio on a quarterly basis to share:
      1. Number of interviews conducted
      2. Common themes
      3. Infographics of interviews to date
      4. Table of insights and number of stories associated with each
      5. Emerging themes
    2. Share on platforms
      1. Internal
        1. All hands
        2. Slack
      2. External
        1. CHT Round Up Call
        2. Forum

    Here’s an example forum post.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/medic/product-development-process/ux-research-repo/synthesizing-nuggets/index.html b/contribute/medic/product-development-process/ux-research-repo/synthesizing-nuggets/index.html index 4a059dbe7d..649e8bbdec 100644 --- a/contribute/medic/product-development-process/ux-research-repo/synthesizing-nuggets/index.html +++ b/contribute/medic/product-development-process/ux-research-repo/synthesizing-nuggets/index.html @@ -1,9 +1,9 @@ -Synthesizing Nuggets | Community Health Toolkit +Synthesizing Nuggets | Community Health Toolkit

    Synthesizing Nuggets

    How to synthesize nuggets in the UX Research Repository to identify insights
    1. Product owners review new Nuggets weekly
      1. Deal with duplicates
    2. Product trio and stakeholders review Nuggets quarterly
      1. Facilitator creates a new “Grid” on the Nuggets table with the date of the workshop
      2. Facilitator applies filters to identify Nuggets to be reviewed
        1. Date (last synthesis workshop to current date)
        2. Focused group (Care or Allies)
        3. Not already associated to an insight
        4. Export filtered Nuggets to CSV
      3. Make copy of latest Miro board and import CSV as stickies
      4. Product trio and stakeholders discuss and sort stickies into existing or new themes
      5. Define problem statements per theme as insights
      6. Return to Baserow and add insights to corresponding nuggets
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/contribute/tech-radar/index.html b/contribute/tech-radar/index.html index 668a53a5ea..831279f69f 100644 --- a/contribute/tech-radar/index.html +++ b/contribute/tech-radar/index.html @@ -1,9 +1,9 @@ -CHT Technology Radars | Community Health Toolkit +CHT Technology Radars | Community Health Toolkit

    CHT Technology Radars

    CHT Technology Radars for Contributors and Implementers

    It is essential for a development toolkit such as the Community Health Toolkit to constantly improve and keep track with the latest useful innovations. It is important to openly look for innovations and new technologies and to question established technologies and methods every now and then.

    To enhance visibility and clarity on the technology choices, the technological strategy, and the available CHT features and tools, we leverage a framework called Technology Radar.

    A Technology Radar provides an easy-to-grasp visual representation of tools, languages, frameworks, platforms, and techniques, as well as features and functionalities we use to build the CHT. Additionally, the Technology Radar provides a degree of adoption and guidelines on using (or not using) a particular technology. The community can leverage it to answer questions like: What’s the difference between technologies such as Klipfolio, Superset, and Grafana? Should we use couch2pg or cht-sync for our project?

    While reflecting on the audience of such a tool, we’ve identified the need to build two separate versions: a CHT Technology Radar for Implementers and a CHT Technology Radar for Contributors. We leverage existing open-source tools by ThoughtWorks and AOE to implement the user interface. Due to our commitment to open-source, all the content of the Technology Radars is public on GitHub and open to the community to provide comments and suggestions.

    For Implementers

    The CHT Technology Radar for Implementers provides a view of all CHT-related tools and components so that CHT implementers and application developers can decide which to use when designing, developing, and hosting their CHT applications.

    We categorized this radar’s content into four quadrants:

    • App Building: These components help build CHT apps, like CHT Conf, and form preview/builder tools like XLSForm.
    • CHT App Features: Aspects of CHT Core Framework that can be used in CHT Apps. These could include new CHT form widgets, updated UI elements like the floating action button, and user management features.
    • Data Use: This includes tools and components used to manage data that comes in or out of the CHT. It also includes integrations, interoperability standards and methods, ETL, Pipeline, and querying techniques. Additionally, this quadrant contains visualization tools for dashboards.
    • Deployment Management: Deploying and maintaining CHT instances takes considerable coordination, especially for large-scale deployments. This quadrant also includes key aspects for hosting, including tools and methods for alerts and monitoring.

    We classify each of the items above in one of these rings:

    • Adopt: The Adopt ring represents tools that you should seriously consider using. We don’t say you should use these for every project; one should only use a tool in an appropriate context. However, an item in the Adopt ring represents something where there’s no doubt it’s proven and mature for use with the CHT.
    • Trial: The Trial ring is for tools ready for use but only partially proven as those in the Adopt ring. You should use these on a trial basis to decide whether they should be part of your toolkit. Others may already be using these items in production, likely as early adopters of the tools.
    • Stop: The Stop (or Not Recommended) ring is for things we think implementers should avoid using or look for ways to move off of. These include items for which a better alternative is available, or where the item is found to not work correctly with the CHT.

    You can learn how to contribute to the CHT Technical Radar for Implementers in the related GitHub repository.

    For Contributors

    The CHT Technology Radar for Contributors provides a helpful view for developers to know what languages, tools, platforms, or techniques to use while contributing to CHT tools.

    We categorized this radar’s content into four quadrants:

    • Languages & Frameworks: Include development languages and more low-level development frameworks, which help implement custom software of all kinds.
    • Tools: These can be components, like databases, software development tools, such as version control systems.
    • Techniques: Include elements of a software development process, such as continuous integration, and ways of creating software, such as progressive web applications.
    • Platforms: There are things that we build software on top of, such as mobile technologies like Android or generic kinds of platforms like Amazon Web Services.

    We classify each of the items above in one of these rings:

    • Adopt, Trial, Stop. These adoption degrees have the same usage recommendation as the ones for the CHT Technology Radar for Implementers above.
    • Assess: The Assess ring contains items to look at closely, but not necessarily trial yet - unless you think they would be a particularly good fit for you. Typically, items in the Assess ring are interesting and worth keeping an eye on.

    You can learn how to contribute to the CHT Technical Radar for Contributors in the related GitHub repository.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/index.html b/core/index.html index 9a169d7432..20ead577fa 100644 --- a/core/index.html +++ b/core/index.html @@ -1,9 +1,9 @@ -CHT Core Framework | Community Health Toolkit +CHT Core Framework | Community Health Toolkit
    \ No newline at end of file diff --git a/core/index.xml b/core/index.xml index 875e473185..04d2481336 100644 --- a/core/index.xml +++ b/core/index.xml @@ -1 +1 @@ -Community Health Toolkit – CHT Core Frameworkhttps://docs.communityhealthtoolkit.org/core/Recent content in CHT Core Framework on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file +CHT Core Framework on Community Health Toolkithttps://docs.communityhealthtoolkit.org/core/Recent content in CHT Core Framework on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file diff --git a/core/overview/architecture/index.html b/core/overview/architecture/index.html index 80245e6621..68c838d899 100644 --- a/core/overview/architecture/index.html +++ b/core/overview/architecture/index.html @@ -1,9 +1,9 @@ -Architecture of CHT Instances | Community Health Toolkit +Architecture of CHT Instances | Community Health Toolkit

    Architecture of CHT Instances

    The different pieces of a CHT project, how they interact, and what they’re used for

    Overview

    Data Flows

    Server

    CHT Core Framework

    The cht-core product is the primary component of the CHT. The server comes with authentication, role based authorization, data security, and a range of protected data access endpoints. Read more detail in cht-core GitHub repository.

    API

    A NodeJS service which runs on the server and provides security and APIs for browsers and integrations. It also includes a custom implementation of filtered replication to allow it to support more concurrent users. See more at the CHT Core API repo on Github.

    Sentinel

    Another NodeJS service running on the server, sentinel performs actions called transitions every time a document in CouchDB is added or modified. Some examples are validations, generating scheduled messages, automatic responses, creating patients, and sending alerts. See more at the CHT Core Sentinel repo on Github.

    CouchDB

    A free and open source NoSQL database used as the primary store for all app data and configuration. This can be multiple instances clustered together for additional scalability. CouchDB is really good at replication which is the process of sending the data to another database and back again, which makes it ideal for replicating data to the phone for offline access. See more at the CouchDB site.

    NGINX

    NGINX provides SSL termination and routes requests to API.

    HAProxy

    HAProxy provides audit logging for any request that makes it to CouchDB so any data access or modification can be validated at a later date.

    CHT Upgrade Service

    The CHT Upgrade Service is used within the CHT to update individual Docker containers when an upgrade is requested. Read more detail in the cht-upgrade-service GitHub repository.

    CHT Sync

    A suite of tools for extracting and normalizing data from the Core Framework’s CouchDB, and rendering the data in analytics dashboards to visualize key data for a CHT deployment. Read more detail on the CHT Sync overview page and the cht-sync GitHub repository.

    CHT Watchdog

    Monitoring and alerting for the CHT Core Framework to ensure CHWs are able to deliver care without interruption caused by server downtime. Read more detail on the CHT Watchdog overview page and the CHT Watchdog GitHub repository.

    Client

    CHT Core Framework

    The CHT Core Framework provides two web applications: the CHT Web App for care teams and program staff, and App Management for program administrators.

    CHT Web Application

    The CHT Web Application is used by Community Health Workers and provides a large variety of features. View the source code in our GitHub repository.

    Technology

    The CHT Web Application is reactive, responsive and a single page application built with Angular and NgRx frameworks. Additionally, it uses the following technology:

    TechnologyUsage
    PouchDBTo implement an Offline-First strategy which means the data is stored on the client and all pages can load immediately regardless of whether the user has a fast connection, slow connection, or no connection at all. The data is stored in PouchDB which replicates changes back and forth in the background with the server CouchDB.
    EnketoTo render configured xforms and help with styling and dynamic elements such as show/hide and validation rules.
    NoolsA rules engine to compute the upcoming tasks and monthly targets of the users.
    Ngx-BootstrapTo integrate Bootstrap components in the Angular application.
    Ngx-translateTo automatically translate the labels from a Angular application. Read more about how to configure translations.
    KarmaA test runner for unit tests
    MochaJSA test framework to run the unit tests
    WebDriverIOTo run the e2e tests
    LessA CSS preprocessor
    Structure

    The CHT Web Application has the following high level structure:

    • /js: Contains the vanilla JavaScript scripts, for example: Enketo widgets, MomentJS locales, etc.
    • /ts: Contains the Angular application source code which uses TypeScript.
      • /actions, /effects, /reducers and /selectors: Contain the implementation for the application’s reactive state which uses NgRx framework.
      • /components, /directives, /pipes, /providers and /services: Contain the reusable elements from Angular framework.
      • /modals: Contains the all application’s modals components.
      • /modules: Contains the application’s modules, each of them has components that are associated to the modules’ routing.
    • /css: Contains the style files. It uses Less as a CSS preprocessor.
    • /fonts: Contains the fonts.
    • /img: Contains the static images.

    App Management

    App Management is an interface for non-technical administrative users to manage users and settings.

    View the application source code in our GitHub repository.

    Technology

    App Management is a single page application built with AngularJS framework and implements Redux to manage a reactive state. Additionally, it uses the following technology:

    TechnologyUsage
    Angular TranslateTo automatically translate the labels from a AngularJS application. Read more about how to manage translations.
    KarmaA test runner for unit tests
    MochaJSA test framework to run the unit tests
    WebDriverIOTo run the e2e tests
    LessA CSS preprocessor
    Structure
    • /css: Contains style files. It uses Less as a CSS preprocessor.
    • /js: Contains the JavaScript code.
      • /actions, /reducers and /selectors: Contain the implementation of Redux.
      • /controllers, /directives, /filters and /services: Contain the reusable elements from AngularJS framework.
      • /modules: Contains the vanilla JavaScript scripts.
    • /template: Contains the HTML templates that are used in the AngularJS components and directives.

    cht-android

    CHT Web Application works in the browser or wrapped in the CHT Android app which allows for project branding, sets the project URL, and hides browser elements like the URL bar.

    Other applications

    cht-gateway

    CHT Gateway is an android app for sending and receiving SMS messages. Each SMS enabled project has one gateway running. It polls an api endpoint to write incoming SMS into the CouchDB and retrieve outgoing SMS to send.

    medic-collect

    Medic Collect is an android app based on Open Data Kit to render xforms on the phone and send reports in to cht-gateway over SMS or directly to api over mobile data.

    cht-conf

    cht-conf is a command line utility for uploading configuration and bulk importing of records.


    CHT Core Framework > + Create project issue

    Architecture of CHT Instances

    The different pieces of a CHT project, how they interact, and what they’re used for

    Overview

    Data Flows

    Server

    CHT Core Framework

    The cht-core product is the primary component of the CHT. The server comes with authentication, role based authorization, data security, and a range of protected data access endpoints. Read more detail in cht-core GitHub repository.

    API

    A NodeJS service which runs on the server and provides security and APIs for browsers and integrations. It also includes a custom implementation of filtered replication to allow it to support more concurrent users. See more at the CHT Core API repo on Github.

    Sentinel

    Another NodeJS service running on the server, sentinel performs actions called transitions every time a document in CouchDB is added or modified. Some examples are validations, generating scheduled messages, automatic responses, creating patients, and sending alerts. See more at the CHT Core Sentinel repo on Github.

    CouchDB

    A free and open source NoSQL database used as the primary store for all app data and configuration. This can be multiple instances clustered together for additional scalability. CouchDB is really good at replication which is the process of sending the data to another database and back again, which makes it ideal for replicating data to the phone for offline access. See more at the CouchDB site.

    NGINX

    NGINX provides SSL termination and routes requests to API.

    HAProxy

    HAProxy provides audit logging for any request that makes it to CouchDB so any data access or modification can be validated at a later date.

    CHT Upgrade Service

    The CHT Upgrade Service is used within the CHT to update individual Docker containers when an upgrade is requested. Read more detail in the cht-upgrade-service GitHub repository.

    CHT Sync

    A suite of tools for extracting and normalizing data from the Core Framework’s CouchDB, and rendering the data in analytics dashboards to visualize key data for a CHT deployment. Read more detail on the CHT Sync overview page and the cht-sync GitHub repository.

    CHT Watchdog

    Monitoring and alerting for the CHT Core Framework to ensure CHWs are able to deliver care without interruption caused by server downtime. Read more detail on the CHT Watchdog overview page and the CHT Watchdog GitHub repository.

    Client

    CHT Core Framework

    The CHT Core Framework provides two web applications: the CHT Web App for care teams and program staff, and App Management for program administrators.

    CHT Web Application

    The CHT Web Application is used by Community Health Workers and provides a large variety of features. View the source code in our GitHub repository.

    Technology

    The CHT Web Application is reactive, responsive and a single page application built with Angular and NgRx frameworks. Additionally, it uses the following technology:

    TechnologyUsage
    PouchDBTo implement an Offline-First strategy which means the data is stored on the client and all pages can load immediately regardless of whether the user has a fast connection, slow connection, or no connection at all. The data is stored in PouchDB which replicates changes back and forth in the background with the server CouchDB.
    EnketoTo render configured xforms and help with styling and dynamic elements such as show/hide and validation rules.
    NoolsA rules engine to compute the upcoming tasks and monthly targets of the users.
    Ngx-BootstrapTo integrate Bootstrap components in the Angular application.
    Ngx-translateTo automatically translate the labels from a Angular application. Read more about how to configure translations.
    KarmaA test runner for unit tests
    MochaJSA test framework to run the unit tests
    WebDriverIOTo run the e2e tests
    LessA CSS preprocessor
    Structure

    The CHT Web Application has the following high level structure:

    • /js: Contains the vanilla JavaScript scripts, for example: Enketo widgets, MomentJS locales, etc.
    • /ts: Contains the Angular application source code which uses TypeScript.
      • /actions, /effects, /reducers and /selectors: Contain the implementation for the application’s reactive state which uses NgRx framework.
      • /components, /directives, /pipes, /providers and /services: Contain the reusable elements from Angular framework.
      • /modals: Contains the all application’s modals components.
      • /modules: Contains the application’s modules, each of them has components that are associated to the modules’ routing.
    • /css: Contains the style files. It uses Less as a CSS preprocessor.
    • /fonts: Contains the fonts.
    • /img: Contains the static images.

    App Management

    App Management is an interface for non-technical administrative users to manage users and settings.

    View the application source code in our GitHub repository.

    Technology

    App Management is a single page application built with AngularJS framework and implements Redux to manage a reactive state. Additionally, it uses the following technology:

    TechnologyUsage
    Angular TranslateTo automatically translate the labels from a AngularJS application. Read more about how to manage translations.
    KarmaA test runner for unit tests
    MochaJSA test framework to run the unit tests
    WebDriverIOTo run the e2e tests
    LessA CSS preprocessor
    Structure
    • /css: Contains style files. It uses Less as a CSS preprocessor.
    • /js: Contains the JavaScript code.
      • /actions, /reducers and /selectors: Contain the implementation of Redux.
      • /controllers, /directives, /filters and /services: Contain the reusable elements from AngularJS framework.
      • /modules: Contains the vanilla JavaScript scripts.
    • /template: Contains the HTML templates that are used in the AngularJS components and directives.

    cht-android

    CHT Web Application works in the browser or wrapped in the CHT Android app which allows for project branding, sets the project URL, and hides browser elements like the URL bar.

    Other applications

    cht-gateway

    CHT Gateway is an android app for sending and receiving SMS messages. Each SMS enabled project has one gateway running. It polls an api endpoint to write incoming SMS into the CouchDB and retrieve outgoing SMS to send.

    medic-collect

    Medic Collect is an android app based on Open Data Kit to render xforms on the phone and send reports in to cht-gateway over SMS or directly to api over mobile data.

    cht-conf

    cht-conf is a command line utility for uploading configuration and bulk importing of records.


    CHT Core Framework > Overview > CHT Watchdog

    An open source monitoring system using Grafana and Prometheus

    CHT Core Framework > Overview > CHT Sync

    Data synchronization tools to enable analytics

    Hosting > Vertical vs Horizontal Scaling

    The power of clustered CouchDB to horizontally scale the CHT

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/overview/cht-conf/index.html b/core/overview/cht-conf/index.html index fdebe0ec91..b757bb6f48 100644 --- a/core/overview/cht-conf/index.html +++ b/core/overview/cht-conf/index.html @@ -1,9 +1,9 @@ -CHT App Configurer | Community Health Toolkit +CHT App Configurer | Community Health Toolkit

    CHT App Configurer

    CHT Conf is a command-line interface tool to manage and configure apps built using the Core Framework of the Community Health Toolkit.

    Installation

    Read more about setting up CHT Conf.

    Currently Supported

    The different items that are supported by CHT Conf include:

    Settings

    • Compile app settings from:
      • tasks
      • rules
      • schedules
      • contact-summary
      • purge
    • App settings can also be defined in a more modular way by having the following files in app_settings folder:
      • base_settings.json
      • forms.json
      • schedules.json
    • Backup app settings from server
    • Upload app settings to server
    • Upload resources to server
    • Upload custom translations to the server
    • Upload privacy policies to server
    • Upload branding to server
    • Upload partners to server

    Forms

    • Fetch from Google Drive and save locally as .xlsx
    • Backup from server
    • Delete all forms from server
    • Delete specific form from server
    • Upload all app or contact forms to server
    • Upload specified app or contact forms to server

    Managing data and images

    • Convert CSV files with contacts and reports to JSON docs
    • Move contacts by downloading and making the changes locally first
    • Upload JSON files as docs on instance
    • Compress PNGs and SVGs in the current directory and its subdirectories

    Editing contacts across the hierarchy.

    To edit existing couchdb documents, create a CSV file that contains the ids of the document you wish to update, and the columns of the document attribute(s) you wish to add/edit. By default, values are parsed as strings. To parse a CSV column as a JSON type.

    ParameterDescriptionRequired
    column(s)Comma delimited list of columns you wish to add/edit. If this is not specified all columns will be added.No
    docDirectoryPathThis action outputs files to local disk at this destinationNo. Default json-docs
    file(s)Comma delimited list of files you wish to process using edit-contacts. By default, contact.csv is searched for in the current directory and processed.No.
    updateOfflineDocsIf passed, this updates the docs already in the docDirectoryPath instead of downloading from the server.No.

    Example

    1. Create a contact.csv file with your columns in the csv folder in your current path. The documentID column is a requirement. The documentID column contains the document IDs to be fetched from couchdb.

      documentIDis_in_emnch:bool
      documentID1false
      documentID2false
      documentID3true
    2. Use the following command to download and edit the documents:

      cht --instance=*instance* edit-contacts -- --column=*is_in_emnch* --docDirectoryPath=*my_folder*
      + Create project issue

    CHT App Configurer

    CHT Conf is a command-line interface tool to manage and configure apps built using the Core Framework of the Community Health Toolkit.

    Installation

    Read more about setting up CHT Conf.

    Currently Supported

    The different items that are supported by CHT Conf include:

    Settings

    • Compile app settings from:
      • tasks
      • rules
      • schedules
      • contact-summary
      • purge
    • App settings can also be defined in a more modular way by having the following files in app_settings folder:
      • base_settings.json
      • forms.json
      • schedules.json
    • Backup app settings from server
    • Upload app settings to server
    • Upload resources to server
    • Upload custom translations to the server
    • Upload privacy policies to server
    • Upload branding to server
    • Upload partners to server

    Forms

    • Fetch from Google Drive and save locally as .xlsx
    • Backup from server
    • Delete all forms from server
    • Delete specific form from server
    • Upload all app or contact forms to server
    • Upload specified app or contact forms to server

    Managing data and images

    • Convert CSV files with contacts and reports to JSON docs
    • Move contacts by downloading and making the changes locally first
    • Upload JSON files as docs on instance
    • Compress PNGs and SVGs in the current directory and its subdirectories

    Editing contacts across the hierarchy.

    To edit existing couchdb documents, create a CSV file that contains the ids of the document you wish to update, and the columns of the document attribute(s) you wish to add/edit. By default, values are parsed as strings. To parse a CSV column as a JSON type.

    ParameterDescriptionRequired
    column(s)Comma delimited list of columns you wish to add/edit. If this is not specified all columns will be added.No
    docDirectoryPathThis action outputs files to local disk at this destinationNo. Default json-docs
    file(s)Comma delimited list of files you wish to process using edit-contacts. By default, contact.csv is searched for in the current directory and processed.No.
    updateOfflineDocsIf passed, this updates the docs already in the docDirectoryPath instead of downloading from the server.No.

    Example

    1. Create a contact.csv file with your columns in the csv folder in your current path. The documentID column is a requirement. The documentID column contains the document IDs to be fetched from couchdb.

      documentIDis_in_emnch:bool
      documentID1false
      documentID2false
      documentID3true
    2. Use the following command to download and edit the documents:

      cht --instance=*instance* edit-contacts -- --column=*is_in_emnch* --docDirectoryPath=*my_folder*
       

      or this one to update already downloaded docs

      cht --instance=*instance* edit-contacts -- --column=*is_in_emnch* --docDirectoryPath=*my_folder* --updateOfflineDocs
       
    3. Then upload the edited documents using the upload-docs command.

      cht --instance=*instance* --upload-docs
       

    Project layout

    This tool expects a project to be structured as follows:

    example-project/
    @@ -364,7 +364,8 @@
     	}
     }
     
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/overview/cht-sync/index.html b/core/overview/cht-sync/index.html index 1f9f01a226..949a85fb39 100644 --- a/core/overview/cht-sync/index.html +++ b/core/overview/cht-sync/index.html @@ -1,9 +1,9 @@ -CHT Sync and CHT Pipeline | Community Health Toolkit +CHT Sync and CHT Pipeline | Community Health Toolkit

    CHT Sync and CHT Pipeline

    Data synchronization tools to enable analytics

    Overview

    CHT Sync is an integrated solution designed to enable data synchronization between CouchDB and PostgreSQL for the purpose of analytics. It combines several technologies to achieve this synchronization and provides an efficient workflow for data processing and visualization. The synchronization occurs in real-time, ensuring that the data displayed on dashboards is up-to-date.

    Read more about setting up CHT Sync.

    CHT Sync uses couch2pg to replicate data from CouchDB to PostgreSQL in a real-time manner. It listens to changes in the CHT database, and updates the analytics database accordingly. + Create project issue

    CHT Sync and CHT Pipeline

    Data synchronization tools to enable analytics

    Overview

    CHT Sync is an integrated solution designed to enable data synchronization between CouchDB and PostgreSQL for the purpose of analytics. It combines several technologies to achieve this synchronization and provides an efficient workflow for data processing and visualization. The synchronization occurs in real-time, ensuring that the data displayed on dashboards is up-to-date.

    Read more about setting up CHT Sync.

    CHT Sync uses couch2pg to replicate data from CouchDB to PostgreSQL in a real-time manner. It listens to changes in the CHT database, and updates the analytics database accordingly. It is not designed to be accessed by users, and it does not have a user interface. It is designed to be run on the same server as the CHT, but it can be run on a separate server if necessary.

    As CHT Sync puts all new data into a PostgreSQL database into a single table that has a jsonb column, this is not very useful for analytics. CHT Pipeline is a set of SQL queries that transform the data in the jsonb column into a more useful format. It uses DBT to define the models that are translated into PostgreSQL tables or views, which makes it easier to query the data in the analytics platform of choice.

    couch2pg

    couch2pg streams data from CouchDB and forwards it to PostgreSQL, ensuring real-time updates.

    PostgreSQL

    A free and open source SQL database used for analytics queries. See more at the PostgreSQL site.

    DBT

    Once the data is synchronized and stored in PostgreSQL, it undergoes transformation using predefined DBT models from the cht-pipeline. DBT is used to ingest raw JSON data from the PosgtreSQL database (jsonb column) and normalize it into a relational schema to make it easier to query. A daemon runs CHT Pipeline, and it updates the database whenever the data in the jsonb column changes.

    Data Visualization

    We recommend Apache Superset as the Data Visualization Tool. Superset is a free, open-source platform for data exploration and data visualization.

    CHT Core Framework & CouchDB

    For more information on these technologies, see CHT Core overview.


    CHT Core Framework > Overview > CHT Core

    The different pieces of a CHT project, how they interact, and what they’re used for

    CHT Core Framework > @@ -309,7 +309,8 @@ Quick Guides > Data > Data Synchronization and Analytics

    Using CHT Sync and CHT Pipeline for data synchronization and analytics

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/overview/data-flows-for-analytics/index.html b/core/overview/data-flows-for-analytics/index.html index 112ad42c7c..09b6d96bce 100644 --- a/core/overview/data-flows-for-analytics/index.html +++ b/core/overview/data-flows-for-analytics/index.html @@ -1,9 +1,9 @@ -Data Flows for Analytics | Community Health Toolkit +Data Flows for Analytics | Community Health Toolkit

    Data Flows for Analytics

    An overview of data flows in the CHT for analytics, impact monitoring, and data science

    In this section, we focus on how data flows through the various components of the Community Health Toolkit. The CHT is built to support the delivery of quality community health care at the last mile. The CHT is designed to work in areas with low connectivity, which means it is an Offline-First toolkit for care provision. The architectural and technology choices in the stack are mostly guided by this principle, which will be evident in the discussion of the data management pipeline.

    Overview

    Data Flows

    At a high level:

    • Data are collected from the device of a health worker;
    • Data are pushed to an online instance from where data are available to other health workers, supervisors, and decision makers;
    • Data are transferred to a relational database (PostgreSQL) using couch2pg and made available for impact monitoring, data science projects, and visualizations;
    • Access to PostreSQL is given to relevant parties at this level, for example members of the Research & Learning team for impact monitoring and data science;
    • Visualization platforms, such as Klipfolio, are then connected to PostgreSQL from where program managers and other partner representatives can access visualizations of their data for decision-making

    Details of the data flow

    The layout detailed here is specific to how Medic supports its CHT partners at the moment. It is replicable and can be deployed as is or tweaked independent of Medic either by modifying or replacing pieces of it with other options.

    Current infrastructure

    We look at this in three general phases.

    1. Data Collection

    Data is collected in the community at the point of care, i.e. the community health worker interacting with the toolkit. These tools and their corresponding data stores are::-

    • Mobile app -> PouchDB
    • Webapp -> PouchDB / CouchDB
    • Text forms / sms -> SMS gateway / SMS aggregator -> CouchDB

    The mobile app and webapp, when deployed for offline first use, use a local database namely PouchDB. Similar to CouchDB, it is a document-oriented database. The data collected in PouchDB is synced to an online CouchDB upon the user connecting to the internet. Local storage is not applicable to SMS; instead, an SMS gateway or an SMS aggregator (for example Africa’s Talking) is used to help get the data to an online CouchDB instance.

    Ultimately all the data ends up in a CouchDB instance deployed in the cloud whether through data synchronization with PouchDB local to the health workers devices, use of SMS aggregators or gateway. It should be mentioned that you can have a deployment supported by all of webapp, mobile app and SMS and have all the data end up in the same CouchDB instance.

    2. Data Transformation

    We use couch2pg or cht-sync to move data from CouchDB to a relational database, PostgreSQL in this case. The choice of PostgreSQL for analytics dashboard data sources is to allow use of the more familiar SQL querying. It is an open source tool that can be easily deployed. When deployed the service uses CouchDB’s changes feed which allows capturing of everything happening in CouchDB in incremental updates. It is run and monitored by the operating system where it is configured to fetch data at a configurable interval.

    Data copied over to PostgreSQL is first stored as raw json (document) making use of PostgreSQL’s jsonb data type to create an exact replica of a CouchDB database. From this, default views are created at deployment of the service and refreshed during every subsequent run. Additional custom materialized views created later are also refreshed at this time.

    Custom materialized views and functions are added specific to a deployment’s needs. Generally the following naming convention is recommended:

    • formview as a view of raw forms
    • useview as a view of form data supporting a use case as defined by design
    • contactview as a view of people and places
    • Database functions are used as a way to join as much relevant data as possible for easier querying in analytics or dashboard visualizations.

    Data in the views and functions mentioned in this section is as accurate as the accuracy of the SQL queries. Best practice is to begin the process of defining these objects at design in order to align analytics and dashboards requirements with workflows being deployed.

    3. Data Use

    The data in PostgreSQL is mostly either used by direct querying or via dashboard visualizations for impact monitoring and data driven-decision making. Database visualizations are built scoped to the requirements of supporting a successful deployment. The work of our Research & Learning team, specifically data science, is supported at the PostgreSQL level through updated contactviews, formviews, useviews and functions with access to these provided to relevant parties as and when needed. Our use of data follows our Privacy & Data Protection policy and is in accordance to agreements with our CHT partners.

    As mentioned previously, formviews are built to present data in a structure similar to the data collection tool (form) used. Useviews are tailored to align with a use case, mostly using the formviews as the data sources. These are fundamentally guided by design of the workflows and should be interpreted in the context of the design materials including a document explaining the definitions of variables used.

    The objects present here are not limited to views and functions. Additional tables can be added, for example providing mappings or supporting operations external to the functions available in the toolkit. In short, there is no limitation to the utility that can be added this level to support analytics and dashboards. That said, measures are taken to ensure controlled access, reliability and timely access of the data by the various parties. Some of these measures are:

    • Roles and users allocation and deallocation done by specific roles within partner technical teams with support from Medic as needed;
    • Access control management is left to the partner technical teams where possible;
    • Dashboard data source refresh intervals set to align with project needs;
    • Update of the data sources monitored to ensure updating works as expected;
    • Review of the dashboards as part of the design process;
    • Qualitative design activities to interrogate trends observed in the dashboards and iterate on them if need be;

    Beyond Our Current Pipeline

    The cht-core is mostly data collection tools and is the first component of the data management pipeline. It is the core part of a deployment but the rest of the tools can be easily replaced with other preferred options. It also helps that couch2pg is an open source tool which provides the opportunity for collaboration to extend its functionality to support other implementations. Klipfolio, the tool that we currently use for visualizations, is a proprietary tool but there are many open source options, such as Apache Superset that are worth exploring and building into future iterations of our impact monitoring and analytics support for the CHT.

    Backup

    The machines running each of CouchDB and PostgreSQL instances are backed up daily.


    CHT Applications > + Create project issue

    Data Flows for Analytics

    An overview of data flows in the CHT for analytics, impact monitoring, and data science

    In this section, we focus on how data flows through the various components of the Community Health Toolkit. The CHT is built to support the delivery of quality community health care at the last mile. The CHT is designed to work in areas with low connectivity, which means it is an Offline-First toolkit for care provision. The architectural and technology choices in the stack are mostly guided by this principle, which will be evident in the discussion of the data management pipeline.

    Overview

    Data Flows

    At a high level:

    • Data are collected from the device of a health worker;
    • Data are pushed to an online instance from where data are available to other health workers, supervisors, and decision makers;
    • Data are transferred to a relational database (PostgreSQL) using couch2pg and made available for impact monitoring, data science projects, and visualizations;
    • Access to PostreSQL is given to relevant parties at this level, for example members of the Research & Learning team for impact monitoring and data science;
    • Visualization platforms, such as Klipfolio, are then connected to PostgreSQL from where program managers and other partner representatives can access visualizations of their data for decision-making

    Details of the data flow

    The layout detailed here is specific to how Medic supports its CHT partners at the moment. It is replicable and can be deployed as is or tweaked independent of Medic either by modifying or replacing pieces of it with other options.

    Current infrastructure

    We look at this in three general phases.

    1. Data Collection

    Data is collected in the community at the point of care, i.e. the community health worker interacting with the toolkit. These tools and their corresponding data stores are::-

    • Mobile app -> PouchDB
    • Webapp -> PouchDB / CouchDB
    • Text forms / sms -> SMS gateway / SMS aggregator -> CouchDB

    The mobile app and webapp, when deployed for offline first use, use a local database namely PouchDB. Similar to CouchDB, it is a document-oriented database. The data collected in PouchDB is synced to an online CouchDB upon the user connecting to the internet. Local storage is not applicable to SMS; instead, an SMS gateway or an SMS aggregator (for example Africa’s Talking) is used to help get the data to an online CouchDB instance.

    Ultimately all the data ends up in a CouchDB instance deployed in the cloud whether through data synchronization with PouchDB local to the health workers devices, use of SMS aggregators or gateway. It should be mentioned that you can have a deployment supported by all of webapp, mobile app and SMS and have all the data end up in the same CouchDB instance.

    2. Data Transformation

    We use couch2pg or cht-sync to move data from CouchDB to a relational database, PostgreSQL in this case. The choice of PostgreSQL for analytics dashboard data sources is to allow use of the more familiar SQL querying. It is an open source tool that can be easily deployed. When deployed the service uses CouchDB’s changes feed which allows capturing of everything happening in CouchDB in incremental updates. It is run and monitored by the operating system where it is configured to fetch data at a configurable interval.

    Data copied over to PostgreSQL is first stored as raw json (document) making use of PostgreSQL’s jsonb data type to create an exact replica of a CouchDB database. From this, default views are created at deployment of the service and refreshed during every subsequent run. Additional custom materialized views created later are also refreshed at this time.

    Custom materialized views and functions are added specific to a deployment’s needs. Generally the following naming convention is recommended:

    • formview as a view of raw forms
    • useview as a view of form data supporting a use case as defined by design
    • contactview as a view of people and places
    • Database functions are used as a way to join as much relevant data as possible for easier querying in analytics or dashboard visualizations.

    Data in the views and functions mentioned in this section is as accurate as the accuracy of the SQL queries. Best practice is to begin the process of defining these objects at design in order to align analytics and dashboards requirements with workflows being deployed.

    3. Data Use

    The data in PostgreSQL is mostly either used by direct querying or via dashboard visualizations for impact monitoring and data driven-decision making. Database visualizations are built scoped to the requirements of supporting a successful deployment. The work of our Research & Learning team, specifically data science, is supported at the PostgreSQL level through updated contactviews, formviews, useviews and functions with access to these provided to relevant parties as and when needed. Our use of data follows our Privacy & Data Protection policy and is in accordance to agreements with our CHT partners.

    As mentioned previously, formviews are built to present data in a structure similar to the data collection tool (form) used. Useviews are tailored to align with a use case, mostly using the formviews as the data sources. These are fundamentally guided by design of the workflows and should be interpreted in the context of the design materials including a document explaining the definitions of variables used.

    The objects present here are not limited to views and functions. Additional tables can be added, for example providing mappings or supporting operations external to the functions available in the toolkit. In short, there is no limitation to the utility that can be added this level to support analytics and dashboards. That said, measures are taken to ensure controlled access, reliability and timely access of the data by the various parties. Some of these measures are:

    • Roles and users allocation and deallocation done by specific roles within partner technical teams with support from Medic as needed;
    • Access control management is left to the partner technical teams where possible;
    • Dashboard data source refresh intervals set to align with project needs;
    • Update of the data sources monitored to ensure updating works as expected;
    • Review of the dashboards as part of the design process;
    • Qualitative design activities to interrogate trends observed in the dashboards and iterate on them if need be;

    Beyond Our Current Pipeline

    The cht-core is mostly data collection tools and is the first component of the data management pipeline. It is the core part of a deployment but the rest of the tools can be easily replaced with other preferred options. It also helps that couch2pg is an open source tool which provides the opportunity for collaboration to extend its functionality to support other implementations. Klipfolio, the tool that we currently use for visualizations, is a proprietary tool but there are many open source options, such as Apache Superset that are worth exploring and building into future iterations of our impact monitoring and analytics support for the CHT.

    Backup

    The machines running each of CouchDB and PostgreSQL instances are backed up daily.


    CHT Applications > Quick Guides > Data

    Creating and managing data in CHT applications

    CHT Applications > Quick Guides > Database

    Managing databases used by CHT applications

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/overview/db-schema/index.html b/core/overview/db-schema/index.html index c81a6517f7..d6f0c39ff1 100644 --- a/core/overview/db-schema/index.html +++ b/core/overview/db-schema/index.html @@ -1,9 +1,9 @@ -Database schema conventions | Community Health Toolkit +Database schema conventions | Community Health Toolkit

    Database schema conventions

    Schema for database objects

    CouchDB (and PouchDB in the browser) is a JSON-based NoSQL datastore that we use to store our data. While unlike SQL databases there is no enforced schema, code still follows conventions, and this document aims to describe the schema as defined by how our code operates.

    In this document “record” means a JSON object that resides in CouchDB or PouchDB.

    General record data structure

    PropertyDescriptionRequired by
    _idCouchDB’s unique identifier of the recordall records
    _revCouchDB’s revision markerall records
    typeThe general type of the document, see belowall user-created* documents
    reported_dateNumerical timestamp of when the document is first createdall user-created documents
    • User-created documents here generally means contacts and reports, but may extend further.

    Contacts (Persons and Places)

    Contacts are either places (e.g. clinic), groupings (e.g. family) or people (e.g. a patient or CHW).

    The type property of contact records depends on the version of Medic you are running:

    • If you are running 3.7 or later you get to configure your contact hierarchy, and the type of contacts is contact, and the configured type is in the contact_type property.
    • In earlier versions the type depended on hierarchical location of the contact. There are 3 hard coded place types: district_hospital, health_centre and clinic and one people type person. These place names are often meaningless (hence the configurable contact hierarchy in later versions) to the configured project, and are textually (ie in the UI not in data structures) renamed to mean other things. For example, as clinic is the lowest level it is often used to represent a family.

    Places

    Represent either an actual physical location such as a clinic, or a grouping such as a family or region.

    Unless a place is at the top of the hierarchy it has a parent place.

    Each location has a primary contact, which is a person contact stored in the contact property.

    People

    People are both patients in the system and users of the system, such as CHWs or Nurses. Users have additional records marking them as users of the system (see User below).

    People always have a parent place.

    Parent hierarchy representation

    Contacts store their parent hierarchy as a minified hierarchical structure, which records the _id of each parent up until the top of the hierarchy:

    Database schema conventions

    Schema for database objects

    CouchDB (and PouchDB in the browser) is a JSON-based NoSQL datastore that we use to store our data. While unlike SQL databases there is no enforced schema, code still follows conventions, and this document aims to describe the schema as defined by how our code operates.

    In this document “record” means a JSON object that resides in CouchDB or PouchDB.

    General record data structure

    PropertyDescriptionRequired by
    _idCouchDB’s unique identifier of the recordall records
    _revCouchDB’s revision markerall records
    typeThe general type of the document, see belowall user-created* documents
    reported_dateNumerical timestamp of when the document is first createdall user-created documents
    • User-created documents here generally means contacts and reports, but may extend further.

    Contacts (Persons and Places)

    Contacts are either places (e.g. clinic), groupings (e.g. family) or people (e.g. a patient or CHW).

    The type property of contact records depends on the version of Medic you are running:

    • If you are running 3.7 or later you get to configure your contact hierarchy, and the type of contacts is contact, and the configured type is in the contact_type property.
    • In earlier versions the type depended on hierarchical location of the contact. There are 3 hard coded place types: district_hospital, health_centre and clinic and one people type person. These place names are often meaningless (hence the configurable contact hierarchy in later versions) to the configured project, and are textually (ie in the UI not in data structures) renamed to mean other things. For example, as clinic is the lowest level it is often used to represent a family.

    Places

    Represent either an actual physical location such as a clinic, or a grouping such as a family or region.

    Unless a place is at the top of the hierarchy it has a parent place.

    Each location has a primary contact, which is a person contact stored in the contact property.

    People

    People are both patients in the system and users of the system, such as CHWs or Nurses. Users have additional records marking them as users of the system (see User below).

    People always have a parent place.

    Parent hierarchy representation

    Contacts store their parent hierarchy as a minified hierarchical structure, which records the _id of each parent up until the top of the hierarchy:

    {
       type: 'person',
       name: 'A patient',
       parent: {
    @@ -443,7 +443,8 @@
     targets.js

    Targets: Definition of target widgets calculated and seen in the app

    CHT Applications > Reference > tasks.js

    Tasks: Definition of tasks shown to app users

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/overview/index.html b/core/overview/index.html index 11f2428345..fefe17bb38 100644 --- a/core/overview/index.html +++ b/core/overview/index.html @@ -1,9 +1,9 @@ -Overview of CHT Components | Community Health Toolkit +Overview of CHT Components | Community Health Toolkit

    Overview of CHT Components

    Technical overview and reference of CHT components

    This section provides an overview and reference of the Core Framework used to develop digital health apps in the Community Health Toolkit (CHT).


    Architecture of CHT Instances

    The different pieces of a CHT project, how they interact, and what they’re used for

    CHT Watchdog

    An open source monitoring system using Grafana and Prometheus

    CHT App Configurer

    CHT Conf is a command-line interface tool to manage and configure apps built using the Core Framework of the Community Health Toolkit.

    CHT Sync and CHT Pipeline

    Data synchronization tools to enable analytics

    Database schema conventions

    Schema for database objects

    Offline-First in the CHT

    An overview of what Offline-First means, why the CHT uses it, and how to contribute code that uses it.

    Installation as a Progressive Web App

    What it means that the CHT Core web application is a Progressive Web App.

    Data Flows for Analytics

    An overview of data flows in the CHT for analytics, impact monitoring, and data science

    Sentinel Transitions

    Overview of Transitions, JavaScript code that runs server-side when database documents change

    How To Manage Translations

    Process for managing translations in CHT Core

    Roadmap

    Improvements that are being worked on, and those that are being considered next

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/overview/index.xml b/core/overview/index.xml index a8e91e717b..5ff45b557f 100644 --- a/core/overview/index.xml +++ b/core/overview/index.xml @@ -1,934 +1,17 @@ -Community Health Toolkit – Overview of CHT Componentshttps://docs.communityhealthtoolkit.org/core/overview/Recent content in Overview of CHT Components on Community Health ToolkitHugo -- gohugo.ioenCore: Architecture of CHT Instanceshttps://docs.communityhealthtoolkit.org/core/overview/architecture/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/architecture/ -<h2 id="overview">Overview</h2> -<!-- make updates to this diagram on the google slides: --> -<!-- https://docs.google.com/presentation/d/1j4jPsi-gHbiaLBfgYOyru1g_YV98PkBrx2zs7bwhoEQ/ --> -<p><a href="architecture.png"><img src="architecture.png" alt="Data Flows"></a></p> -<h2 id="server">Server</h2> -<h3 id="cht-core-framework">CHT Core Framework</h3> -<p>The cht-core product is the primary component of the CHT. The server comes with authentication, role based authorization, data security, and a range of protected data access endpoints. Read more detail in <a href="https://github.com/medic/cht-core">cht-core GitHub repository</a>.</p> -<h4 id="api">API</h4> -<p>A NodeJS service which runs on the server and provides security and APIs for browsers and integrations. It also includes a custom implementation of filtered replication to allow it to support more concurrent users. See more at the <a href="https://github.com/medic/cht-core/tree/master/api">CHT Core API repo</a> on Github.</p> -<h4 id="sentinel">Sentinel</h4> -<p>Another NodeJS service running on the server, sentinel performs actions called transitions every time a document in CouchDB is added or modified. Some examples are validations, generating scheduled messages, automatic responses, creating patients, and sending alerts. See more at the <a href="https://github.com/medic/cht-core/tree/master/sentinel">CHT Core Sentinel repo</a> on Github.</p> -<h4 id="couchdb">CouchDB</h4> -<p>A free and open source NoSQL database used as the primary store for all app data and configuration. This can be multiple instances clustered together for additional scalability. CouchDB is really good at replication which is the process of sending the data to another database and back again, which makes it ideal for replicating data to the phone for offline access. See more at the <a href="http://couchdb.apache.org">CouchDB</a> site.</p> -<h4 id="nginx">NGINX</h4> -<p><a href="https://www.nginx.com/">NGINX</a> provides SSL termination and routes requests to API.</p> -<h4 id="haproxy">HAProxy</h4> -<p><a href="https://www.haproxy.com/">HAProxy</a> provides audit logging for any request that makes it to CouchDB so any data access or modification can be validated at a later date.</p> -<h3 id="cht-upgrade-service">CHT Upgrade Service</h3> -<p>The CHT Upgrade Service is used within the CHT to update individual Docker containers when an upgrade is requested. Read more detail in the <a href="https://github.com/medic/cht-upgrade-service/">cht-upgrade-service GitHub repository</a>.</p> -<h3 id="cht-sync">CHT Sync</h3> -<p>A suite of tools for extracting and normalizing data from the Core Framework&rsquo;s CouchDB, and rendering the data in analytics dashboards to visualize key data for a CHT deployment. Read more detail on the <a href="https://docs.communityhealthtoolkit.org/core/overview/cht-sync/">CHT Sync overview page</a> and the <a href="https://github.com/medic/cht-sync">cht-sync GitHub repository</a>.</p> -<h3 id="cht-watchdog">CHT Watchdog</h3> -<p>Monitoring and alerting for the CHT Core Framework to ensure CHWs are able to deliver care without interruption caused by server downtime. Read more detail on the <a href="https://docs.communityhealthtoolkit.org/core/overview/watchdog/">CHT Watchdog overview page</a> and the <a href="https://github.com/medic/cht-watchdog">CHT Watchdog GitHub repository</a>.</p> -<h2 id="client">Client</h2> -<h3 id="cht-core-framework-1">CHT Core Framework</h3> -<p>The CHT Core Framework provides two web applications: the <a href="#cht-web-application">CHT Web App</a> for care teams and program staff, and <a href="#app-management">App Management</a> for program administrators.</p> -<h4 id="cht-web-application">CHT Web Application</h4> -<p>The CHT Web Application is used by Community Health Workers and provides a large variety of <a href="https://docs.communityhealthtoolkit.org/apps/features/">features</a>. View the source code in <a href="https://github.com/medic/cht-core/tree/master/webapp">our GitHub repository</a>.</p> -<h5 id="technology">Technology</h5> -<p>The CHT Web Application is <a href="https://angular.io/guide/rx-library">reactive</a>, responsive and a single page application built with <a href="https://angular.io/">Angular</a> and <a href="https://ngrx.io">NgRx</a> frameworks. Additionally, it uses the following technology:</p> -<table> -<thead> -<tr> -<th>Technology</th> -<th>Usage</th> -</tr> -</thead> -<tbody> -<tr> -<td><a href="https://pouchdb.com">PouchDB</a></td> -<td>To implement an <a href="https://docs.communityhealthtoolkit.org/core/overview/offline-first/">Offline-First</a> strategy which means the data is stored on the client and all pages can load immediately regardless of whether the user has a fast connection, slow connection, or no connection at all. The data is stored in PouchDB which replicates changes back and forth in the background with the server CouchDB.</td> -</tr> -<tr> -<td><a href="https://enketo.org">Enketo</a></td> -<td>To render configured xforms and help with styling and dynamic elements such as show/hide and validation rules.</td> -</tr> -<tr> -<td><a href="https://github.com/C2FO/nools">Nools</a></td> -<td>A rules engine to compute the upcoming tasks and monthly targets of the users.</td> -</tr> -<tr> -<td><a href="https://github.com/valor-software/ngx-bootstrap">Ngx-Bootstrap</a></td> -<td>To integrate Bootstrap components in the Angular application.</td> -</tr> -<tr> -<td><a href="https://github.com/ngx-translate/core">Ngx-translate</a></td> -<td>To automatically translate the labels from a Angular application. Read more about <a href="https://docs.communityhealthtoolkit.org/apps/reference/translations/">how to configure translations</a>.</td> -</tr> -<tr> -<td><a href="https://github.com/karma-runner/karma">Karma</a></td> -<td>A test runner for <a href="https://github.com/medic/cht-core/tree/master/webapp/tests">unit tests</a></td> -</tr> -<tr> -<td><a href="https://mochajs.org/">MochaJS</a></td> -<td>A test framework to run the <a href="https://github.com/medic/cht-core/tree/master/webapp/tests">unit tests</a></td> -</tr> -<tr> -<td><a href="https://webdriver.io/">WebDriverIO</a></td> -<td>To run the <a href="https://github.com/medic/cht-core/tree/master/tests/e2e">e2e tests</a></td> -</tr> -<tr> -<td><a href="http://lesscss.org/">Less</a></td> -<td>A CSS preprocessor</td> -</tr> -</tbody> -</table> -<h5 id="structure">Structure</h5> -<p>The CHT Web Application has the following high level structure:</p> -<ul> -<li><strong>/js</strong>: Contains the vanilla JavaScript scripts, for example: Enketo widgets, MomentJS locales, etc.</li> -<li><strong>/ts</strong>: Contains the Angular application source code which uses TypeScript. -<ul> -<li><strong>/actions</strong>, <strong>/effects</strong>, <strong>/reducers</strong> and <strong>/selectors</strong>: Contain the implementation for the application’s reactive state which uses <a href="https://ngrx.io">NgRx</a> framework.</li> -<li><strong>/components</strong>, <strong>/directives</strong>, <strong>/pipes</strong>, <strong>/providers</strong> and <strong>/services</strong>: Contain the reusable elements from <a href="https://angular.io/">Angular</a> framework.</li> -<li><strong>/modals</strong>: Contains the all application’s modals components.</li> -<li><strong>/modules</strong>: Contains the application’s modules, each of them has components that are associated to the modules’ routing.</li> -</ul> -</li> -<li><strong>/css</strong>: Contains the style files. It uses <a href="http://lesscss.org/">Less</a> as a CSS preprocessor.</li> -<li><strong>/fonts</strong>: Contains the fonts.</li> -<li><strong>/img</strong>: Contains the static images.</li> -</ul> -<h4 id="app-management">App Management</h4> -<p><a href="https://docs.communityhealthtoolkit.org/apps/features/admin/">App Management</a> is an interface for non-technical administrative users to manage users and settings.</p> -<p>View the application source code in <a href="https://github.com/medic/cht-core/tree/master/admin">our GitHub repository</a>.</p> -<h5 id="technology-1">Technology</h5> -<p>App Management is a single page application built with <a href="https://angularjs.org">AngularJS</a> framework and implements <a href="https://github.com/reduxjs/redux">Redux</a> to manage a reactive state. Additionally, it uses the following technology:</p> -<table> -<thead> -<tr> -<th>Technology</th> -<th>Usage</th> -</tr> -</thead> -<tbody> -<tr> -<td><a href="https://github.com/angular-translate/angular-translate">Angular Translate</a></td> -<td>To automatically translate the labels from a AngularJS application. Read more about <a href="https://docs.communityhealthtoolkit.org/apps/reference/translations/">how to manage translations</a>.</td> -</tr> -<tr> -<td><a href="https://github.com/karma-runner/karma">Karma</a></td> -<td>A test runner for <a href="https://github.com/medic/cht-core/tree/master/admin/tests">unit tests</a></td> -</tr> -<tr> -<td><a href="https://mochajs.org/">MochaJS</a></td> -<td>A test framework to run the <a href="https://github.com/medic/cht-core/tree/master/admin/tests">unit tests</a></td> -</tr> -<tr> -<td><a href="https://webdriver.io/">WebDriverIO</a></td> -<td>To run the <a href="https://github.com/medic/cht-core/tree/master/tests/e2e">e2e tests</a></td> -</tr> -<tr> -<td><a href="http://lesscss.org/">Less</a></td> -<td>A CSS preprocessor</td> -</tr> -</tbody> -</table> -<h5 id="structure-1">Structure</h5> -<ul> -<li><strong>/css</strong>: Contains style files. It uses <a href="http://lesscss.org/">Less</a> as a CSS preprocessor.</li> -<li><strong>/js</strong>: Contains the JavaScript code. -<ul> -<li><strong>/actions</strong>, <strong>/reducers</strong> and <strong>/selectors</strong>: Contain the implementation of <a href="https://github.com/reduxjs/redux">Redux</a>.</li> -<li><strong>/controllers</strong>, <strong>/directives</strong>, <strong>/filters</strong> and <strong>/services</strong>: Contain the reusable elements from <a href="https://angularjs.org">AngularJS</a> framework.</li> -<li><strong>/modules</strong>: Contains the vanilla JavaScript scripts.</li> -</ul> -</li> -<li><strong>/template</strong>: Contains the HTML templates that are used in the AngularJS components and directives.</li> -</ul> -<h3 id="cht-android">cht-android</h3> -<p>CHT Web Application works in the browser or wrapped in the <a href="https://github.com/medic/cht-android">CHT Android</a> app which allows for project branding, sets the project URL, and hides browser elements like the URL bar.</p> -<h2 id="other-applications">Other applications</h2> -<h3 id="cht-gateway">cht-gateway</h3> -<p><a href="https://github.com/medic/cht-gateway">CHT Gateway</a> is an android app for sending and receiving SMS messages. Each SMS enabled project has one gateway running. It polls an api endpoint to write incoming SMS into the CouchDB and retrieve outgoing SMS to send.</p> -<h3 id="medic-collect">medic-collect</h3> -<p><a href="https://github.com/medic/medic-collect">Medic Collect</a> is an android app based on <a href="https://opendatakit.org">Open Data Kit</a> to render xforms on the phone and send reports in to cht-gateway over SMS or directly to api over mobile data.</p> -<h3 id="cht-conf">cht-conf</h3> -<p><a href="https://github.com/medic/cht-conf">cht-conf</a> is a command line utility for uploading configuration and bulk importing of records.</p>Core: CHT Watchdoghttps://docs.communityhealthtoolkit.org/core/overview/watchdog/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/watchdog/ -<h2 id="overview">Overview</h2> -<p>CHT Watchdog is deployed on a separate server so that you can watch for, and alert on, any critical issues with the CHT Core. Read more about <a href="https://docs.communityhealthtoolkit.org/hosting/monitoring/setup/">setting up CHT Watchdog</a>.</p> -<!-- make updates to this diagram on the google slides: --> -<!-- https://docs.google.com/presentation/d/1j4jPsi-gHbiaLBfgYOyru1g_YV98PkBrx2zs7bwhoEQ/ --> -<p><a href="CHT.Watchdog.Architecture.png"><img src="CHT.Watchdog.Architecture.png" alt="Data Flows"></a></p> -<h3 id="grafana">Grafana</h3> -<p><a href="https://grafana.com/">Grafana</a> is a dashboard visualization and alerting software. It is open source and an industry standard for this task. There is an <a href="https://grafana.com/grafana/dashboards/">free repository of pre-existing dashboards</a> which greatly reduce the time to create new dashboards and alerts. It can send alerts via email, Slack, SMS and many more.</p> -<h3 id="prometheus">Prometheus</h3> -<p><a href="https://prometheus.io/docs/prometheus/latest/">Prometheus</a> is an open source Time Series Database (TSDB) that was developed explicitly to do detailed longitudinal monitoring. It also aggregates metrics and can automatically cull older data to save on CPU and disk space.</p> -<h3 id="json-exporter">JSON Exporter</h3> -<p><a href="https://github.com/prometheus-community/json_exporter">JSON Exporter</a> is a wrapper utility to convert a JSON API to be compatible with Prometheus scrape config. This is used to convert the CHT <a href="https://docs.communityhealthtoolkit.org/apps/reference/api/#get-apiv2monitoring">Monitoring API&rsquo;s JSON</a>.</p> -<h3 id="postgres-exporter">Postgres Exporter</h3> -<p><a href="https://github.com/prometheus-community/postgres_exporter">Postgres Exporter</a> allows Prometheus to scrape a Postgres database and at a predefined interval. The queries can be configured to ingest any relevant data needed.</p> -<h3 id="cht-core-framework--rdbms">CHT Core Framework &amp; RDBMS</h3> -<p>For more information on these technologies, see <a href="https://docs.communityhealthtoolkit.org/core/overview/architecture/">CHT Core overview</a>.</p>Core: CHT App Configurerhttps://docs.communityhealthtoolkit.org/core/overview/cht-conf/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/cht-conf/ -<h2 id="installation">Installation</h2> -<p>Read more about setting up <a href="https://docs.communityhealthtoolkit.org/contribute/code/cht-conf/">CHT Conf</a>.</p> -<h2 id="currently-supported">Currently Supported</h2> -<p>The different items that are supported by CHT Conf include:</p> -<h3 id="settings">Settings</h3> -<ul> -<li>Compile app settings from: -<ul> -<li>tasks</li> -<li>rules</li> -<li>schedules</li> -<li>contact-summary</li> -<li>purge</li> -</ul> -</li> -<li>App settings can also be defined in a more modular way by having the following files in app_settings folder: -<ul> -<li>base_settings.json</li> -<li>forms.json</li> -<li>schedules.json</li> -</ul> -</li> -<li>Backup app settings from server</li> -<li>Upload app settings to server</li> -<li>Upload resources to server</li> -<li>Upload custom translations to the server</li> -<li>Upload privacy policies to server</li> -<li>Upload branding to server</li> -<li>Upload partners to server</li> -</ul> -<h3 id="forms">Forms</h3> -<ul> -<li>Fetch from Google Drive and save locally as <code>.xlsx</code></li> -<li>Backup from server</li> -<li>Delete all forms from server</li> -<li>Delete specific form from server</li> -<li>Upload all app or contact forms to server</li> -<li>Upload specified app or contact forms to server</li> -</ul> -<h3 id="managing-data-and-images">Managing data and images</h3> -<ul> -<li>Convert CSV files with contacts and reports to JSON docs</li> -<li>Move contacts by downloading and making the changes locally first</li> -<li>Upload JSON files as docs on instance</li> -<li>Compress PNGs and SVGs in the current directory and its subdirectories</li> -</ul> -<h3 id="editing-contacts-across-the-hierarchy">Editing contacts across the hierarchy.</h3> -<p>To edit existing couchdb documents, create a CSV file that contains the ids of the document you wish to update, and the columns of the document attribute(s) you wish to add/edit. By default, values are parsed as strings. To parse a CSV column as a JSON type.</p> -<table> -<thead> -<tr> -<th>Parameter</th> -<th>Description</th> -<th>Required</th> -</tr> -</thead> -<tbody> -<tr> -<td>column(s)</td> -<td>Comma delimited list of columns you wish to add/edit. If this is not specified all columns will be added.</td> -<td>No</td> -</tr> -<tr> -<td>docDirectoryPath</td> -<td>This action outputs files to local disk at this destination</td> -<td>No. Default <code>json-docs</code></td> -</tr> -<tr> -<td>file(s)</td> -<td>Comma delimited list of files you wish to process using edit-contacts. By default, contact.csv is searched for in the current directory and processed.</td> -<td>No.</td> -</tr> -<tr> -<td>updateOfflineDocs</td> -<td>If passed, this updates the docs already in the docDirectoryPath instead of downloading from the server.</td> -<td>No.</td> -</tr> -</tbody> -</table> -<h4 id="example">Example</h4> -<ol> -<li> -<p>Create a contact.csv file with your columns in the csv folder in your current path. The documentID column is a requirement. The documentID column contains the document IDs to be fetched from couchdb.</p> -<table> -<thead> -<tr> -<th>documentID</th> -<th>is_in_emnch:bool</th> -</tr> -</thead> -<tbody> -<tr> -<td>documentID1</td> -<td>false</td> -</tr> -<tr> -<td>documentID2</td> -<td>false</td> -</tr> -<tr> -<td>documentID3</td> -<td>true</td> -</tr> -</tbody> -</table> -</li> -<li> -<p>Use the following command to download and edit the documents:</p> -<pre tabindex="0"><code>cht --instance=*instance* edit-contacts -- --column=*is_in_emnch* --docDirectoryPath=*my_folder* -</code></pre><p>or this one to update already downloaded docs</p> -<pre tabindex="0"><code>cht --instance=*instance* edit-contacts -- --column=*is_in_emnch* --docDirectoryPath=*my_folder* --updateOfflineDocs -</code></pre></li> -<li> -<p>Then upload the edited documents using the <em><strong>upload-docs</strong></em> command.</p> -<pre tabindex="0"><code>cht --instance=*instance* --upload-docs -</code></pre></li> -</ol> -<h2 id="project-layout">Project layout</h2> -<p>This tool expects a project to be structured as follows:</p> -<pre tabindex="0"><code>example-project/ -.eslintrc -app_settings.json -contact-summary.js -privacy-policies.json -privacy-policies/ -language1.html -… -purge.js -resources.json -resources/ -icon-one.png -… -targets.js -tasks.js -task-schedules.json -forms/ -app/ -my_project_form.xlsx -my_project_form.xml -my_project_form.properties.json -my_project_form-media/ -[extra files] -… -contact/ -person-create.xlsx -person-create.xml -person-create-media/ -[extra files] -… -… -… -translations/ -messages-xx.properties -… -</code></pre><p>If you are starting from scratch you can initialise the file layout using the initialise-project-layout action:</p> -<pre tabindex="0"><code>cht initialise-project-layout -</code></pre><h3 id="derived-configs">Derived configs</h3> -<p>Configuration can be inherited from another project, and then modified. This allows the <code>app_settings.json</code> and contained files (<code>task-schedules.json</code>, <code>targets.json</code> etc.) to be imported, and then modified.</p> -<p>To achieve this, create a file called <code>settings.inherit.json</code> in your project&rsquo;s root directory with the following format:</p> -<pre tabindex="0"><code>{ -&#34;inherit&#34;: &#34;../path/to/other/project&#34;, -&#34;replace&#34;: { -&#34;keys.to.replace&#34;: &#34;value-to-replace-it-with&#34; -}, -&#34;merge&#34;: { -&#34;complex.objects&#34;: { -&#34;will_be_merged&#34;: true -} -}, -&#34;delete&#34;: [ -&#34;all.keys.listed.here&#34;, -&#34;will.be.deleted&#34; -], -&#34;filter&#34;: { -&#34;object.at.this.key&#34;: [ -&#34;will&#34;, -&#34;keep&#34;, -&#34;only&#34;, -&#34;these&#34;, -&#34;properties&#34; -] -} -} -</code></pre>Core: CHT Sync and CHT Pipelinehttps://docs.communityhealthtoolkit.org/core/overview/cht-sync/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/cht-sync/ -<h2 id="overview">Overview</h2> -<p>CHT Sync is an integrated solution designed to enable data synchronization between CouchDB and PostgreSQL for the purpose of analytics. It combines several technologies to achieve this synchronization and provides an efficient workflow for data processing and visualization. The synchronization occurs in real-time, ensuring that the data displayed on dashboards is up-to-date.</p> -<p>Read more about setting up <a href="https://docs.communityhealthtoolkit.org/apps/guides/data/analytics/setup/">CHT Sync</a>.</p> -<!-- make updates to this diagram on the google slides: --> -<!-- https://docs.google.com/presentation/d/1j4jPsi-gHbiaLBfgYOyru1g_YV98PkBrx2zs7bwhoEQ/ --> -<figure class=" center col-8 col-lg-6"><a href="cht-sync.png"> -<img src="cht-sync.png"/> </a> -</figure> -<p><a href="https://github.com/medic/cht-sync">CHT Sync</a> uses <code>couch2pg</code> to replicate data from CouchDB to PostgreSQL in a real-time manner. It listens to changes in the CHT database, and updates the analytics database accordingly. -It is not designed to be accessed by users, and it does not have a user interface. It is designed to be run on the same server as the CHT, but it can be run on a separate server if necessary.</p> -<p>As CHT Sync puts all new data into a PostgreSQL database into a single table that has a <code>jsonb</code> column, this is not very useful for analytics. <a href="https://github.com/medic/cht-pipeline">CHT Pipeline</a> is a set of SQL queries that transform the data in the <code>jsonb</code> column into a more useful format. It uses <a href="https://www.getdbt.com/">DBT</a> to define the models that are translated into PostgreSQL tables or views, which makes it easier to query the data in the analytics platform of choice.</p> -<h4 id="couch2pg">couch2pg</h4> -<p><a href="https://github.com/medic/cht-sync/tree/main/couch2pg">couch2pg</a> streams data from CouchDB and forwards it to PostgreSQL, ensuring real-time updates.</p> -<h4 id="postgresql">PostgreSQL</h4> -<p>A free and open source SQL database used for analytics queries. See more at the <a href="https://www.postgresql.org">PostgreSQL</a> site.</p> -<h4 id="dbt">DBT</h4> -<p>Once the data is synchronized and stored in PostgreSQL, it undergoes transformation using predefined <a href="https://www.getdbt.com/">DBT</a> models from the <a href="https://github.com/medic/cht-pipeline">cht-pipeline</a>. DBT is used to ingest raw JSON data from the PosgtreSQL database (<code>jsonb</code> column) and normalize it into a relational schema to make it easier to query. A daemon runs CHT Pipeline, and it updates the database whenever the data in the <code>jsonb</code> column changes.</p> -<h4 id="data-visualization">Data Visualization</h4> -<p>We recommend <a href="https://superset.apache.org/">Apache Superset</a> as the Data Visualization Tool. Superset is a free, open-source platform for data exploration and data visualization.</p> -<h3 id="cht-core-framework--couchdb">CHT Core Framework &amp; CouchDB</h3> -<p>For more information on these technologies, see <a href="https://docs.communityhealthtoolkit.org/core/overview/architecture/">CHT Core overview</a>.</p>Core: Database schema conventionshttps://docs.communityhealthtoolkit.org/core/overview/db-schema/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/db-schema/ -<p>CouchDB (and PouchDB in the browser) is a JSON-based NoSQL datastore that we use to store our data. While unlike SQL databases there is no enforced schema, code still follows conventions, and this document aims to describe the schema as defined by how our code operates.</p> -<p>In this document &ldquo;record&rdquo; means a JSON object that resides in CouchDB or PouchDB.</p> -<h2 id="general-record-data-structure">General record data structure</h2> -<table> -<thead> -<tr> -<th>Property</th> -<th>Description</th> -<th>Required by</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>_id</code></td> -<td>CouchDB&rsquo;s unique identifier of the record</td> -<td>all records</td> -</tr> -<tr> -<td><code>_rev</code></td> -<td>CouchDB&rsquo;s revision marker</td> -<td>all records</td> -</tr> -<tr> -<td><code>type</code></td> -<td>The general type of the document, see below</td> -<td>all user-created* documents</td> -</tr> -<tr> -<td><code>reported_date</code></td> -<td>Numerical timestamp of when the document is first created</td> -<td>all user-created documents</td> -</tr> -</tbody> -</table> -<ul> -<li>User-created documents here generally means contacts and reports, but may extend further.</li> -</ul> -<h2 id="contacts-persons-and-places">Contacts (Persons and Places)</h2> -<p>Contacts are either places (e.g. clinic), groupings (e.g. family) or people (e.g. a patient or CHW).</p> -<p>The <code>type</code> property of contact records depends on the version of Medic you are running:</p> -<ul> -<li>If you are running 3.7 or later you get to <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/hierarchy/#app_settingsjson-contact_types">configure your contact hierarchy</a>, and the <code>type</code> of contacts is <code>contact</code>, and the configured type is in the <code>contact_type</code> property.</li> -<li>In earlier versions the type depended on hierarchical location of the contact. There are 3 hard coded place types: <code>district_hospital</code>, <code>health_centre</code> and <code>clinic</code> and one people type <code>person</code>. These place names are often meaningless (hence the configurable contact hierarchy in later versions) to the configured project, and are textually (ie in the UI not in data structures) renamed to mean other things. For example, as <code>clinic</code> is the lowest level it is often used to represent a family.</li> -</ul> -<h3 id="places">Places</h3> -<p>Represent either an actual physical location such as a clinic, or a grouping such as a family or region.</p> -<p>Unless a place is at the top of the hierarchy it has a <code>parent</code> place.</p> -<p>Each location has a primary contact, which is a <code>person</code> contact stored in the <code>contact</code> property.</p> -<h3 id="people">People</h3> -<p>People are both patients in the system and users of the system, such as CHWs or Nurses. Users have additional records marking them as users of the system (see <a href="#users">User</a> below).</p> -<p>People always have a <code>parent</code> place.</p> -<h3 id="parent-hierarchy-representation">Parent hierarchy representation</h3> -<p>Contacts <strong>store</strong> their parent hierarchy as a minified hierarchical structure, which records the <code>_id</code> of each parent up until the top of the hierarchy:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;person&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">name</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;A patient&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">parent</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">_id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;clinic-id&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">parent</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">_id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;health_centre-id&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">parent</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">_id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;district_hospital-id&#39;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>Generally when contacts are <strong>used</strong> in the app they are first &ldquo;hydrated&rdquo;, with the rest of the information filled in from their parent&rsquo;s place documents:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;person&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">name</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;A patient&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">parent</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">_id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;clinic-id&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">name</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;A clinic&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">reported_date</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1234</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">...</span> <span style="color:#8f5902;font-style:italic">// etc -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">parent</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">_id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;health_centre-id&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">name</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;A Health Centre&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">reported_date</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1134</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">...</span> <span style="color:#8f5902;font-style:italic">// etc -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">parent</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">_id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;district_hospital-id&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">name</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;THE District Hospital&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">reported_date</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1034</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">...</span> <span style="color:#8f5902;font-style:italic">// etc -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div> -<p><em>See Also</em>: <a href="https://docs.communityhealthtoolkit.org/apps/guides/data/hydration/">Document hydration</a></p> -<p>As of version <strong>3.10</strong>, you can connect contacts with other documents via the <code>linked_docs</code> property. This allows the app to have access to these linked documents when the contact is used.</p> -<p>Like the parent hierarchy, linked docs are stored as a minified object, where every linked doc is identified by a string tag and the UUID of the document it represents.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;person&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">name</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;A patient&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">parent</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">_id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;clinic-id&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">linked_docs</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">custom_tag1</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;some-contact-id&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">custom_tag2</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;other-contact-id&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">custom_tag3</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;report-id&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">...</span> <span style="color:#8f5902;font-style:italic">// etc -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><p>Linked docs are shallowly &ldquo;hydrated&rdquo; along with the parent hierarchy:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;person&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">name</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;A patient&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">parent</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">_id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;clinic-id&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">name</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;A clinic&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">reported_date</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1234</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">...</span> <span style="color:#8f5902;font-style:italic">// etc -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">parent</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">_id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;health_centre-id&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">name</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;A Health Centre&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">reported_date</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1134</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">...</span> <span style="color:#8f5902;font-style:italic">// etc -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000">parent</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">_id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;district_hospital-id&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">name</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;THE District Hospital&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">reported_date</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1034</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">...</span> <span style="color:#8f5902;font-style:italic">// etc -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">linked_docs</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">custom_tag1</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">_id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;some-contact-id&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">name</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;some contact&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;person&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">parent</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">_id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;other-clinic&#39;</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">reported_date</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">4569</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">...</span> <span style="color:#8f5902;font-style:italic">// etc -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">custom_tag2</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">_id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;other-contact-id&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">name</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;other contact&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">type</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;clinic&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">parent</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">_id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;health_center&#39;</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">...</span> <span style="color:#8f5902;font-style:italic">// etc -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">custom_tag3</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">_id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;report-id&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">form</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;FORM&#39;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000">contact</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> <span style="color:#000">_id</span><span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#4e9a06">&#39;submitter-id&#39;</span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">...</span> <span style="color:#8f5902;font-style:italic">// etc -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"></span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h2 id="reports">Reports</h2> -<p>Reports are created by users filling out and submitting forms, as well as sending in SMS.</p> -<p>All reports:</p> -<ul> -<li>Use the <code>data_record</code> type</li> -<li>Have their fields stored in the <code>fields</code> property</li> -<li>Have the report author&rsquo;s phone number (if it exists) stored in the <code>from</code> field</li> -<li>Store the form&rsquo;s identifier in the <code>form</code> field</li> -<li>May have a <code>contact</code> property, which is a minified version of the report author&rsquo;s contact and its hierarchy (see above)</li> -</ul> -<p>Reports can and should be linked to a contact when possible. The report&rsquo;s associated contact (sometimes called the report&rsquo;s subject) can be either a person or place. The link between report and contact is established by defining one of the following properties within the report:</p> -<ol> -<li>A person shortcode or uuid at <code>doc.fields.patient_id</code>, <code>doc.fields.patient_uuid</code>, or <code>doc.patient_id</code></li> -<li>A place shortcode or uuid at <code>doc.fields.place_id</code> or <code>doc.place_id</code>.</li> -</ol> -<p>Additionally, SMS reports:</p> -<ul> -<li>Have an <code>sms_message</code> property which contains, among other things, the raw SMS</li> -<li>May not have a <code>contact</code> property if the SMS comes from a phone number that does not have an associated contact</li> -</ul> -<p>Additionally, XML reports:</p> -<ul> -<li>Have the XML file that Enketo (the XForm renderer used) generates as an attachment</li> -<li>Have a <code>content_type</code> property of <code>xml</code></li> -</ul> -<h2 id="forms">Forms</h2> -<p>SMS forms are defined in <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/patient_reports/#app_settingsjson-patient_reports">application config</a>.</p> -<p>XML forms are stored in the database and have:</p> -<ul> -<li>An <code>_id</code> of <code>form:&lt;formname&gt;</code></li> -<li>The <code>type</code> of <code>form</code></li> -<li>The actual XML Xforms definition attached</li> -</ul> -<p>XML forms are defined as XForm XML files</p> -<h2 id="users">Users</h2> -<p>Users represent credentials and roles / permissions for accessing the application. This can either be:</p> -<ul> -<li>people who can log into the application, such as CHWs or Nurses</li> -<li>or credentials granting external software restricted permissions to perform certain tasks, such as allowing an external service permission to write reports via the api.</li> -</ul> -<p>User records have at least:</p> -<ul> -<li>An <code>_id</code> of <code>org.couchdb.user:&lt;username&gt;</code></li> -<li>A <code>name</code> which is the same as <code>&lt;username&gt;</code> above</li> -<li>A <code>roles</code> array</li> -</ul> -<p>There are two slightly different copies of this record stored.</p> -<p>The record in the <code>_users</code> database includes:</p> -<ul> -<li>The <code>type</code> of <code>user</code></li> -<li>The password hash and associated data</li> -</ul> -<p>The <code>_users</code> database is what CouchDB uses for authentication and is only editable by administrative users, so is authoritative when it comes to roles and the like.</p> -<p>The <code>medic</code> database stores a copy of roles and permissions along with:</p> -<ul> -<li>The <code>type</code> of <code>user-settings</code></li> -<li>They may have a <code>contact_id</code> field that is the <code>_id</code> of the <em>person</em> that the user is attached to</li> -<li>They may also have a <code>facility_id</code> field that is the <code>_id</code> of the <em>place</em> that the user is attached to</li> -<li>They may also have a <code>known</code> field. If this field is <code>true</code>, it means the user has logged in once. Otherwise, it will be <code>undefined</code>.</li> -</ul> -<p>Note that SMS users do not have a users record: their phone number will be attached to a <code>person</code> record, but they do not have a user because they do not access the application.</p> -<p>Users then, can be represented by up to 3 docs:</p> -<ul> -<li>a <code>person</code> document that represents a physical human being in our hierarchy of places and people</li> -<li>a <code>users</code> document that represents authorisation and authentication information for physical people or authenticated external services</li> -<li>a <code>user-settings</code> document that ties the <code>user</code> and <code>person</code> documents together</li> -</ul> -<h2 id="tasks">Tasks</h2> -<p><a href="https://docs.communityhealthtoolkit.org/apps/reference/tasks/#tasksjs">Partner configuration code</a> running inside the Core Framework can cause tasks to appear within the Tasks tab. Each task in the tab is powered by a task document. Task documents are:</p> -<ul> -<li>updated only after the data for their emitting contact changes or every 7 days</li> -<li>created in the database for any task due within the last 60 days</li> -<li>immutable once their state is &ldquo;terminal&rdquo; (Cancelled, Completed, Failed)</li> -</ul> -<table> -<thead> -<tr> -<th>State</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>Draft</td> -<td>Task has been calculated but it is scheduled in the future</td> -</tr> -<tr> -<td>Ready</td> -<td>Task is currently showing to the user</td> -</tr> -<tr> -<td>Cancelled</td> -<td>Task was not emitted when refreshing the requester&rsquo;s data. Task has invalid partner emission.</td> -</tr> -<tr> -<td>Completed</td> -<td>Task was emitted with { resolved: true }</td> -</tr> -<tr> -<td>Failed</td> -<td>Task was never terminated and the endDate has past</td> -</tr> -</tbody> -</table> -<table> -<thead> -<tr> -<th>Attribute</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>user</td> -<td>The user settings id of the user who calculated and created the document. Used for controlling replication. (eg. <code>org.couchdb.user:agatha</code>)</td> -</tr> -<tr> -<td>requester</td> -<td>The <a href="https://docs.communityhealthtoolkit.org/glossary/">guid</a> of the contact whose data brought about the creation of the document. Used for controlling cancellation.</td> -</tr> -<tr> -<td>owner</td> -<td>The guid of the contact whose profile this task will appear on in the contact&rsquo;s tab.</td> -</tr> -<tr> -<td>forId</td> -<td>If completing a task&rsquo;s action opens a form. Completing the form creates a report. <code>forId</code> is the guid of the contact information that will be passed into the form. For most forms, the resulting report will be associated with this contact.</td> -</tr> -<tr> -<td>emission</td> -<td>Minified task data emitted from the partner code.</td> -</tr> -<tr> -<td>stateHistory</td> -<td>Each time the state attribute changes, the time of the change is recorded in the state history.</td> -</tr> -</tbody> -</table> -<p>To understand the difference between a task requester and a task owner, kindly see the example below:</p> -<ul> -<li>A CHW submits a delivery form. The patient is woman1, and she delivered one child child1.</li> -<li>The delivery form generates a task, delivery_child_followup, but the task&rsquo;s patient is child1 not woman1</li> -<li>In the case of this task, the requester is woman1, because it was their delivery report that triggered task generation, but the owner is child1.</li> -</ul> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;task~org.couchdb.user:agatha~pregReport~pregnancy-facility-visit-reminder~2~523435132468&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;task&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;authoredOn&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">523435132468</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;user&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;org.couchdb.user:agatha&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;requester&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;requester-contact-guid&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;owner&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;owner-contact-guid&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;state&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Ready&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;emission&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;pregReport~pregnancy-facility-visit-reminder~2&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;forId&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;for-contact-guid&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;dueDate&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2000-01-01&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;startDate&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;1999-12-29&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;endDate&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;2000-01-08&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;priority&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;high&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;priorityLabel&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;task.priority&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#a40000">...</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;stateHistory&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;state&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Ready&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;timestamp&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">523435132468</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}],</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div><h2 id="targets">Targets</h2> -<p><a href="https://docs.communityhealthtoolkit.org/apps/reference/targets/#targetsjs">Partner configuration code</a> can configure targets to appear within the Targets/Analytics tab. Target documents are:</p> -<ul> -<li>one per analytics reporting period</li> -<li>updated when the user loads the application or when they view the targets tab</li> -<li>updated a maximum of once per day</li> -</ul> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;_id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;target~2000-01~user-contact-guid~org.couchdb.user:agatha&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;target&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;user&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;org.couchdb.user:agatha&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;owner&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;user-contact-guid&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;updated_date&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">523435132468</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;targets&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;deaths-this-month&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#a40000">...</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;value&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;pass&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;total&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">15</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#a40000">...</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div>Core: Offline-First in the CHThttps://docs.communityhealthtoolkit.org/core/overview/offline-first/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/offline-first/ -<h2 id="introduction">Introduction</h2> -<p>CHT applications are designed to be used equally well in areas with no internet connectivity, slow or unreliable internet connectivity, and good internet connectivity. Achieving reliable performance and powerful features requires diligence and strict adherence to the principles of Offline-First development.</p> -<p>In this page we&rsquo;ll cover why and how we achieve this in the CHT.</p> -<h2 id="why-this-is-important">Why this is important</h2> -<p>The CHT is designed to improve healthcare in the hardest to reach communities. While some users have a strong internet connection, to be as inclusive as possible we optimise for Care Teams with connections that are intermittent, unreliable, expensive, and low bandwidth. To achieve this the CHT is designed to be offline first, which, as the name suggests, means the application never relies on an internet connection for day to day tasks.</p> -<p>Caveat: a small set of use cases require a decent internet connection, but these are limited to:</p> -<ul> -<li>Logging in</li> -<li>Initial download of the application and data</li> -<li>Changing your own password</li> -<li>Most administrative tasks such as creating new users</li> -<li>Data analytics over large data sets</li> -</ul> -<h2 id="requirements">Requirements</h2> -<h3 id="code">Code</h3> -<p>The CHT uses a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers">Service Worker</a> to store the code needed to start up and run which is best practice for caching web applications. The CHT Core Framework code is downloaded when the user visits the login page for the first time and completes silently in the background while the user logs in.</p> -<p>The code includes JavaScript, CSS, HTML, fonts, and images. It doesn&rsquo;t contain any private information so it&rsquo;s safe to download and cache it without authentication. It also doesn&rsquo;t contain deployment specific configuration (covered in detail <a href="#data">below</a>).</p> -<p>The CHT Core Framework checks for updates to the code by attempting to connect to the server in the background. The user isn&rsquo;t notified if this check fails or times out, so they can continue to complete workflows without interruption. When an update is found the new code is downloaded and the service worker cache is updated. The user is prompted to reload to start using the new version of the application, but this can be dismissed if they are busy with a patient or don&rsquo;t want to lose their progress if they are midway through an action like creating a contact or completing a form. Either way, the update will be automatically applied the next time the application starts up.</p> -<h3 id="data">Data</h3> -<p>Once the application has started up it needs data to be useful. This data falls into two categories:</p> -<ul> -<li><strong>Configuration</strong> specific to a single deployment such as forms, task definitions, places, and hierarchies.</li> -<li><strong>Patient data</strong> such as personal details, information about visits, reports, tasks, and messages.</li> -</ul> -<p>Both of these types of data are cached on the device because they are required for the user to do their job. The CHT Core Framework uses <a href="https://pouchdb.com/">PouchDB</a> to store the data on the device&rsquo;s disk. Once cached, when the application starts up the code is executed, which reads the data, allowing the user to do their job regardless of the quality of their internet connection.</p> -<p>If the user creates new patient data by registering a family or completing a task the application stores this in the phone&rsquo;s cache and attempts to submit this to the server. Periodically the application checks to see if there is new data on the server that is relevant to the user and if so, it updates the cache. This process of sending and receiving data updates is called <a href="https://docs.communityhealthtoolkit.org/apps/guides/performance/replication/">replication</a>, and is performed without interrupting the user from their work.</p> -<h2 id="faqs-from-cht-contributors">FAQs from CHT contributors</h2> -<h3 id="q-should-i-rely-on-request-failure-handling">Q: Should I rely on request failure handling?</h3> -<p>A: No. This only works well when the user has a strong connection, or if the user is completely offline and the request fails immediately. It&rsquo;s impossible to know for sure how long the request will take, so users with poor connectivity may end up waiting forever. If this request is essential to doing their job they will be unable to move on. It is important to handle request failures gracefully, but it doesn&rsquo;t make a request offline first.</p> -<h3 id="q-should-i-rely-on-a-request-timeout">Q: Should I rely on a request timeout?</h3> -<p>A: No. This attempts to mitigate the problem by unblocking the user eventually. The problem is setting the timeout length correctly - if it&rsquo;s too short then the request won&rsquo;t succeed, but if it&rsquo;s too long the user will be required to wait. Experience suggests the timeout for a simple request would have to be around a minute to allow the request to succeed for most users, but a minute is far too long to expect a user to wait. Almost all requests should have a timeout, but that is not sufficient to make a request offline first.</p> -<h3 id="q-should-i-rely-on-a-spinner-or-loading-bar">Q: Should I rely on a spinner or loading bar?</h3> -<p>A: No. Much like the timeout solution, this is an attempt to mitigate the problem in this case by giving the user more information about the request. Regardless of the UX, blocking user interaction while waiting for a response is not offline first and therefore not appropriate for the CHT. Showing UX elements when loading is still recommended for local requests, user initiated server requests, and other potentially slow operations.</p> -<h3 id="q-how-can-i-check-if-the-user-is-authenticated">Q: How can I check if the user is authenticated?</h3> -<p>A: It is not possible to know for certain if a user has an active session without getting a response from the server. The CHT caches some data to hint at whether the session is still active, for example the session expiry date. However, the only way to be certain is to connect to the server, which is not offline first. This has been implemented in the CHT by checking the status code on background requests such as replication, and instructing the user to login when necessary.</p> -<h3 id="q-should-i-rely-on-apis-which-report-device-connection-status">Q: Should I rely on APIs which report device connection status?</h3> -<p>A: No. There are <a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine">APIs available</a> which report whether the device is online with reasonable reliability. Unfortunately these APIs don&rsquo;t reliably report whether the connection is slow, whether the connection will be held until the request completes, or whether the CHT server is available. This means that even if the browser API reports that the device is online the code will still make a request that may fail or wait indefinitely for a response. These APIs can be useful but must be used in conjunction with Offline-First principles, for example, the CHT uses the &ldquo;online&rdquo; event listener as a prompt to attempt replication in the background.</p> -<h3 id="q-should-i-add-a-feature-that-requires-a-connection">Q: Should I add a feature that requires a connection?</h3> -<p>A: Maybe. There are some things that cannot be done offline first because they either require server interaction (eg: authentication during login) or they need to access data that cannot be cached locally (eg: deployment-wide analytics). Every effort should be made to find a way to implement the feature using Offline-First principles, and trade-offs discussed with senior CHT contributors. If the feature truly needs to be online first then the UX needs to be designed so that the user understands the feature requires an internet connection.</p> -<h2 id="further-reading">Further reading</h2> -<ul> -<li><a href="https://alistapart.com/article/offline-first/">Designing Offline-First Web Apps</a> by A List Apart</li> -<li><a href="https://hasura.io/blog/design-guide-to-offline-first-apps/">A Design Guide for Building Offline First Apps</a> by Hasura</li> -<li><a href="https://blog.couchdb.org/2017/09/19/couchdb-takes-medic-mobile-to-the-front-lines-of-healthcare-work/">CouchDB takes CHT to the front lines of healthcare work</a> by CouchDB</li> -</ul>Core: Installation as a Progressive Web Apphttps://docs.communityhealthtoolkit.org/core/overview/pwa/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/pwa/ -<h2 id="what-is-a-progressive-web-app-pwa">What is a Progressive Web App (PWA)?</h2> -<p>A PWA is a web application that can be used like a website in the browser, but the user can choose to &ldquo;install&rdquo; it. This means a shortcut is added to the home screen of the device, and when the application is run it doesn&rsquo;t have the usual browser address bar and tabs so it looks like a regular application.</p> -<p>The CHT Core webapp has been developed to be a PWA to give users more choice about how applications are installed.</p> -<h2 id="benefits-of-pwa-installation">Benefits of PWA installation</h2> -<p>Using a PWA avoids the need to build an Android application and publish it to the Play Store which can be cumbersome and slow. This also means there&rsquo;s no need for the user to install or update the Android application as any updates will be downloaded automatically from the CHT server.</p> -<p>In addition you can install PWAs on a wider range of devices, for example, desktop computers.</p> -<p>Deployments can use a mixture of PWA and Android applications.</p> -<h2 id="benefits-of-android-applications">Benefits of Android applications</h2> -<p>Installation of the PWA may be more difficult for users, for example they will need to visit the website and then follow the instructions to install it. This process is less well known than a usual Play Store installation.</p> -<p>Certain features are only available with an Android application, including integration with other android apps, and sending SMS from within the app.</p> -<h2 id="how-to-distribute-a-cht-app-as-a-pwa">How to distribute a CHT app as a PWA</h2> -<ol> -<li>Ensure branding configuration is complete. Without this the browser won&rsquo;t recognize the CHT as a PWA and installation will not be possible. Specifically the app will need a <code>title</code> and <code>icon</code> as these are used for the icon on the home screen. To set this up, follow the instructions on the <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/application-graphics/#1-site-branding">branding page</a>.</li> -<li>In Chrome or Firefox go to the application URL, log in, and wait for the webapp to finish loading completely.</li> -<li>Follow these <a href="https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Add_to_home_screen">instructions for PWA installation</a>.</li> -</ol> -<p>Now the application can be used offline just like a regular Android application.</p>Core: Data Flows for Analyticshttps://docs.communityhealthtoolkit.org/core/overview/data-flows-for-analytics/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/data-flows-for-analytics/ -<p>In this section, we focus on how data flows through the various components of the Community Health Toolkit. The CHT is built to support the delivery of quality community health care at the last mile. The CHT is designed to work in areas with low connectivity, which means it is an <a href="https://docs.communityhealthtoolkit.org/core/overview/offline-first/">Offline-First</a> toolkit for care provision. The architectural and technology choices in the stack are mostly guided by this principle, which will be evident in the discussion of the data management pipeline.</p> -<h2 id="overview">Overview</h2> -<p><img src="data-flows.png" alt="Data Flows"></p> -<p>At a high level:</p> -<ul> -<li>Data are collected from the device of a health worker;</li> -<li>Data are pushed to an online instance from where data are available to other health workers, supervisors, and decision makers;</li> -<li>Data are transferred to a relational database (PostgreSQL) using <a href="https://github.com/medic/medic-couch2pg">couch2pg</a> and made available for impact monitoring, data science projects, and visualizations;</li> -<li>Access to PostreSQL is given to relevant parties at this level, for example members of the Research &amp; Learning team for impact monitoring and data science;</li> -<li>Visualization platforms, such as <a href="https://www.klipfolio.com/">Klipfolio</a>, are then connected to PostgreSQL from where program managers and other partner representatives can access visualizations of their data for decision-making</li> -</ul> -<h2 id="details-of-the-data-flow">Details of the data flow</h2> -<p>The layout detailed here is specific to how Medic supports its CHT partners at the moment. It is replicable and can be deployed as is or tweaked independent of Medic either by modifying or replacing pieces of it with other options.</p> -<h3 id="current-infrastructure">Current infrastructure</h3> -<p>We look at this in three general phases.</p> -<h4 id="1-data-collection">1. Data Collection</h4> -<p>Data is collected in the community at the point of care, i.e. the community health worker interacting with the toolkit. These tools and their corresponding data stores are::-</p> -<ul> -<li>Mobile app -&gt; PouchDB</li> -<li>Webapp -&gt; PouchDB / CouchDB</li> -<li>Text forms / sms -&gt; SMS gateway / SMS aggregator -&gt; CouchDB</li> -</ul> -<p>The mobile app and webapp, when deployed for offline first use, use a local database namely PouchDB. Similar to CouchDB, it is a document-oriented database. The data collected in PouchDB is synced to an online CouchDB upon the user connecting to the internet. Local storage is not applicable to SMS; instead, an <a href="https://github.com/medic/cht-gateway">SMS gateway</a> or an SMS aggregator (for example <a href="https://africastalking.com">Africa&rsquo;s Talking</a>) is used to help get the data to an online CouchDB instance.</p> -<p>Ultimately all the data ends up in a CouchDB instance deployed in the cloud whether through data synchronization with PouchDB local to the health workers devices, use of SMS aggregators or gateway. It should be mentioned that you can have a deployment supported by all of webapp, mobile app and SMS and have all the data end up in the same CouchDB instance.</p> -<h4 id="2-data-transformation">2. Data Transformation</h4> -<p>We use <a href="https://github.com/medic/medic-couch2pg">couch2pg</a> or <a href="https://docs.communityhealthtoolkit.org/core/overview/cht-sync/">cht-sync</a> to move data from CouchDB to a relational database, PostgreSQL in this case. The choice of PostgreSQL for analytics dashboard data sources is to allow use of the more familiar SQL querying. It is an open source tool that can be <a href="https://github.com/medic/medic-couch2pg#user-content-installation-steps-if-applicable">easily deployed</a>. When deployed the service uses <a href="https://docs.couchdb.org/en/stable/api/database/changes.html">CouchDB&rsquo;s changes feed</a> which allows capturing of everything happening in CouchDB in incremental updates. It is run and monitored by the operating system where it is configured to fetch data at a configurable interval.</p> -<p>Data copied over to PostgreSQL is first stored as raw json (document) making use of PostgreSQL&rsquo;s jsonb data type to create an exact replica of a CouchDB database. From this, default views are created at deployment of the service and refreshed during every subsequent run. Additional custom materialized views created later are also refreshed at this time.</p> -<p>Custom materialized views and functions are added specific to a deployment&rsquo;s needs. Generally the following naming convention is recommended:</p> -<ul> -<li><em>formview</em> as a view of raw forms</li> -<li><em>useview</em> as a view of form data supporting a use case as defined by design</li> -<li><em>contactview</em> as a view of people and places</li> -<li>Database functions are used as a way to join as much relevant data as possible for easier querying in analytics or dashboard visualizations.</li> -</ul> -<p>Data in the views and functions mentioned in this section is as accurate as the accuracy of the SQL queries. Best practice is to begin the process of defining these objects at design in order to align analytics and dashboards requirements with workflows being deployed.</p> -<h4 id="3-data-use">3. Data Use</h4> -<p>The data in PostgreSQL is mostly either used by direct querying or via dashboard visualizations for impact monitoring and data driven-decision making. Database visualizations are built scoped to the requirements of supporting a successful deployment. The work of our Research &amp; Learning team, specifically data science, is supported at the PostgreSQL level through updated contactviews, formviews, useviews and functions with access to these provided to relevant parties as and when needed. Our use of data follows our Privacy &amp; Data Protection policy and is in accordance to agreements with our CHT partners.</p> -<p>As mentioned previously, formviews are built to present data in a structure similar to the data collection tool (form) used. Useviews are tailored to align with a use case, mostly using the formviews as the data sources. These are fundamentally guided by design of the workflows and should be interpreted in the context of the design materials including a document explaining the definitions of variables used.</p> -<p>The objects present here are not limited to views and functions. Additional tables can be added, for example providing mappings or supporting operations external to the functions available in the toolkit. In short, there is no limitation to the utility that can be added this level to support analytics and dashboards. That said, measures are taken to ensure controlled access, reliability and timely access of the data by the various parties. Some of these measures are:</p> -<ul> -<li>Roles and users allocation and deallocation done by specific roles within partner technical teams with support from Medic as needed;</li> -<li>Access control management is left to the partner technical teams where possible;</li> -<li>Dashboard data source refresh intervals set to align with project needs;</li> -<li>Update of the data sources monitored to ensure updating works as expected;</li> -<li>Review of the dashboards as part of the design process;</li> -<li>Qualitative design activities to interrogate trends observed in the dashboards and iterate on them if need be;</li> -</ul> -<h3 id="beyond-our-current-pipeline">Beyond Our Current Pipeline</h3> -<p>The <a href="https://github.com/medic/cht-core">cht-core</a> is mostly data collection tools and is the first component of the data management pipeline. It is the core part of a deployment but the rest of the tools can be easily replaced with other preferred options. It also helps that couch2pg is an open source tool which provides the opportunity for collaboration to extend its functionality to support other implementations. Klipfolio, the tool that we currently use for visualizations, is a proprietary tool but there are many open source options, such as <a href="https://superset.incubator.apache.org/">Apache Superset</a> that are worth exploring and building into future iterations of our impact monitoring and analytics support for the CHT.</p> -<h2 id="backup">Backup</h2> -<p>The machines running each of CouchDB and PostgreSQL instances are backed up daily.</p>Core: Sentinel Transitionshttps://docs.communityhealthtoolkit.org/core/overview/transitions/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/transitions/ -<p>A transition is javascript code that runs when a document is changed. A -transition can edit the changed doc or do anything server side code can do for -that matter.</p> -<p>Transitions are run in series, not in parallel:</p> -<ul> -<li> -<p>For a given change, you can expect one transition to be finished before the -next runs.</p> -</li> -<li> -<p>You can expected one change to be fully processed by all transitions before -the next starts being processed.</p> -</li> -</ul> -<p>Transitions obey the following rules:</p> -<ul> -<li> -<p>has a <code>filter(doc)</code> function that accepts the changed document as an argument and -returns <code>true</code> if it needs to run/be applied.</p> -</li> -<li> -<p>a <code>onMatch(change, db, auditDb, callback)</code> function than will run on changes -that pass the filter.</p> -</li> -<li> -<p>can have an <code>init()</code> function to do any required setup and throw Errors on invalid -configuration.</p> -</li> -<li> -<p>has an <code>onChange(change, db, audit, callback)</code> function that makes changes to -the <code>change.doc</code> reference (copying is discouraged). <code>db</code> and <code>audit</code> are -handled to let you query those DBs. You can learn more about <code>callback</code> in subsequent sections.</p> -</li> -<li> -<p>It is not necessary for an individual transition to save the changes made to <code>change.doc</code> to the db, the doc will be saved once, after all the transitions have edited it. -If an individual transition saves the document provided at <code>change.doc</code>, it takes responsibility re-attaching the newly saved document (with new seq etc) at <code>change.doc</code></p> -</li> -<li> -<p>guarantees the consistency of a document.</p> -</li> -<li> -<p>runs serially and in any order. A transition is free to make async calls but -the next transition will only run after the previous transitions&rsquo;s callback -is called. If your transition is dependent on another transition then it will -run on the next change. Code your transition to support two changes rather -than require a specific ordering. You can optimize your ordering but don&rsquo;t -require it. This keeps configuration simpler.</p> -</li> -<li> -<p>is repeatable, it can run multiple times on the same document without -negative effect. You can use the <code>transitions</code> property on a document to -determine if a transition has run.</p> -</li> -</ul> -<p>Callback arguments:</p> -<ul> -<li> -<p>callback(err, needsSaving)</p> -<p><code>needsSaving</code> is true if the <code>change.doc</code> needs to be saved to db by the transition runner. For instance if the transition has edited the <code>change.doc</code> in memory. -<code>err</code> if truthy, the error will be added to the <code>changes.doc</code> in memory. (Note that if <code>needsSaving</code> is falsy, the doc will not be saved, so that error will not be persisted).</p> -</li> -</ul> -<p>Regardless whether the doc is saved or not, the transitions will all be run (unless one crashes).</p> -<p>When your transition encounters an error, there are different ways to deal with it. You can :</p> -<ul> -<li>finish your transition with <code>callback(someError, true)</code>. This will save the error to <code>change.doc</code>.</li> -<li>finish your transition with <code>callback(someError, false)</code>. The error will be logged to the sentinel log. This will not save the error on the <code>change.doc</code>, so there will be no record that this transition ran. That particular <code>change</code> will not go through transitions again, but if the same doc has another change in the future, since there is no record of the erroring transition having run, it will be rerun.</li> -<li>crash sentinel. When sentinel restarts, since that <code>change</code> did not record a successful processing, it will be reprocessed. Transitions that did not save anything to the <code>change.doc</code> will be rerun.</li> -</ul>Core: How To Manage Translationshttps://docs.communityhealthtoolkit.org/core/overview/translations/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/translations/ -<p>Apps built with CHT Core are localized so that users can use them in the language of their choice. Languages supported by default are English, French, Nepali, Spanish, and Swahili. The goal of this doc is to help the community manage these and future translations.</p> -<h2 id="overview">Overview</h2> -<p>Like the rest of the code the translation files live in the GitHub repo. These translation files are <a href="https://en.wikipedia.org/wiki/.properties">properties files</a>, which are a series of keys and their corresponding values. The English file is used by default, and as such, it contains the entire set of keys. If any key is missing from another language file the English value is used.</p> -<h2 id="adding-new-languages">Adding new languages</h2> -<p>New languages must be added and configured in several places:</p> -<ul> -<li>Create a new <code>messages-xx.properties</code> file in the <a href="https://github.com/medic/cht-core/tree/master/api/resources/translations"><code>api/resources/translations</code></a> folder, replacing &ldquo;xx&rdquo; with the 2 or 3 letter <a href="https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes">language code</a>.</li> -<li>Add the language to the <a href="https://github.com/medic/cht-core/blob/e6d184946affc62773d569168216a5b913f38a30/api/src/translations.js#L17"><code>LOCAL_NAME_MAP</code> in api</a>. Use the language code for the key, and the local name followed by the English name for the language in brackets, eg: &ldquo;fr: &lsquo;Français (French)&rsquo;&rdquo;.</li> -<li>Import the moment language pack in the <a href="https://github.com/medic/cht-core/blob/e6d184946affc62773d569168216a5b913f38a30/webapp/src/ts/main.ts#L23">main.ts file</a>. If moment doesn&rsquo;t provide the required language pack you may need to contribute it upstream to the moment library.</li> -</ul> -<h2 id="adding-new-keys">Adding new keys</h2> -<ol> -<li>First check if an appropriate key already exists in <a href="https://github.com/medic/cht-core/blob/master/api/resources/translations/messages-en.properties"><code>messages-en.properties</code></a>. Using existing keys when possible reduces the effort required to translate the app into a new language.</li> -<li>Create a new key and default English value.</li> -<li>Reach out to the community to translate the value into all supported languages. The CI will block merging PRs unless all values are provided.</li> -<li>Validate the translations are complete and correct by executing <code>npm run lint-translations</code>.</li> -</ol> -<h3 id="translating-static-text">Translating static text</h3> -<p>In angular this is done using angular-translate, and ideally using the <a href="http://angular-translate.github.io/docs/#/guide/05_using-translate-directive">translate directive</a> to reduce the number of watchers, eg: <code>&lt;h3 translate&gt;date.incorrect.title&lt;/h3&gt;</code>.</p> -<p>Use the translation functions in the config module in <a href="https://github.com/medic/cht-core/blob/e6d184946affc62773d569168216a5b913f38a30/api/src/config.js#L72">API</a> and <a href="https://github.com/medic/cht-core/blob/e6d184946affc62773d569168216a5b913f38a30/sentinel/src/config.js#L88">Sentinel</a>.</p> -<h3 id="translating-configurations">Translating configurations</h3> -<p>Much of the app is configurable (eg: forms and schedules). Because the specifics of the configuration aren&rsquo;t known during development time, these can&rsquo;t be provided via messages. Instead configurers can provide a map of locale to value for each translated property. Then use the <code>translateFrom</code> filter to translate from the configured map using the user&rsquo;s language.</p> -<h2 id="modifying-any-existing-translation-values">Modifying any existing translation values</h2> -<ol> -<li>Update the default English value.</li> -<li>Reach out to the community to translate the value into all supported languages.</li> -<li>Validate the translations are complete and correct by executing <code>npm run lint-translations</code>.</li> -</ol> -<h2 id="removing-translation-keys">Removing translation keys</h2> -<p>Carefully verify that the translation key isn&rsquo;t used. This can be challenging if keys have been concatenated or generated because then you won&rsquo;t be able to find the complete string in the source code. Once this has been confirmed, then simply remove the key and value from all translation files.</p>Core: Roadmaphttps://docs.communityhealthtoolkit.org/core/overview/roadmap/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/roadmap/ -<div class="pageinfo pageinfo-primary"> -<p>The CHT is actively developed by <a href="https://medic.org">Medic</a>, a non-profit organization that serves as the technical steward, alongside many <a href="https://docs.communityhealthtoolkit.org/why-the-cht/#the-cht-community">partners in the CHT Community</a>.</p> -</div> -<p>Improvements for the CHT are raised and discussed with the community in the <a href="https://forum.communityhealthtoolkit.org/c/product/23">Forum</a>, and tracked in the <a href="https://roadmap.communityhealthtoolkit.org">CHT Roadmap</a>. The roadmap is organized around <em><strong>Product initiatives</strong></em>; specific improvement areas that align with medium and long-term vision for the CHT. These initiatives are organized by the ones that are being worked on <em><strong>Now</strong></em>, those that are likely <em><strong>Next</strong></em>, and those that might be <em><strong>Later</strong></em>.</p> -<h3 id="views">Views</h3> -<p>The views represent initiatives being tackled by multidisciplinary working groups that focus on different key users: <strong>Care Teams</strong> for patients, caregivers, CHWs, supervisors; <strong>Allies</strong> for those that support health systems, like program administrators and app builders; and <strong>Infrastructure</strong> for CHT hosting service providers and core developers.</p> -<h3 id="communication">Communication</h3> -<p>Initiatives are communicated quarterly to the CHT Community on the <a href="https://forum.communityhealthtoolkit.org/c/product/roadmaps/25">CHT Forum</a> and discussed monthly on the CHT Roundup calls. Please reach out via the <a href="https://forum.communityhealthtoolkit.org/c/product/23">Forum</a> to discuss your ideas and thoughts on product improvements.</p> \ No newline at end of file +Overview of CHT Components on Community Health Toolkithttps://docs.communityhealthtoolkit.org/core/overview/Recent content in Overview of CHT Components on Community Health ToolkitHugo -- gohugo.ioenArchitecture of CHT Instanceshttps://docs.communityhealthtoolkit.org/core/overview/architecture/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/architecture/Overview Server CHT Core Framework The cht-core product is the primary component of the CHT. The server comes with authentication, role based authorization, data security, and a range of protected data access endpoints. Read more detail in cht-core GitHub repository. +API A NodeJS service which runs on the server and provides security and APIs for browsers and integrations. It also includes a custom implementation of filtered replication to allow it to support more concurrent users.CHT Watchdoghttps://docs.communityhealthtoolkit.org/core/overview/watchdog/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/watchdog/Overview CHT Watchdog is deployed on a separate server so that you can watch for, and alert on, any critical issues with the CHT Core. Read more about setting up CHT Watchdog. +Grafana Grafana is a dashboard visualization and alerting software. It is open source and an industry standard for this task. There is an free repository of pre-existing dashboards which greatly reduce the time to create new dashboards and alerts.CHT App Configurerhttps://docs.communityhealthtoolkit.org/core/overview/cht-conf/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/cht-conf/Installation Read more about setting up CHT Conf. +Currently Supported The different items that are supported by CHT Conf include: +Settings Compile app settings from: tasks rules schedules contact-summary purge App settings can also be defined in a more modular way by having the following files in app_settings folder: base_settings.json forms.json schedules.json Backup app settings from server Upload app settings to server Upload resources to server Upload custom translations to the server Upload privacy policies to server Upload branding to server Upload partners to server Forms Fetch from Google Drive and save locally as .CHT Sync and CHT Pipelinehttps://docs.communityhealthtoolkit.org/core/overview/cht-sync/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/cht-sync/Overview CHT Sync is an integrated solution designed to enable data synchronization between CouchDB and PostgreSQL for the purpose of analytics. It combines several technologies to achieve this synchronization and provides an efficient workflow for data processing and visualization. The synchronization occurs in real-time, ensuring that the data displayed on dashboards is up-to-date. +Read more about setting up CHT Sync. +CHT Sync uses couch2pg to replicate data from CouchDB to PostgreSQL in a real-time manner.Database schema conventionshttps://docs.communityhealthtoolkit.org/core/overview/db-schema/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/db-schema/CouchDB (and PouchDB in the browser) is a JSON-based NoSQL datastore that we use to store our data. While unlike SQL databases there is no enforced schema, code still follows conventions, and this document aims to describe the schema as defined by how our code operates. +In this document &ldquo;record&rdquo; means a JSON object that resides in CouchDB or PouchDB. +General record data structure Property Description Required by _id CouchDB&rsquo;s unique identifier of the record all records _rev CouchDB&rsquo;s revision marker all records type The general type of the document, see below all user-created* documents reported_date Numerical timestamp of when the document is first created all user-created documents User-created documents here generally means contacts and reports, but may extend further.Offline-First in the CHThttps://docs.communityhealthtoolkit.org/core/overview/offline-first/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/offline-first/Introduction CHT applications are designed to be used equally well in areas with no internet connectivity, slow or unreliable internet connectivity, and good internet connectivity. Achieving reliable performance and powerful features requires diligence and strict adherence to the principles of Offline-First development. +In this page we&rsquo;ll cover why and how we achieve this in the CHT. +Why this is important The CHT is designed to improve healthcare in the hardest to reach communities.Installation as a Progressive Web Apphttps://docs.communityhealthtoolkit.org/core/overview/pwa/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/pwa/What is a Progressive Web App (PWA)? A PWA is a web application that can be used like a website in the browser, but the user can choose to &ldquo;install&rdquo; it. This means a shortcut is added to the home screen of the device, and when the application is run it doesn&rsquo;t have the usual browser address bar and tabs so it looks like a regular application. +The CHT Core webapp has been developed to be a PWA to give users more choice about how applications are installed.Data Flows for Analyticshttps://docs.communityhealthtoolkit.org/core/overview/data-flows-for-analytics/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/data-flows-for-analytics/In this section, we focus on how data flows through the various components of the Community Health Toolkit. The CHT is built to support the delivery of quality community health care at the last mile. The CHT is designed to work in areas with low connectivity, which means it is an Offline-First toolkit for care provision. The architectural and technology choices in the stack are mostly guided by this principle, which will be evident in the discussion of the data management pipeline.Sentinel Transitionshttps://docs.communityhealthtoolkit.org/core/overview/transitions/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/transitions/A transition is javascript code that runs when a document is changed. A transition can edit the changed doc or do anything server side code can do for that matter. +Transitions are run in series, not in parallel: +For a given change, you can expect one transition to be finished before the next runs. +You can expected one change to be fully processed by all transitions before the next starts being processed.How To Manage Translationshttps://docs.communityhealthtoolkit.org/core/overview/translations/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/translations/Apps built with CHT Core are localized so that users can use them in the language of their choice. Languages supported by default are English, French, Nepali, Spanish, and Swahili. The goal of this doc is to help the community manage these and future translations. +Overview Like the rest of the code the translation files live in the GitHub repo. These translation files are properties files, which are a series of keys and their corresponding values.Roadmaphttps://docs.communityhealthtoolkit.org/core/overview/roadmap/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/roadmap/The CHT is actively developed by Medic, a non-profit organization that serves as the technical steward, alongside many partners in the CHT Community. +Improvements for the CHT are raised and discussed with the community in the Forum, and tracked in the CHT Roadmap. The roadmap is organized around Product initiatives; specific improvement areas that align with medium and long-term vision for the CHT. These initiatives are organized by the ones that are being worked on Now, those that are likely Next, and those that might be Later. \ No newline at end of file diff --git a/core/overview/offline-first/index.html b/core/overview/offline-first/index.html index 9eb66209b2..43ef761340 100644 --- a/core/overview/offline-first/index.html +++ b/core/overview/offline-first/index.html @@ -1,9 +1,9 @@ -Offline-First in the CHT | Community Health Toolkit +Offline-First in the CHT | Community Health Toolkit

    Offline-First in the CHT

    An overview of what Offline-First means, why the CHT uses it, and how to contribute code that uses it.

    Introduction

    CHT applications are designed to be used equally well in areas with no internet connectivity, slow or unreliable internet connectivity, and good internet connectivity. Achieving reliable performance and powerful features requires diligence and strict adherence to the principles of Offline-First development.

    In this page we’ll cover why and how we achieve this in the CHT.

    Why this is important

    The CHT is designed to improve healthcare in the hardest to reach communities. While some users have a strong internet connection, to be as inclusive as possible we optimise for Care Teams with connections that are intermittent, unreliable, expensive, and low bandwidth. To achieve this the CHT is designed to be offline first, which, as the name suggests, means the application never relies on an internet connection for day to day tasks.

    Caveat: a small set of use cases require a decent internet connection, but these are limited to:

    • Logging in
    • Initial download of the application and data
    • Changing your own password
    • Most administrative tasks such as creating new users
    • Data analytics over large data sets

    Requirements

    Code

    The CHT uses a Service Worker to store the code needed to start up and run which is best practice for caching web applications. The CHT Core Framework code is downloaded when the user visits the login page for the first time and completes silently in the background while the user logs in.

    The code includes JavaScript, CSS, HTML, fonts, and images. It doesn’t contain any private information so it’s safe to download and cache it without authentication. It also doesn’t contain deployment specific configuration (covered in detail below).

    The CHT Core Framework checks for updates to the code by attempting to connect to the server in the background. The user isn’t notified if this check fails or times out, so they can continue to complete workflows without interruption. When an update is found the new code is downloaded and the service worker cache is updated. The user is prompted to reload to start using the new version of the application, but this can be dismissed if they are busy with a patient or don’t want to lose their progress if they are midway through an action like creating a contact or completing a form. Either way, the update will be automatically applied the next time the application starts up.

    Data

    Once the application has started up it needs data to be useful. This data falls into two categories:

    • Configuration specific to a single deployment such as forms, task definitions, places, and hierarchies.
    • Patient data such as personal details, information about visits, reports, tasks, and messages.

    Both of these types of data are cached on the device because they are required for the user to do their job. The CHT Core Framework uses PouchDB to store the data on the device’s disk. Once cached, when the application starts up the code is executed, which reads the data, allowing the user to do their job regardless of the quality of their internet connection.

    If the user creates new patient data by registering a family or completing a task the application stores this in the phone’s cache and attempts to submit this to the server. Periodically the application checks to see if there is new data on the server that is relevant to the user and if so, it updates the cache. This process of sending and receiving data updates is called replication, and is performed without interrupting the user from their work.

    FAQs from CHT contributors

    Q: Should I rely on request failure handling?

    A: No. This only works well when the user has a strong connection, or if the user is completely offline and the request fails immediately. It’s impossible to know for sure how long the request will take, so users with poor connectivity may end up waiting forever. If this request is essential to doing their job they will be unable to move on. It is important to handle request failures gracefully, but it doesn’t make a request offline first.

    Q: Should I rely on a request timeout?

    A: No. This attempts to mitigate the problem by unblocking the user eventually. The problem is setting the timeout length correctly - if it’s too short then the request won’t succeed, but if it’s too long the user will be required to wait. Experience suggests the timeout for a simple request would have to be around a minute to allow the request to succeed for most users, but a minute is far too long to expect a user to wait. Almost all requests should have a timeout, but that is not sufficient to make a request offline first.

    Q: Should I rely on a spinner or loading bar?

    A: No. Much like the timeout solution, this is an attempt to mitigate the problem in this case by giving the user more information about the request. Regardless of the UX, blocking user interaction while waiting for a response is not offline first and therefore not appropriate for the CHT. Showing UX elements when loading is still recommended for local requests, user initiated server requests, and other potentially slow operations.

    Q: How can I check if the user is authenticated?

    A: It is not possible to know for certain if a user has an active session without getting a response from the server. The CHT caches some data to hint at whether the session is still active, for example the session expiry date. However, the only way to be certain is to connect to the server, which is not offline first. This has been implemented in the CHT by checking the status code on background requests such as replication, and instructing the user to login when necessary.

    Q: Should I rely on APIs which report device connection status?

    A: No. There are APIs available which report whether the device is online with reasonable reliability. Unfortunately these APIs don’t reliably report whether the connection is slow, whether the connection will be held until the request completes, or whether the CHT server is available. This means that even if the browser API reports that the device is online the code will still make a request that may fail or wait indefinitely for a response. These APIs can be useful but must be used in conjunction with Offline-First principles, for example, the CHT uses the “online” event listener as a prompt to attempt replication in the background.

    Q: Should I add a feature that requires a connection?

    A: Maybe. There are some things that cannot be done offline first because they either require server interaction (eg: authentication during login) or they need to access data that cannot be cached locally (eg: deployment-wide analytics). Every effort should be made to find a way to implement the feature using Offline-First principles, and trade-offs discussed with senior CHT contributors. If the feature truly needs to be online first then the UX needs to be designed so that the user understands the feature requires an internet connection.

    Further reading

    -

    Last modified 24.02.2022: Document offline first (88b26211)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/overview/pwa/index.html b/core/overview/pwa/index.html index 8f6869a950..b990b34674 100644 --- a/core/overview/pwa/index.html +++ b/core/overview/pwa/index.html @@ -1,9 +1,9 @@ -Installation as a Progressive Web App | Community Health Toolkit +Installation as a Progressive Web App | Community Health Toolkit

    Installation as a Progressive Web App

    What it means that the CHT Core web application is a Progressive Web App.

    What is a Progressive Web App (PWA)?

    A PWA is a web application that can be used like a website in the browser, but the user can choose to “install” it. This means a shortcut is added to the home screen of the device, and when the application is run it doesn’t have the usual browser address bar and tabs so it looks like a regular application.

    The CHT Core webapp has been developed to be a PWA to give users more choice about how applications are installed.

    Benefits of PWA installation

    Using a PWA avoids the need to build an Android application and publish it to the Play Store which can be cumbersome and slow. This also means there’s no need for the user to install or update the Android application as any updates will be downloaded automatically from the CHT server.

    In addition you can install PWAs on a wider range of devices, for example, desktop computers.

    Deployments can use a mixture of PWA and Android applications.

    Benefits of Android applications

    Installation of the PWA may be more difficult for users, for example they will need to visit the website and then follow the instructions to install it. This process is less well known than a usual Play Store installation.

    Certain features are only available with an Android application, including integration with other android apps, and sending SMS from within the app.

    How to distribute a CHT app as a PWA

    1. Ensure branding configuration is complete. Without this the browser won’t recognize the CHT as a PWA and installation will not be possible. Specifically the app will need a title and icon as these are used for the icon on the home screen. To set this up, follow the instructions on the branding page.
    2. In Chrome or Firefox go to the application URL, log in, and wait for the webapp to finish loading completely.
    3. Follow these instructions for PWA installation.

    Now the application can be used offline just like a regular Android application.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/overview/roadmap/index.html b/core/overview/roadmap/index.html index ebb8669d64..d778a0c27e 100644 --- a/core/overview/roadmap/index.html +++ b/core/overview/roadmap/index.html @@ -1,9 +1,9 @@ -Roadmap | Community Health Toolkit +Roadmap | Community Health Toolkit

    Roadmap

    Improvements that are being worked on, and those that are being considered next

    The CHT is actively developed by Medic, a non-profit organization that serves as the technical steward, alongside many partners in the CHT Community.

    Improvements for the CHT are raised and discussed with the community in the Forum, and tracked in the CHT Roadmap. The roadmap is organized around Product initiatives; specific improvement areas that align with medium and long-term vision for the CHT. These initiatives are organized by the ones that are being worked on Now, those that are likely Next, and those that might be Later.

    Views

    The views represent initiatives being tackled by multidisciplinary working groups that focus on different key users: Care Teams for patients, caregivers, CHWs, supervisors; Allies for those that support health systems, like program administrators and app builders; and Infrastructure for CHT hosting service providers and core developers.

    Communication

    Initiatives are communicated quarterly to the CHT Community on the CHT Forum and discussed monthly on the CHT Roundup calls. Please reach out via the Forum to discuss your ideas and thoughts on product improvements.


    CHT Core Framework > + Create project issue

    Roadmap

    Improvements that are being worked on, and those that are being considered next

    The CHT is actively developed by Medic, a non-profit organization that serves as the technical steward, alongside many partners in the CHT Community.

    Improvements for the CHT are raised and discussed with the community in the Forum, and tracked in the CHT Roadmap. The roadmap is organized around Product initiatives; specific improvement areas that align with medium and long-term vision for the CHT. These initiatives are organized by the ones that are being worked on Now, those that are likely Next, and those that might be Later.

    Views

    The views represent initiatives being tackled by multidisciplinary working groups that focus on different key users: Care Teams for patients, caregivers, CHWs, supervisors; Allies for those that support health systems, like program administrators and app builders; and Infrastructure for CHT hosting service providers and core developers.

    Communication

    Initiatives are communicated quarterly to the CHT Community on the CHT Forum and discussed monthly on the CHT Roundup calls. Please reach out via the Forum to discuss your ideas and thoughts on product improvements.


    CHT Core Framework > Releases

    Versions currently supported, dependencies, and release notes for the CHT Core Framework

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/overview/transitions/index.html b/core/overview/transitions/index.html index 256d7bea94..266217588e 100644 --- a/core/overview/transitions/index.html +++ b/core/overview/transitions/index.html @@ -1,9 +1,9 @@ -Sentinel Transitions | Community Health Toolkit +Sentinel Transitions | Community Health Toolkit

    Sentinel Transitions

    Overview of Transitions, JavaScript code that runs server-side when database documents change

    A transition is javascript code that runs when a document is changed. A + Create project issue

    Sentinel Transitions

    Overview of Transitions, JavaScript code that runs server-side when database documents change

    A transition is javascript code that runs when a document is changed. A transition can edit the changed doc or do anything server side code can do for that matter.

    Transitions are run in series, not in parallel:

    • For a given change, you can expect one transition to be finished before the next runs.

    • You can expected one change to be fully processed by all transitions before @@ -322,7 +322,8 @@ Reference > app_settings.json > .transitions

      Sentinel Transitions: functions executed when database documents change

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/overview/translations/index.html b/core/overview/translations/index.html index 1109e836aa..660524a08f 100644 --- a/core/overview/translations/index.html +++ b/core/overview/translations/index.html @@ -1,9 +1,9 @@ -How To Manage Translations | Community Health Toolkit +How To Manage Translations | Community Health Toolkit

    How To Manage Translations

    Process for managing translations in CHT Core

    Apps built with CHT Core are localized so that users can use them in the language of their choice. Languages supported by default are English, French, Nepali, Spanish, and Swahili. The goal of this doc is to help the community manage these and future translations.

    Overview

    Like the rest of the code the translation files live in the GitHub repo. These translation files are properties files, which are a series of keys and their corresponding values. The English file is used by default, and as such, it contains the entire set of keys. If any key is missing from another language file the English value is used.

    Adding new languages

    New languages must be added and configured in several places:

    • Create a new messages-xx.properties file in the api/resources/translations folder, replacing “xx” with the 2 or 3 letter language code.
    • Add the language to the LOCAL_NAME_MAP in api. Use the language code for the key, and the local name followed by the English name for the language in brackets, eg: “fr: ‘Français (French)’”.
    • Import the moment language pack in the main.ts file. If moment doesn’t provide the required language pack you may need to contribute it upstream to the moment library.

    Adding new keys

    1. First check if an appropriate key already exists in messages-en.properties. Using existing keys when possible reduces the effort required to translate the app into a new language.
    2. Create a new key and default English value.
    3. Reach out to the community to translate the value into all supported languages. The CI will block merging PRs unless all values are provided.
    4. Validate the translations are complete and correct by executing npm run lint-translations.

    Translating static text

    In angular this is done using angular-translate, and ideally using the translate directive to reduce the number of watchers, eg: <h3 translate>date.incorrect.title</h3>.

    Use the translation functions in the config module in API and Sentinel.

    Translating configurations

    Much of the app is configurable (eg: forms and schedules). Because the specifics of the configuration aren’t known during development time, these can’t be provided via messages. Instead configurers can provide a map of locale to value for each translated property. Then use the translateFrom filter to translate from the configured map using the user’s language.

    Modifying any existing translation values

    1. Update the default English value.
    2. Reach out to the community to translate the value into all supported languages.
    3. Validate the translations are complete and correct by executing npm run lint-translations.

    Removing translation keys

    Carefully verify that the translation key isn’t used. This can be challenging if keys have been concatenated or generated because then you won’t be able to find the complete string in the source code. Once this has been confirmed, then simply remove the key and value from all translation files.


    CHT Applications > + Create project issue

    How To Manage Translations

    Process for managing translations in CHT Core

    Apps built with CHT Core are localized so that users can use them in the language of their choice. Languages supported by default are English, French, Nepali, Spanish, and Swahili. The goal of this doc is to help the community manage these and future translations.

    Overview

    Like the rest of the code the translation files live in the GitHub repo. These translation files are properties files, which are a series of keys and their corresponding values. The English file is used by default, and as such, it contains the entire set of keys. If any key is missing from another language file the English value is used.

    Adding new languages

    New languages must be added and configured in several places:

    • Create a new messages-xx.properties file in the api/resources/translations folder, replacing “xx” with the 2 or 3 letter language code.
    • Add the language to the LOCAL_NAME_MAP in api. Use the language code for the key, and the local name followed by the English name for the language in brackets, eg: “fr: ‘Français (French)’”.
    • Import the moment language pack in the main.ts file. If moment doesn’t provide the required language pack you may need to contribute it upstream to the moment library.

    Adding new keys

    1. First check if an appropriate key already exists in messages-en.properties. Using existing keys when possible reduces the effort required to translate the app into a new language.
    2. Create a new key and default English value.
    3. Reach out to the community to translate the value into all supported languages. The CI will block merging PRs unless all values are provided.
    4. Validate the translations are complete and correct by executing npm run lint-translations.

    Translating static text

    In angular this is done using angular-translate, and ideally using the translate directive to reduce the number of watchers, eg: <h3 translate>date.incorrect.title</h3>.

    Use the translation functions in the config module in API and Sentinel.

    Translating configurations

    Much of the app is configurable (eg: forms and schedules). Because the specifics of the configuration aren’t known during development time, these can’t be provided via messages. Instead configurers can provide a map of locale to value for each translated property. Then use the translateFrom filter to translate from the configured map using the user’s language.

    Modifying any existing translation values

    1. Update the default English value.
    2. Reach out to the community to translate the value into all supported languages.
    3. Validate the translations are complete and correct by executing npm run lint-translations.

    Removing translation keys

    Carefully verify that the translation key isn’t used. This can be challenging if keys have been concatenated or generated because then you won’t be able to find the complete string in the source code. Once this has been confirmed, then simply remove the key and value from all translation files.


    CHT Applications > Reference > translations/

    Localization: Localized labels for CHT applications

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/overview/watchdog/index.html b/core/overview/watchdog/index.html index b5a925befa..a3ff835aae 100644 --- a/core/overview/watchdog/index.html +++ b/core/overview/watchdog/index.html @@ -1,9 +1,9 @@ -CHT Watchdog | Community Health Toolkit +CHT Watchdog | Community Health Toolkit

    CHT Watchdog

    An open source monitoring system using Grafana and Prometheus

    Overview

    CHT Watchdog is deployed on a separate server so that you can watch for, and alert on, any critical issues with the CHT Core. Read more about setting up CHT Watchdog.

    Data Flows

    Grafana

    Grafana is a dashboard visualization and alerting software. It is open source and an industry standard for this task. There is an free repository of pre-existing dashboards which greatly reduce the time to create new dashboards and alerts. It can send alerts via email, Slack, SMS and many more.

    Prometheus

    Prometheus is an open source Time Series Database (TSDB) that was developed explicitly to do detailed longitudinal monitoring. It also aggregates metrics and can automatically cull older data to save on CPU and disk space.

    JSON Exporter

    JSON Exporter is a wrapper utility to convert a JSON API to be compatible with Prometheus scrape config. This is used to convert the CHT Monitoring API’s JSON.

    Postgres Exporter

    Postgres Exporter allows Prometheus to scrape a Postgres database and at a predefined interval. The queries can be configured to ingest any relevant data needed.

    CHT Core Framework & RDBMS

    For more information on these technologies, see CHT Core overview.


    CHT Core Framework > + Create project issue

    CHT Watchdog

    An open source monitoring system using Grafana and Prometheus

    Overview

    CHT Watchdog is deployed on a separate server so that you can watch for, and alert on, any critical issues with the CHT Core. Read more about setting up CHT Watchdog.

    Data Flows

    Grafana

    Grafana is a dashboard visualization and alerting software. It is open source and an industry standard for this task. There is an free repository of pre-existing dashboards which greatly reduce the time to create new dashboards and alerts. It can send alerts via email, Slack, SMS and many more.

    Prometheus

    Prometheus is an open source Time Series Database (TSDB) that was developed explicitly to do detailed longitudinal monitoring. It also aggregates metrics and can automatically cull older data to save on CPU and disk space.

    JSON Exporter

    JSON Exporter is a wrapper utility to convert a JSON API to be compatible with Prometheus scrape config. This is used to convert the CHT Monitoring API’s JSON.

    Postgres Exporter

    Postgres Exporter allows Prometheus to scrape a Postgres database and at a predefined interval. The queries can be configured to ingest any relevant data needed.

    CHT Core Framework & RDBMS

    For more information on these technologies, see CHT Core overview.


    CHT Core Framework > Overview > CHT Core

    The different pieces of a CHT project, how they interact, and what they’re used for

    CHT Core Framework > Overview > CHT Sync

    Data synchronization tools to enable analytics

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/0.4.15-and-earlier/index.html b/core/releases/0.4.15-and-earlier/index.html index 89fc943b67..06ee87899c 100644 --- a/core/releases/0.4.15-and-earlier/index.html +++ b/core/releases/0.4.15-and-earlier/index.html @@ -1,4 +1,4 @@ -0.x release notes | Community Health Toolkit +0.x release notes | Community Health Toolkit

    0.x release notes

    0.4.15

    March 2, 2017

    • Fixed potential race condition with medic-gateway. Issue: medic-projects/issues/1243
    • Bumped libphonenumber to make phone number validation more up to date. Issue: medic-projects/issues/1005

    0.4.14

    December 16, 2016

    • Bug fix for medic-gateway sending scheduled messages. Issue: #2535

    0.4.13

    October 21, 2016

    • Option to set birthdate using days old instead of weeks. Issue: #2756
    • The week/month is off by 2 in the Reporting Rates analytics dashboard. Issue: #2781
    • Remove socket limit in medic-api. Issue: #2632

    0.4.12

    July 21, 2016

    • Fixed bug in reporting rates for weekly time unit. #2429
    • Log warnings in sentinel when ID collisions happen. #1898
    • Support integration with medic-gateway for sending and receiving SMS medic-api#69

    0.4.11

    February 4, 2016

    • Security fix for leaking auth info.

    0.4.10

    Nov 16, 2015

    0.x release notes

    0.4.15

    March 2, 2017

    • Fixed potential race condition with medic-gateway. Issue: medic-projects/issues/1243
    • Bumped libphonenumber to make phone number validation more up to date. Issue: medic-projects/issues/1005

    0.4.14

    December 16, 2016

    • Bug fix for medic-gateway sending scheduled messages. Issue: #2535

    0.4.13

    October 21, 2016

    • Option to set birthdate using days old instead of weeks. Issue: #2756
    • The week/month is off by 2 in the Reporting Rates analytics dashboard. Issue: #2781
    • Remove socket limit in medic-api. Issue: #2632

    0.4.12

    July 21, 2016

    • Fixed bug in reporting rates for weekly time unit. #2429
    • Log warnings in sentinel when ID collisions happen. #1898
    • Support integration with medic-gateway for sending and receiving SMS medic-api#69

    0.4.11

    February 4, 2016

    • Security fix for leaking auth info.

    0.4.10

    Nov 16, 2015

    • Added support for Outgoing Deny List, a comma separated list of phone numbers or strings to deny outgoing service to. #750

    • Fixed bug in records export. #1273

    • Fixed bugs in uniqueWithin validation. medic-sentinel#74

    • Added link to Help page in main menu.

    0.4.9

    Aug 26, 2015

    • Fixed bug on node 0.12 in felix-couchdb. #1145

    • Improved error handling when notifications (start/stop) configs are misconfigured. #1144

    • Fixed bug in exists validation where it fails on some unicode characters. #1147

    • Fixed Reporting Rates interface that was neglected and broken. #1030

    • Fixed bug in exporting data by date, it’s now inclusive. #1104

    0.4.8

    July 14, 2015

    • Added SMS parser fixes from dev branch:

      • parse string fields with exclamation marks
      • compact textforms format handles quotes in quotes
    • Fixed export bug when using lowercase form codes. Please re-upload your forms so they are formatted correctly. #998

    • Fixed bug where exporting yields same result with or without date @@ -437,7 +437,8 @@ Outgoing Messages Scheduled Tasks

      0.1.22

      • Major improvements in scheduled reminder support in Sentinel.
      • Support for Twilio in Sentinel.
      • Minor refactor of records screen:
        • added counters to show sent/scheduled messages in records rows.
        • displaying scheduled tasks in records expanded view
        • added RC Code column
        • combined facility data into one column
        • improved contact info column, made more prominent by moving clinic contact info and phone together and into second column.
      • Removed auto-reply messages from form submission that use sentinel/scheduler so only one response is sent on a new form submission.
      • Fixed bug: Only update records of type ‘data_record’ on records screen
      • Preserve css (expando visibility) when row is replaced in records.
      • Use serial_number instead of patient_id for ohw_registration.
      • Fixed bug in textforms parser where 01234 was parsed as 1234 and added test.
      • Fixed unit tests for ONOT and ORPT form updates.
      • Fixed/Added tests for ohw emergency report (OEMR) responses.
      • Added PNC test for ohw emergency report (OEMR).
      • Fixed ohw notifications transition to set correct muted value.
      • Fixed OHW birth report (OBIR) logic and added tests.

      0.1.21

      • Fixed bug: regression from error handling refactor where assigning a record a new facility did not clear facility_not_found errors.
      • Fixed bug: months based reporting valid value was not being passed through view.
      • Fixed regression: returning form_invalid response when form_invalid_custom error is found.
      • Fixed NYAA form custom validation.
      • Added ability to do bulk deletes and facility updates.
      • Fixed bug where record is created as valid if callback request is not completed due to disconnection or phone running out of battery. Records are initialized as invalid.

      0.1.20

      • HTTP callbacks refactor, now records are created on first HTTP POST.
      • Tests refactor, replaced some redundant/large tests with more specific ones.
      • Error handling refactor, now clients should not get system messages.
      • Updated JSON forms to include Carlos Slim test forms.
      • SMSSync 2.0 support: parsing of new timestamp format.
      • Bundled SMSSync 2.0.1-hcb (http-callbacks) build.
      • ’null’ form key in filters to find records without a form definition.

      Upgrade Notes

      • If you upgrade the gateway without upgrading Kujua the timestamps from the messages will not get parsed. So messages will get a timestamp assigned to them by Kujua, which reflect the time the record was created in Kujua rather than the actual time the report/message was sent.

      0.1.19

      • Fixed wifi lock bug in SMSSync #54.
      • Updated docs with new SMSSync build instructions
      • Fixed labels names and stat placement on Health Center and District reporting screens.
      • Added help panel to spreadsheet to explain key bindings.
      • Form revisions for OHW and CDC Nepal

      0.1.18

      • Fixed bug in records screen where 1 row was not getting rendered until a scroll.
      • Refactored messaging, supporting messages_task in json forms.
      • Fixed bug where non-districts showing up in districts filter.
      • Keep Kujua Reporting files in-tact so it can be activated with config.
      • Added validations, messages and ref id labels to forms on gateway testing doc.
      • Added autocomplete=off to error missing phone form in records screen.
      • Added css for pointer hover to icon-exclamation-sign.
      • Renamed use-sentinel to use_sentinel to be consistent in JSON forms.
      • Added basic unstructured message support.
      • Fixed bug in records during edit row function where reported date get munged.
      • Added spreadsheet feature so children get updated when a parent facility is changed.
      • Fixed bug where using wrong label on main analytics screen.
      • Always create records, even on empty or unstructured messages.
      • SMS response messaging refactor, added response messages to gateway testing doc.
      • Fixed hard coded URL paths in reporting tests.

      0.1.17

      • Renamed reference_field property on forms to facility_reference.
      • Allow phone number to match on any facility type not just clinics.

      0.1.16

      • Tell user to login on 403 template.
      • Added record detail to analytics screen expand div.
      • Added configuration entries for reporting rates/analytics.
      • Made labels more configurable.
      • Added Kujua Sentinel support for reminders and alerts.
      • Updated spreadsheet columns to use config for labels.
      • Added support for form_invalid_custom error codes.
      • Introduced reference_field on forms.
      • Fixed kujua reporting views to better handle undefined clinic object in facility data.
      • Make it clear in HC reporting screen that contact name/phone is undefined.
      • Use __dirname in sentinel so it can be launched from any directory.
      • Added page for reminder logs.
      • Fixed district filtering support.
      • Added lockRows option to spreadsheet.
      • Fixed bug in XML spreadsheet output where true boolean value shows as 0.
      • Added support in sentinel to update children when a facility is edited.
      • Fixed bug on delete and editRow modal window.
      • Hide all nav items except docs when logged out.
      • Removed console references and added JSON.stringify to logger call.

      0.1.15

      • Saving responses to record and displaying in records screen.
      • Added timezone offset to reported date in exports and data records screen.
      • Added datepicker to filter exports by date.
      • Added support for custom form validation functions
      • Show better error message for other codes like missing_fields
      • Fixed buttons in data records screen
      • Added Kujua Reporting package for easy switching of analytics features
      • Improved test coverage for missing fields, cleaned/fixed up tests.
      • Fixed form parsing for fields defined as sub-objects and boolean parsing.
      • Added cases for sms responses when form is undefined
      • Display tooltip on input element focus in spreadsheet
      • Added delete on facilities spreadsheet
      • Make en/xls default export format
      • Custom labels via config.js configuration doc
      • Support for records to key on any facility
      • Nepalese responses
      • VPD field updates
      • added additional form definitions

      0.1.0-pre.4

      • Parsing refactor to allow for better unstructured and textforms support, #106, #79
      • Add validation to spreadsheet with first cut of phone validation. fixes #104
      • Direct support for json-form definitions #76
      • Added textforms support for tiny labels in form definition
      • Generating example messages automatically in Gateway Testing doc
      • Fixed textforms parser for Couchbase on OSX #46
      • Using complete field keys instead of tiny labels in textforms data record creation #79
      • Better generic localized message handling #79
      • Added delete feature to spreadsheet rows #97
      • Updated install docs to include require_valid_user steps
      • Various spreadsheet fixes #103, #98
      • Better localized string support in form definitions
      • Include national office data to districts on save

      0.1.0-pre.3

      • Added Quick Install Doc
      • Renamed kujua-export to kujua-base #88
      • Added dropdown health centers and districts to spreadsheet #99, #66
      • Various facilities spreadsheet bug fixes #92, #91, #94
      • Added CouchDB security steps to install doc #88, #66
      • Fixed authorization passthrough for tasks pending #87
      • Various data records screen fixes #84, #83, #56
      • Updated gateway/SMSSync binary and added S3 link to install docs
      • Removed Export branding
      • Started on functional tests using zombie.js and vows
      • Added health centers and districts spreadsheet
      • Added facilities section and spreadsheet editor #63
      • Assert forms that pass validation gets saved #78, #75, #46
      • Fixed bug where form submission fails if it has extra fields #75
      • Fixed 24-hour time display bug
      • Added close button to login window #70
      • Fixed bug with scroll listener binding to other pages #69

      0.1.0-pre.2

      • Better textforms support including validation #46
      • Added optional required field to form defs, all field definitions are required by default.
      • Close dropdown menus when clicking anywhere on the site #53
      • Stream export data instead of collecting it in memory #57
      • Added validation step to all new form submissions #73
      • Added records filtering by form #56
      • Moved data records code to module #69

      0.1.0-pre.1

      • added facility columns to export formats #54
      • added fr translations for facility names. #54
      • The server will not send a response unless a form is recognized.
      • Filtering on Districts support
      • Added Records section
      • Fixed reported date on re-imports, so the dates are preserved based on sent timestamp in sms message.
      • Added infinite scroll for viewing records on one page instead of paging.
      • Marking empty fields with question marks.
      • Marking errors on records that have referral tasks with no recipient defined.
      • Real-time updates via changes feed
      -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/releases/2.10.0/index.html b/core/releases/2.10.0/index.html index 09ab1ad782..c9786944bb 100644 --- a/core/releases/2.10.0/index.html +++ b/core/releases/2.10.0/index.html @@ -1,5 +1,5 @@ -2.10.0 release notes | Community Health Toolkit -

    2.10.0 release notes

    Features

    • Use reference to translation keys in app_settings. Issue: #3127
    • Add date of birth to person created by SMS. Issue: #3100
    • Configure the max number of SMS in multipart SMS. Issue: #3095
    • Load messages script fails to use https. Issue: #3081
    • Cannot access all fields for contact in select2. Issue: #3069
    • Configurable contact summary cards. Issue: #3037
    • Display additional information in contact profile. Issue: #2914
    • Support additional context for hiding/showing actions. Issue: #2913
    • Update Tour. Issue: #2212

    Bug fixes

    • Targets aren’t updated when no longer relevant. Issue: #3207
    • Task issues for restricted user when a report is deleted on the server. Issue: #3189
    • When navigating to the targets tab, I see a flash of the “no analytics modules configured” message. Issue: #3177
    • Update reports when loading the tab. Issue: #3178
    • Error after submitting form. Issue: #3157
    • Deleted message persists until refresh. Issue: #3003
    • Single delete and bulk delete does not immediately remove items from LHS in Reports tab. Issue: #3001
    • Tasks list says “no tasks found” before it’s loaded. Issue: #1935
    • Labels not translated for generated report fields. Issue: #3154
    • Getting 502s after submitting task; Tasks not cleared until refresh. Issue: #3111
    • Do not know if patient ID is valid when processing Registrations/Report Actions. Issue: #3082
    • Patient contact creation should happen if a patient contact doesn’t already exist. Issue: #3115
    • Task schedules created using the reported_date of a report do not show/hide at the expected time. Issue: #3097
    • Patients reports accepted even if no person has the patient_id. Issue: #3075
    • Registrations that clear previous registrations also clear themselves. Issue: #3074
    • Ensure useful commands is on medic-os $PATH by default. Issue: #2750
    • Family Members section header shows on person’s profile. Issue: #3108
    • Uncaught exception triggers 500 response for subsequent requests. Issue: #3099
    • Broken links in app settings. Issue: #3088
    • Edit function not working for reports sent by unknown number. Issue: #3087
    • SMS reports do not show name in summary. Issue: #3084
    • Auto replies and Scheduled SMS are truncated to fit in single SMS. Issue: #3083
    • Bubble task count not showing on browser refresh. Issue: #3028
    • Scheduled messages not showing accurate date. Issue: #3012
    • SMS API sets messages to scheduled on POST. Issue: #3011
    • Scheduled messages not being sent. Issue: #3010
    • JavaRosa Parser should give a better error message when form definition on the web app is mismatched with the submitted message using medic collect. Issue: #2638
    • Contact persons don’t show up in their places. Issue: #2385
    • setup_complete is set too fast, so setup wizard is likely to be skipped. Issue: #2376
    • Submitting a family survey doesn’t clear the task. Issue: #2265
    • Ages of children showing up strangely. Issue: #2191
    • Forms and icons fail to replicate on slow connections. Issue: #2113

    UI/UX improvements

    • Clickable portion of action is smaller than item. Issue: #3104
    • “Targets” tab blank for admin users. Issue: #3029
    • Action button items get lost in RHS. Issue: #3005
    • Action button should always be left-most button in FAB. Issue: #3004
    • “Up” button at bottom of place/person pages. Issue: #2894
    • Status icon for delivered is orange instead of green. Issue: #2752
    • Display format for phone numbers. Issue: #1930

    Performance improvements

    • medic-api migration to remove couchmark. Issue: #3068
    • Extract XML forms into attachments. Issue: #3009
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.10.1/index.html b/core/releases/2.10.1/index.html index eacbdcb9c9..d17498d44a 100644 --- a/core/releases/2.10.1/index.html +++ b/core/releases/2.10.1/index.html @@ -1,5 +1,5 @@ -2.10.1 release notes | Community Health Toolkit -

    2.10.1 release notes

    Bug fixes

    • Sending a message from the Messages tab creates a message with uuid equal to database URL. Issue: #3242
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.10.2/index.html b/core/releases/2.10.2/index.html index fbbbf9a0d3..023b644509 100644 --- a/core/releases/2.10.2/index.html +++ b/core/releases/2.10.2/index.html @@ -1,5 +1,5 @@ -2.10.2 release notes | Community Health Toolkit -

    2.10.2 release notes

    Bug fixes

    • Sentinel somehow infinitely loops and continually writes to its metadata file. Issue: #3275
    • API crashes after /medic/_bulk_docs gets called. Issue: #3268
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.10.3/index.html b/core/releases/2.10.3/index.html index 5f767067a3..fd35fedea4 100644 --- a/core/releases/2.10.3/index.html +++ b/core/releases/2.10.3/index.html @@ -1,5 +1,5 @@ -2.10.3 release notes | Community Health Toolkit -

    2.10.3 release notes

    Bug fixes

    • Unicode support for storing enketo xml. Issue: #3308
    • Support negative values in xform fields better. Issue: medic/medic-projects#1624
    • Trigger enketo calc updates when option names are changed. Issue: #3281
    • New Household button missing. Issue: #3132
    • Change report form language without requiring refresh. Issue: #3174
    • Corrupted translation strings. Issue: #3305

    UI/UX improvements

    • Show report subject name on patient page. Issue: #3309
    • Translate task schedule group titles. Issue: #3283
    • Add additional supported moment locales. Issue: #3282
    • Translate contact forms. Issue: #3323
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.11.0/index.html b/core/releases/2.11.0/index.html index 4b49f22675..bd8ee52314 100644 --- a/core/releases/2.11.0/index.html +++ b/core/releases/2.11.0/index.html @@ -1,5 +1,5 @@ -2.11.0 release notes | Community Health Toolkit -

    2.11.0 release notes

    Migration notes

    • #3230 changes patient ID generation so it automatically increases the length as needed, up to 13 digits. If you are validating incoming patient_ids in Sentinel, be sure to remove or correct any length restrictions, e.g. ^[0-9]{5}$ would become ^[0-9]{5,13}$.
    • #3166 adds a new transition that adds patient_ids to every created person: generate_patient_id_on_people. Enable this transition if you want to send SMS about patients that may be created through the webapp.

    Features

    • Drop id_format app setting in favour of auto-lengthening ids. Issue: #3230
    • Support for Nepali number characters. Issue: #3192
    • Show XForm in User’s language. Issue: #3174
    • Sentinel needs to support these patient_id use cases. Issue: #3166
    • Enable users to export, even if they do not have permission to configure. Issue: #3113
    • Support required_message and required_message translations in Enketo. Issue: #3056

    Bug fixes

    • namespace-form-fields migration causing Express toString fail. Issue: #3371
    • Cannot use Collect with username/password fields. Issue: #3118
    • No permissions available for configuration on first run. Issue: #3251
    • Fix outdated npm shrinkwrap entry for enketo-core. Issue: #3352
    • Set user-agent header in Medic Collect. Issue: #3334
    • Buttons in LHS FAB disappear. Issue: #3321
    • Analytics tab Hindi text is not aligning properly. Issue: #3297
    • Relative times are not translated. Issue: #3282
    • Submitting feedback no longer works. Issue: #3273
    • Full access users cannot create users, even when they have the appropriate permissions. Issue: #3262
    • Sending a message from the Messages tab creates a message with uuid equal to database URL. Issue: #3242
    • API does not check if COUCH_NODE_NAME is set at startup. Issue: #3226
    • Support Nepali calendar in outgoing SMS. Issue: #3219
    • New reports for same time period do not replace previous ones. Issue: #3160
    • API is picky about trailing slashes for SMS endpoint. Issue: #3152
    • Couchdb’s startup.log does not include timestamps. Issue: #3131
    • Exporting feedback crashed api and it didn’t come back. Issue: #3107
    • Export server logs from webapp does not work. Issue: #3089
    • Requesting audit log makes server unresponsive. Issue: #1789
    • Unrecognized input type found when trying to reset. Issue: #1655

    UI/UX improvements

    • Contact profile fields collapse on mobile. Issue: #3306
    • Disable cancel buttons when saving. Issue: #1650

    Performance improvements

    • enketo-core package.json is included in inbox.js bundle. Issue: #3293
    • Use enketo xml cache for contact forms too. Issue: #3325
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.11.1/index.html b/core/releases/2.11.1/index.html index 3c057874a5..23ec4e41e2 100644 --- a/core/releases/2.11.1/index.html +++ b/core/releases/2.11.1/index.html @@ -1,5 +1,5 @@ -2.11.1 release notes | Community Health Toolkit -

    2.11.1 release notes

    Bug fixes

    • Cannot report via SMS about people who are registered in the web app. Issue: #3401
    • Results page CSS messed up in v2.11. Issue: #3369
    • The user needs an associated contact to create a contact. Issue: #3394
    • Error when adding Place with new person. Issue: #3420
    • Error after canceling and re-opening any contact creation form. Issue: #3448
    • namespace-form-fields migration : report bulk errors. Issue: #3371 (second part)
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.11.2/index.html b/core/releases/2.11.2/index.html index 4624b6a21f..9a57b56628 100644 --- a/core/releases/2.11.2/index.html +++ b/core/releases/2.11.2/index.html @@ -1,5 +1,5 @@ -2.11.2 release notes | Community Health Toolkit -

    2.11.2 release notes

    Performance improvements

    • Slow initial replication for users with lots of docs. Issue: #3508
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.11.3/index.html b/core/releases/2.11.3/index.html index 1819673054..dbea8e741e 100644 --- a/core/releases/2.11.3/index.html +++ b/core/releases/2.11.3/index.html @@ -1,5 +1,5 @@ -2.11.3 release notes | Community Health Toolkit -

    2.11.3 release notes

    Bug fixes

    • The namespace-form-fields migration conflicts itself. Issue: #3534
    • In the create-patient-contacts migration provide a more complete list of potential patient_name locations. Issue: #3372
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.12.0/index.html b/core/releases/2.12.0/index.html index 05ef1b0404..0dd97037b7 100644 --- a/core/releases/2.12.0/index.html +++ b/core/releases/2.12.0/index.html @@ -1,5 +1,5 @@ -2.12.0 release notes | Community Health Toolkit -

    2.12.0 release notes

    Features

    • Add sync status indicator for offline users. Issue: #3357
    • Add gateway message delivery statuses. Issue: #3073
    • Add a replication_date property to records. Issue: #2180
    • Change patient id generation to store the length of id it’s generating. Issues: #3505
    • Allow form upload through Form Configuration UI. Issue: #3433

    Bug fixes

    • On small screen, cannot re-open date filter in history tab. Issue: #3467
    • Debug section of the About screen has some weird extra text. Issue: #3463
    • Medic Gateway runs into document update conflicts while trying to upload sms status. Issue: #3443
    • Stop maintain_info_doc transition from writing sentinel metadata. Issue: #3424
    • Webapp does not supply XML forms (XForms) to Collect. Issue: #3390
    • Cannot render form in Firefox. Issue: #3354
    • False positive error uploading translations. Issue: #3350
    • Exporting server logs fails with api 500. Issue: #3209
    • AWS EC2 AMI Regression: Does not currently boot. Issue: #3173
    • Form exits on Refresh/Reload in Tasks tab. Issue: #3090
    • Facility reference code fails to match when using integers and textforms. Issue: #1058

    UI/UX improvements

    • Bullet displayed incorrectly. Issue: #3020
    • File chooser for importing translations should filter for .properties files. Issue: #3474
    • Show loading progress when app is starting. Issue: #3384
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.12.1/index.html b/core/releases/2.12.1/index.html index 93905d5e0a..35dac5e64f 100644 --- a/core/releases/2.12.1/index.html +++ b/core/releases/2.12.1/index.html @@ -1,5 +1,5 @@ -2.12.1 release notes | Community Health Toolkit -

    2.12.1 release notes

    Bug fixes

    • Improved error messages for SMS endpoint. Issue: #3587
    • Allow for empty SMS message Content. Issue: #3656
    • Implement 500 item max for bulk delete. Issue: #3605

    Security

    • Fixed kanso packages that inadvertently cached credentials. Issue: #3648
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.12.2/index.html b/core/releases/2.12.2/index.html index 2494dc4ba9..d59d61fbd5 100644 --- a/core/releases/2.12.2/index.html +++ b/core/releases/2.12.2/index.html @@ -1,5 +1,5 @@ -2.12.2 release notes | Community Health Toolkit -

    2.12.2 release notes

    Bug fixes

    • Accept patient reports for patients created in app. Issue: #3740
    • Stop accept_patient_reports transition clearing messages for unrelated registrations. Issue: #3742
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.12.3/index.html b/core/releases/2.12.3/index.html index cf3b726d67..0befaeca00 100644 --- a/core/releases/2.12.3/index.html +++ b/core/releases/2.12.3/index.html @@ -1,5 +1,5 @@ -2.12.3 release notes | Community Health Toolkit -

    2.12.3 release notes

    Bug fixes

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.12.4/index.html b/core/releases/2.12.4/index.html index fb681f61eb..63823e6ac3 100644 --- a/core/releases/2.12.4/index.html +++ b/core/releases/2.12.4/index.html @@ -1,5 +1,5 @@ -2.12.4 release notes | Community Health Toolkit -

    2.12.4 release notes

    Bug fixes

    • Fix issue with /api/v1/records. Issue: #3770
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.12.5/index.html b/core/releases/2.12.5/index.html index 3248baef34..421362044a 100644 --- a/core/releases/2.12.5/index.html +++ b/core/releases/2.12.5/index.html @@ -1,5 +1,5 @@ -2.12.5 release notes | Community Health Toolkit -

    2.12.5 release notes

    Bug fixes

    • Fix bug where id generation wouldn’t automatically increase id length when it ran out of ids. Issue: #3790
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.13.0/index.html b/core/releases/2.13.0/index.html index c06cba1b42..a5b1106271 100644 --- a/core/releases/2.13.0/index.html +++ b/core/releases/2.13.0/index.html @@ -1,5 +1,5 @@ -2.13.0 release notes | Community Health Toolkit -

    2.13.0 release notes

    Migration notes

    • #2635 changes the context available to the configured contact summary script. The contact parameter no longer has information about parents. This information is now in an array called lineage. More information is available in the configuration documentation.
    • #3546 changes the implementation of the contact_summary so instead of declaring the output on the last line of the script, now you have to return the output. Usually this is as easy as adding a return on the last line, so output; becomes return output;. More information is available in the configuration documentation.

    Features

    • multi_report_alerts transition added to sentinel. See the documentation in cht-docs. Issue: #3416
    • Specify which forms can be downloaded using Collect. Issue: #3607
    • Validate sentinel transition configs at transition load time. Issue: #3585
    • Information from the contact-summary is now available as input to forms. Issue: #3413
    • Allow users to enter Bikram Sambat dates in Enketo forms on Android phones. Issue: #3513
    • Allow users to enter Bikram Sambat dates in Enketo forms in the web app. Issue: #3404
    • Registration of a person from a report/action form is now possible. Issue: #2912

    Bug fixes

    • Couch responds with 200 when a bad app_settings file is uploaded. Issue: #3674
    • Select All in bulk delete doesn’t work. Issue: #3646
    • Only serve collect-specific XML forms to collect. Issue: #3642
    • Exporting when filtered by report type exports more reports than are displayed in the web app. Issue: #3615
    • Bulk delete fails when deleting more than a few hundred records. Issue: #3605
    • Exporting reports filtered by place results in an empty xml. Issue: #3593
    • Requesting forms should respond with 4xx on client error. Issue: #3569
    • Can’t view contacts for restricted user. Issue: #3517
    • Bad error message when associated contact is not available in the local DB. Issue: #3499
    • Gardener bug on startup when module_name is undefined. Issue: #3481
    • Reports list showing when user doesn’t have proper permission. Issue: #3452
    • select2 in a repeat group does not work as expected in an Xform. Issue: #3430
    • {{patient_name}} not found when patient was created by xform. Issue: #3419
    • Search doesn’t work in Nepali or with accented characters. Issue: #3392
    • Remove nested contacts. Issue: #2635

    UI/UX improvements

    • Sync status cut off in mobile view. Issue: #3703
    • Hide Collect XForms from filter list in History tab. Issue: #3625
    • Split the form configuration page into JSON and XML tabs. Issue: #3559
    • Wrap text in tasks list. Issue: #3525
    • Task summary screen looks ugly on desktop. Issue: #3521
    • Send Report dropdown menu items are misaligned. Issue: #3469
    • Disable tasks tour for admins. Issue: #3144
    • Confirmation popup should not show on ‘Error loading form’. Issue: #3045
    • ‘May lose data’ warning displayed when form has no fields. Issue: #1601

    Performance improvements

    • Remove modules.js attachment. Issue: #3684
    • Use alternative pagination method for running batched migrations. Issue: #3553
    • The read status of documents is now stored in a user specific database to reduce unnecessary doc updates. Issue: #2418

    Security

    • Password validation so when creating or updating users the new passwords have to be at least 8 characters long and reasonably complex. Issue: #1472
    • Don’t eval() user input. Issue: #3546
    • Set Secure setting on AuthSession cookie for HTTPS pages. Issue: #3182
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.13.1/index.html b/core/releases/2.13.1/index.html index 935395d776..f31e4dc2dd 100644 --- a/core/releases/2.13.1/index.html +++ b/core/releases/2.13.1/index.html @@ -1,5 +1,5 @@ -2.13.1 release notes | Community Health Toolkit -

    2.13.1 release notes

    Bug fixes

    • Fix bug in extract-person-contacts migration introduced in 2.13.0 #4031
    • Remove the now invalid erlang migrations #4033
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.13.2/index.html b/core/releases/2.13.2/index.html index 58d40bf0ea..9ef10d389d 100644 --- a/core/releases/2.13.2/index.html +++ b/core/releases/2.13.2/index.html @@ -1,5 +1,5 @@ -2.13.2 release notes | Community Health Toolkit -

    2.13.2 release notes

    Bug fixes

    • Force outputs to recalc on form load #4111
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.13.3/index.html b/core/releases/2.13.3/index.html index 6ebb042a20..d235e85b46 100644 --- a/core/releases/2.13.3/index.html +++ b/core/releases/2.13.3/index.html @@ -1,5 +1,5 @@ -2.13.3 release notes | Community Health Toolkit -

    2.13.3 release notes

    Improvements

    • Bump libphonenumber to the latest
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.13.4/index.html b/core/releases/2.13.4/index.html index 8d31bccc46..6626b1742c 100644 --- a/core/releases/2.13.4/index.html +++ b/core/releases/2.13.4/index.html @@ -1,5 +1,5 @@ -2.13.4 release notes | Community Health Toolkit -

    2.13.4 release notes

    Improvements

    • Bump libphonenumber for Nepal Smart Telecom.
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.13.5/index.html b/core/releases/2.13.5/index.html index c78af7e508..d89f7fb292 100644 --- a/core/releases/2.13.5/index.html +++ b/core/releases/2.13.5/index.html @@ -1,5 +1,5 @@ -2.13.5 release notes | Community Health Toolkit -

    2.13.5 release notes

    Bug fixes

    • Update Notification transition crashes sentinel if the patient id is misconfigured. #4121
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.13.6/index.html b/core/releases/2.13.6/index.html index 69de0bb778..53b5c1f0bc 100644 --- a/core/releases/2.13.6/index.html +++ b/core/releases/2.13.6/index.html @@ -1,5 +1,5 @@ -2.13.6 release notes | Community Health Toolkit -

    2.13.6 release notes

    Performance improvements

    • Drastically improve performance of form loading when the patient context is used, and that context is very large (e.g. you’re including lineages). #4430
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.13.7/index.html b/core/releases/2.13.7/index.html index db1821bba6..c558dc47e5 100644 --- a/core/releases/2.13.7/index.html +++ b/core/releases/2.13.7/index.html @@ -1,5 +1,5 @@ -2.13.7 release notes | Community Health Toolkit -

    2.13.7 release notes

    Performance improvements

    • Various PouchDB performance improvements were backported from 2.14. This includes increasing the PouchDB version and removing our use of pouchdb-worker.
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.14.0/index.html b/core/releases/2.14.0/index.html index 17cde88e52..62b19ae957 100644 --- a/core/releases/2.14.0/index.html +++ b/core/releases/2.14.0/index.html @@ -1,9 +1,9 @@ -2.14.0 release notes | Community Health Toolkit +2.14.0 release notes | Community Health Toolkit

    2.14.0 release notes

    Additional release notes are available here.

    Migration notes

    • #3449: We included a feature which makes it unnecessary to use a repeat-relevant node in Enketo forms to workaround a bug which created an empty child. This node should now be removed.
    • #3629: We added more configurable text to the target widgets. Also, configuring an array of target titles is now deprecated in favor specifying a single translation key. Reconfigure your targets to specify values for translation_key, subtitle_translation_key, and percentage_count_translation_key properties. Full documentation.
    • #3818: We changed the way groups of scheduled messages are silenced when using silence_for. Previously, only the first group found to be in the silence window was silenced. Now, all groups are.
    • #4134: Review use of calculate="." in all forms. If you have any XForm with with calculate="." you will need to modify the corresponding XLSForm, reconvert, and upload. The changes to make are:
      • From type calculate to string
      • Make sure the calculation column is empty
      • Have the appearance as hidden
      • The label can be NO_LABEL to avoid warnings and bloat in the form

    Features

    • #3096: Allow users to take a photo while filling in an xform in Enketo and upload the photo with the form.
    • #3459: Add format-date-tr() to our custom xpath functions to support translations of days and months in xforms.
    • #3450: Show the logout button in the hamburger menu for android users who have the new can_log_out_on_android permission set.

    Bug fixes

    • #3944: Unread reports bubble not working with deleted docs.
    • #3563: Sentinel scheduling EDDs on a Sunday for all ANC registrations.
    • #3821: Export api doesn’t handle errors during export gracefully.
    • #4111: Enketo or-output never shows initial value.
    • #4166: People created via sentinel transitions are not replicated.
    • #4200: Sentinel nulls out parent when multiple docs generated from one form submission.
    • #4201: PNC schedule is not generated when a delivery report is submitted.

    UI/UX improvements

    • #3945: Update the icons used for contacts.
    • #3904: Make user type required in the edit user screen.
    • #3758: Title of form is misaligned in list of reports and reports detail pane.
    • #3736: Configurable profile field UI changes.
    • #3735: Reports content row improvements.
    • #3734: Tasks content row improvements.
    • #3732: Message content row improvements.
    • #3731: Update content row UI for consistency & improved readability.
    • #3720: Improve display of icons in configurable profile cards.
    • #3719: Person & place profile page UI changes.
    • #3704: Improve password strength validation error messages.
    • #3629: Update target widget cards and targets page layout.
    • #3597: Update fonts to Noto.
    • #3561: Display required fields when creating a “restricted to a place” user.
    • #2522: Percentage target values are confusing.

    Performance improvements

    • #3950: Remove stats collection in API as it’s no longer used.
    • #3913: Sentinel’s fetchHydratedDoc function should use only fetch contacts that aren’t already present in the lineage list.
    • #4174: Remove WebWorker for improved client database performance.

    Security

    • #3873: Escape output to defend against javascript injection.
    • #3239: Accessing webapp without logging in is possible.
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.14.1/index.html b/core/releases/2.14.1/index.html index 4727135f0f..bf5a0c6408 100644 --- a/core/releases/2.14.1/index.html +++ b/core/releases/2.14.1/index.html @@ -1,5 +1,5 @@ -2.14.1 release notes | Community Health Toolkit -

    2.14.1 release notes

    Performance improvements

    • #4430: Drastically improve performance of form loading when the patient context is used, and that context is very large (e.g. you’re including lineages).
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.14.2/index.html b/core/releases/2.14.2/index.html index 6f7f1b891f..60f8db0172 100644 --- a/core/releases/2.14.2/index.html +++ b/core/releases/2.14.2/index.html @@ -1,5 +1,5 @@ -2.14.2 release notes | Community Health Toolkit -

    2.14.2 release notes

    Bug fixes

    • #3099: Uncaught exception triggers 500 response for subsequent requests.
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.14.3/index.html b/core/releases/2.14.3/index.html index c97839555f..c94a8a5f98 100644 --- a/core/releases/2.14.3/index.html +++ b/core/releases/2.14.3/index.html @@ -1,5 +1,5 @@ -2.14.3 release notes | Community Health Toolkit -

    2.14.3 release notes

    Bug fixes

    • #4457: The z-score enketo widget is not usable.
    • #4460: Uncaught Exception: write after end error.
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.15.0/index.html b/core/releases/2.15.0/index.html index 127d49735b..8978167c35 100644 --- a/core/releases/2.15.0/index.html +++ b/core/releases/2.15.0/index.html @@ -1,9 +1,9 @@ -2.15.0 release notes | Community Health Toolkit +2.15.0 release notes | Community Health Toolkit

    2.15.0 release notes

    What’s New

    View ‘clinic’ Places in Places Filter

    You might have noticed that for SMS projects, CHW areas went missing from the places filter in the Reports tab. Good news! They are back.

    When we started having CHWs log into the Medic app and register families, the places filter on the reports page became crowded with thousands of families, creating a performance issue. To get around that issue, we removed 'clinic' level places from this filter drop-down so that users would only see health centers and CHW areas, no families. This meant that no one could filter by family, but that wasn’t a needed feature.

    Filter bar, place filter highlighted

    image

    However, as we started using version 2 with our SMS projects, we needed the ability to filter by CHW area. In those projects, CHW areas are the 'clinic' level places. In order to avoid further performance issues for projects that register families, we made this a configurable option.

    image

    The default is to show only the district_hospital and health_center levels in the places filter. If you only want to see those two levels, there is no need to change your config. However, if you want to see the clinic places as well, you will need to add the following anywhere in your app_settings.json file:

        "place_hierarchy_types": ["district_hospital", "health_center", "clinic"],
    + Create project issue

    2.15.0 release notes

    What’s New

    View ‘clinic’ Places in Places Filter

    You might have noticed that for SMS projects, CHW areas went missing from the places filter in the Reports tab. Good news! They are back.

    When we started having CHWs log into the Medic app and register families, the places filter on the reports page became crowded with thousands of families, creating a performance issue. To get around that issue, we removed 'clinic' level places from this filter drop-down so that users would only see health centers and CHW areas, no families. This meant that no one could filter by family, but that wasn’t a needed feature.

    Filter bar, place filter highlighted

    image

    However, as we started using version 2 with our SMS projects, we needed the ability to filter by CHW area. In those projects, CHW areas are the 'clinic' level places. In order to avoid further performance issues for projects that register families, we made this a configurable option.

    image

    The default is to show only the district_hospital and health_center levels in the places filter. If you only want to see those two levels, there is no need to change your config. However, if you want to see the clinic places as well, you will need to add the following anywhere in your app_settings.json file:

        "place_hierarchy_types": ["district_hospital", "health_center", "clinic"],
     

    DO NOT display clinic places if your project is registering families as this will cause performance issues with admin or program manager (full access) users rendering the reports page.

    [#3902]

    Just-in-Time Messages

    Just-in-time messages are available just in time for you to get those messages to that CHW who changed her phone number last week. And if she got any messages in the wrong language, we’ve got you covered.

    Previously, the phone number and outgoing message language were set at the time that a schedule was created. If a CHW changed her phone number, only new registrations would go to the new phone number. Any schedules that were ongoing would still send messages to her old phone number.

    With this new feature, any updates to phone numbers are reflected in real time for any scheduled messages that are not yet pending or sent. In addition, if there are messages in your schedule going to the nurse or a CHW supervisor and they have to change their phone number, any messages in existing schedules that are going to be sent to them in the future will go to the new numbers.

    image

    After updating Elizabeth’s phone number, you’ll see the following. Note that the auto-reply will have been sent to her old phone number because it was sent immediately after registration. If you look at the outgoing messages, you’ll see that all have been updated to use Elizabeth’s new number.

    image

    This improvement also affects outgoing message language. If the outgoing message language is changed, all future messages, including those in ongoing schedules, will be sent in the new language. So if a CHW registers a pregnancy and the outgoing messages are generated in English initially, if the project changes the outgoing language to Swahili, any remaining messages will be sent out in Swahili.

    image

    These changes will occur as soon as you reload the report(s) affected by the update. You should see the new phone number appear as the recipient of any future scheduled messages. The future scheduled messages should also be in the new language for outgoing messages, if you’ve updated that.

    In order for this feature to work for both phone number and translation updates, you must make sure your config:

    1. Uses only translation keys for ALL outgoing messages, including scheduled messages, auto-replies, and notifications. You must remove ALL message arrays and replace with translation keys.
    2. Uses the correct alias for each message recipient for all scheduled messages. We are no longer recommending that you use reporting_unit as the message recipient for message schedules. Instead, use clinic (to send to the patient’s CHW), health_center (to send to the nurse / primary contact of the health center), or district (to send to the primary contact at the district level). This will help ensure that phone numbers are updated in real time. Using reporting_unit for auto-replies and notifications is completely fine as these go out immediately after a report is received. The exception to this might be in cases where both nurses and CHWs are confirming visits and/or deliveries via SMS and you want to notify the CHW.

    More on this below.

    Your config will look something like this (**See the Standard app_settings.json file for a full example):

    {
           "name": "ANC Reminders LMP",
           "translation_key": "schedule.anc_lmp",
    @@ -336,7 +336,8 @@
       ]
     }
     

    The Sentinel transition simply adds a date of death of the person to their profile doc. Currently, it will be the reported_date of the death confirmation form. This is not currently customizable, but you can choose to display the date the CHW entered as the date of death on the person’s profile. We are planning to improve this in 2.16.0 by allowing you to use a date_of_death field in your death confirmation form as the date that is transferred to the person’s doc. If you want to record dates of death for people but not have the profiles update, just use the transition and don’t add the death_reporting property to app_settings.

    [#3956]

    Report Linkages

    Please accept our apology… We know you loved showing off your SQL skills, but we’ve made it easier to link certain forms together in PostgreSQL with the addition of a few new fields.

    We’ve added some new fields to reports to help make it easier to write queries in analytics. You’ll need to run sentinel transitions for these to work. All of these fields can be added to SMS forms and some can be added to app forms. Here’s a list of what’s been added:

    • Any report that runs the registration transition and invokes the add_patient (or deprecated add_patient_id) trigger will create a patient doc with a source_id field referencing the report and a created_by field referencing the ID of the contact who submitted the report.
      • This makes it possible to know who created each person and links the person to the form used to register them.
    • Any report that clears a schedule either via the accept_patient_reports transition or the clear_schedule trigger in the registration transition will have the ID of the latest matching registration recorded in the registration_id field.
      • If you register a pregnancy and then submit a V form, the V form will have a registration_id which is the ID of the most recent pregnancy registration.
      • If you submit a D form for a pregnancy that was previously registered, the D form will have a registration_id which is the ID of the most recent pregnancy registration.
      • This also applies to any other schedule that is generated by registering a patient (PNC, Immunizations, etc.). Any visit form will have the ID of the most recent registration.
    • Any scheduled message that is cleared before being sent will have a cleared_by field with the report that caused the task to be cleared.
      • If you register a pregnancy and receive the first visit reminder (Please remind Janet to go to the clinic for ANC), then submit a V form, the second reminder (Did Janet attend her visit?) would be cleared. That reminder would have a cleared_by field which is the ID of the V form.
      • If you register a pregnancy and then send in a D form, all remaining reminders are cleared. Each of the reminders would have a cleared_by field which is the ID of the V form.
      • This also applied to any other scheduled messages that are cleared - we will always know which report cleared the message.

    [#3959]

    Permissions to Show/Hide Call and Message Buttons

    Call me, maybe.

    We’ve added two new permissions to our list so that you can determine whether users should see the call and message buttons when they are viewing a person’s profile. These buttons will appear by default, but you can remove a user type’s access to them, in case you want to prevent CHWs from having the call or message options, for example.

    image

    [#3657]

    Bug Fixes

    Contact Pagination Fixed for Restricted Users

    Call off the search party, your missing families have been found!

    You might have noticed that some contacts were not appearing on the people tab. Turns out we had a pagination bug. Sorry for those who couldn’t find their missing families! This is now fixed.

    [#4085]

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/releases/2.16.0/index.html b/core/releases/2.16.0/index.html index dce735264d..a7ee434c1c 100644 --- a/core/releases/2.16.0/index.html +++ b/core/releases/2.16.0/index.html @@ -1,9 +1,9 @@ -2.16.0 release notes | Community Health Toolkit +2.16.0 release notes | Community Health Toolkit

    2.16.0 release notes

    What’s New

    View date last visited for places on the people tab

    Because knowing where you’ve been helps you know where you’re going next! - Medic proverb

    In order to help CHWs achieve full coverage of every family or household they care for, we’ve added an optional feature to update the list of families or areas to display the date that family or area was last visited. You can use any patient- or family-level form or forms to update the date last visited. You may run into performance issues if you configure this to look at forms submitted very frequently. For example, we expect five forms submitted only once a month to work better than two forms submitted every day. Make sure you test!

    Screenshot

    In order to see the date last visited instead of the primary contact, make sure you give your CHWs (or whichever users need to see date last visited) the can_view_last_visited_date permission. Once that permission is enabled, you’ll be able to display the date the family was last visited in the list. Make sure you have this permission enabled in app_settings to avoid overwriting the permission when you upload your config:

    {
    + Create project issue

    2.16.0 release notes

    What’s New

    View date last visited for places on the people tab

    Because knowing where you’ve been helps you know where you’re going next! - Medic proverb

    In order to help CHWs achieve full coverage of every family or household they care for, we’ve added an optional feature to update the list of families or areas to display the date that family or area was last visited. You can use any patient- or family-level form or forms to update the date last visited. You may run into performance issues if you configure this to look at forms submitted very frequently. For example, we expect five forms submitted only once a month to work better than two forms submitted every day. Make sure you test!

    Screenshot

    In order to see the date last visited instead of the primary contact, make sure you give your CHWs (or whichever users need to see date last visited) the can_view_last_visited_date permission. Once that permission is enabled, you’ll be able to display the date the family was last visited in the list. Make sure you have this permission enabled in app_settings to avoid overwriting the permission when you upload your config:

    {
       "name": "can_view_last_visited_date",
       "roles": [
         "chw",
    @@ -322,7 +322,8 @@
       "date_field": "fields.date_of_death"
     }
     

    [#4636]

    Bug Fixes

    Updated google-libphonenumber

    Now you can change your number to avoid your ex without missing a single Medic reminder.

    We’ve updated to the latest google-libphonenumber to make sure our app will accept new phone numbers across all of the countries where we work. The current version is 3.1.8. [#4665]

    Fixed a performance issue with loading contacts

    CHWs see a lot of people every day. That’s a lot of searching! We’ve improved search performance by 30% which really adds up.

    Loading contacts is now done with an allDocs request instead of using custom views. This improves performance of searching on the people tab and on the reports tab. It’s a workaround for now as we look further into the root cause of the slowness. [#4666]

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/releases/2.16.1/index.html b/core/releases/2.16.1/index.html index 5bef6c38d8..7309777d3c 100644 --- a/core/releases/2.16.1/index.html +++ b/core/releases/2.16.1/index.html @@ -1,9 +1,9 @@ -2.16.1 release notes | Community Health Toolkit +2.16.1 release notes | Community Health Toolkit

    2.16.1 release notes

    What’s New

    Show an icon next to families or areas that are overdue for a visit

    As the ICONic Britney Spears once sang, “Show me a siiiiiign… [visit] me, baby, one more time!”

    In order to help CHWs achieve full coverage of every family or household they care for, we added a feature in 2.16 to update the list of families or areas to display the date that family or area was last visited. Now, we’ve added an icon next to families that were visited more than a month ago to help CHWs quickly spot whom they might visit. Currently, the time frame of “more than a month ago” is not configurable. [#4747]

    Screenshot

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.17.0/index.html b/core/releases/2.17.0/index.html index b124b3fa7f..5a5ccc29b2 100644 --- a/core/releases/2.17.0/index.html +++ b/core/releases/2.17.0/index.html @@ -1,4 +1,4 @@ -2.17.0 release notes | Community Health Toolkit +2.17.0 release notes | Community Health Toolkit

    2.17.0 release notes

    What’s New

    Show pictures in the report view (History tab)

    Your selfies are safe with us (and now visible in the Reports tab).

    Photos uploaded by CHWs are now visible in the Reports view, or History tab. This is currently being used in the mRDT workflow by supervisors to confirm that CHWs read the mRDT test results correctly. [#4742]

    Screenshot

    Improve styling of mRDT Enketo widget

    “OK, but make it pretty.” Fine, here you go: ✨ UI fairy dust ✨

    We’ve improved styling by:

    • Moving the “Take Photo” button **above ** where the photo is;
    • Adding a space between the photo and the “Take Photo” button;

    Screenshot

    [#4745]

    Include mRDT in an android release

    We made changes in the Android container to make mRDT-related features available. [#4744]

    Include mRDT in a webapp release

    We included mRDT-related features in a webapp release. [#4743]

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.18.0/index.html b/core/releases/2.18.0/index.html index 4e425d083c..a04346a726 100644 --- a/core/releases/2.18.0/index.html +++ b/core/releases/2.18.0/index.html @@ -1,9 +1,9 @@ -2.18.0 release notes | Community Health Toolkit +2.18.0 release notes | Community Health Toolkit

    2.18.0 release notes

    What’s New

    Make People page default sort configurable (UHC mode)

    Previously, the default sort for the People page list was alphabetical. We’ve now made the default sort configurable. For UHC mode, this would likely mean sorting by last visited date. In the future, it could include sorting by number of visits this month or another value.

    To enable default contact sorting to be based on last visited date, you need to configure it in app_settings. Please visit the following documentation in cht-docs to learn how. [#4752]

    Add display of visit counts and conditionally style them (UHC mode)

    We added visit count numbers to the right side of each family row. The visit count will display a count of the number of times that family has been visited so far within the current month. The exact definition of the calendar month is configurable. This is because some partners may follow the Western calendar literally, while other partners have different definitions of a month (ex: Muso counts from the 26th of one month until the 25th of the next month). Whatever forms have been configured to calculate “Date last visited” will be the same forms used to calculate “Visits this month.” Please note this will be a straightforward count of forms submitted and cannot be configured to only include a max number of forms in a particular time frame, such as “per day.”

    These visit counts are also available to be conditionally styled. If the partner has no specific goals, just like with Targets, the text is normal black. If the partner does have specific goals, we use red to indicate “bad”, yellow to indicate “ok” and green to indicate “good” or “goal met.” It is also possible to display an icon next to the count number. This icon is only available as an option if the partner has a goal. If the partner does not have a goal, there will never be an icon. The icon is positioned to the left of the count number on the same baseline.

    Note: We removed the red warning icon by the date last visited text that we implemented in 2.16.1. The new functionality and location of the icon as described here replaces what we did in 2.16.1. It used to be tied to date last visited. It is now tied to visits this month.

    Screenshots

    To enable the specific goal (which color codes the number of visits), you need to configure it in app_settings. Please visit the following documentation in cht-docs to learn how. [#4758]

    Improvements

    LHS list doesn’t update “out of page” items

    LiveList and Search webapp services previously did not support sorting items by fields/values that are likely to change (e.g. by last visited date). Now, when the list is multiple pages long and it’s not entirely loaded, if an existent item receives a change that would push it lower than the current number of loaded items, it will refresh and move to appropriate position in the list. [#4782).]

    There are view generation errors

    [#4612]

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.18.1/index.html b/core/releases/2.18.1/index.html index cfc43e9053..dfb8fa15ca 100644 --- a/core/releases/2.18.1/index.html +++ b/core/releases/2.18.1/index.html @@ -1,9 +1,9 @@ -2.18.1 release notes | Community Health Toolkit +2.18.1 release notes | Community Health Toolkit

    2.18.1 release notes

    What’s New

    Count two visits on the same day as one visit

    If a family is visited twice on the same day it now only counts as one visit in UHC mode. [#4897]

    Inputs group not saved when its relevance is set to false

    Form inputs are now always saved on the reports even when they are marked as not relevant to help with analytics and editing forms. [#4875]

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.6.0/index.html b/core/releases/2.6.0/index.html index 98417a68e7..0d886d888f 100644 --- a/core/releases/2.6.0/index.html +++ b/core/releases/2.6.0/index.html @@ -1,9 +1,9 @@ -2.6.0 release notes | Community Health Toolkit +2.6.0 release notes | Community Health Toolkit

    2.6.0 release notes

    This release contains breaking changes from 0.x versions. Updating from 0.x versions may result in the application no longer operating as expected.

    • The app can now be used offline and synced back to the server later.
    • Added an android app for accessing the webapp from mobile.
    • Added Tasks feature for rich event scheduling.
    • Forms can now be provided in XForm format for rich form UIs.
    • Added a configurable Target analytics module.
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.6.1/index.html b/core/releases/2.6.1/index.html index 902cf0916e..5fda398b6e 100644 --- a/core/releases/2.6.1/index.html +++ b/core/releases/2.6.1/index.html @@ -1,5 +1,5 @@ -2.6.1 release notes | Community Health Toolkit -

    2.6.1 release notes

    • User’s fullname is not showing up in /configuration/users. Issue: #2200
    • Deleted documents cause sentinel log spam. Issue: #1999
    • Disable nools for unrestricted users. Issue: medic-projects#149
    • Update libphonenumber and use strict validation. Issue: #2159 #2196
    • Contacts export response garbled. Issue: #2187
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.6.2/index.html b/core/releases/2.6.2/index.html index 4fbecac8db..cf925340ec 100644 --- a/core/releases/2.6.2/index.html +++ b/core/releases/2.6.2/index.html @@ -1,5 +1,5 @@ -2.6.2 release notes | Community Health Toolkit -

    2.6.2 release notes

    • Update PouchDB to improve replication reliability and performance. Issue: #2134 #2167
    • When editing a CHP Area, previously set values for CHP, Branch, and Supervisor do not show up. Issue: #2223
    • Dropdowns in CHP Area create and edit forms have no blank option. Issue: #2227
    • allow-new appearance in Enketo doesn’t make the “New” option appear. Issue: #2251
    • Improve performance of Enketo db-object-widget. Issue: #2161
    • Ensure roles are always available on user-settings. Issue: #2199
    • Form type filter doesn’t include all forms. Issue: #1409
    • Added APIs for creating Users, People, and Places. Issue: #2046
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.6.3/index.html b/core/releases/2.6.3/index.html index c2185db9e1..957db5913b 100644 --- a/core/releases/2.6.3/index.html +++ b/core/releases/2.6.3/index.html @@ -1,5 +1,5 @@ -2.6.3 release notes | Community Health Toolkit -

    2.6.3 release notes

    • “console not defined” error when loading page. Issue: #2277
    • Pouch doesn’t update seq unless something has changed. Issue: #2288
    • Snackbar showing all the time. Issue: #2306
    • Support external_id property on user-settings docs. Issue: #2310
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.7.0/index.html b/core/releases/2.7.0/index.html index 1d15e04924..21d5c8cdb8 100644 --- a/core/releases/2.7.0/index.html +++ b/core/releases/2.7.0/index.html @@ -1,5 +1,5 @@ -2.7.0 release notes | Community Health Toolkit -

    2.7.0 release notes

    Features

    • Bulk delete reports. Issue: #1000

    Bug fixes

    • Report list item summaries aren’t translated. Issue: #2100
    • Fix form type filter. Issue: #1409

    Performance improvements

    • Replication performance. Issue: #2286
    • Improve search performance. Issue: #2302
    • Don’t fetch form titles for each Contact report. Issue: #2300
    • Only fetch relevant data for the Users service. Issue: #2262
    • Remove clinics from the Facility filter dropdown. Issue: #2218
    • Optimize admin bandwidth concerns. Issue: #2211
    • We request facilities from the server over and over again. Issue: #2210
    • Don’t audit _local docs. Issue: #2366
    • All requests to CouchDB time out after 10 seconds. Issue: #2325
    • Long delay loading contact dropdowns. Issue: #2326
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.7.1/index.html b/core/releases/2.7.1/index.html index 6aa8690393..95b811fc0d 100644 --- a/core/releases/2.7.1/index.html +++ b/core/releases/2.7.1/index.html @@ -1,5 +1,5 @@ -2.7.1 release notes | Community Health Toolkit -

    2.7.1 release notes

    Bug fixes

    • Creating user via fails due to invalid reported_date. Issue: #2449

    Performance improvements

    • App takes minutes to load a person dropdown. Issue: #2445
    • Cannot load Configuration Users page. Issue: #2444
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.7.2/index.html b/core/releases/2.7.2/index.html index 6626ecb97b..89a86cf5a3 100644 --- a/core/releases/2.7.2/index.html +++ b/core/releases/2.7.2/index.html @@ -1,5 +1,5 @@ -2.7.2 release notes | Community Health Toolkit -

    2.7.2 release notes

    Bug fixes

    • Connection refused when trying to load app. Issue: #2476
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.7.3/index.html b/core/releases/2.7.3/index.html index 38fd76a6dc..7170f2318b 100644 --- a/core/releases/2.7.3/index.html +++ b/core/releases/2.7.3/index.html @@ -1,5 +1,5 @@ -2.7.3 release notes | Community Health Toolkit -

    2.7.3 release notes

    Bug fixes

    • Remove maxSockets limit to allow more concurrent connections. Issue: #2492
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.8.0/index.html b/core/releases/2.8.0/index.html index dbb8f781b8..707560b714 100644 --- a/core/releases/2.8.0/index.html +++ b/core/releases/2.8.0/index.html @@ -1,5 +1,5 @@ -2.8.0 release notes | Community Health Toolkit -

    2.8.0 release notes

    Features

    • Pass user’s info to rule to customize Tasks per user type or location. Issue: #2408
    • Add context to target types and goals. Issue: #2409
    • Update default translations
    • Add ageInDays and ageInMonths functions to the XML forms context utilities. Issue: #2650
    • Users can now only access an optionally configured number of hierarchy levels below their facility. Issue: #2648

    Bug fixes

    • Android back button doesn’t work as expected. Issue: #2600
    • In date filter for Reports tab, the selected dates are being offset by 1 day. Issue: #2185
    • ‘New Contact’ option does not appear without a search. Issue: #2516
    • Place contact should be a child of the place. Issue: #1710
    • Geolocation information is not included in submitted form. Issue: #2450
    • Cannot update a contact’s phone number without an error. Issue: #2420
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.8.1/index.html b/core/releases/2.8.1/index.html index 23f12a525c..ae280ce81a 100644 --- a/core/releases/2.8.1/index.html +++ b/core/releases/2.8.1/index.html @@ -1,5 +1,5 @@ -2.8.1 release notes | Community Health Toolkit -

    2.8.1 release notes

    Bug fixes

    • If initial sync fails without syncing anything subsequent syncs get no results. Issue: #2770
    • Initial sync fails if server doesn’t respond within 30 seconds. Issue: #2771
    • Targets tab is blank on first access. Issue: #2739

    Performance improvements

    • Adding a space to a contact search term performs poorly. Issue: #2769
    • Local DB grows without limit. Issue: #2434
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.8.2/index.html b/core/releases/2.8.2/index.html index a5b51b04e7..ab30a2e4ac 100644 --- a/core/releases/2.8.2/index.html +++ b/core/releases/2.8.2/index.html @@ -1,5 +1,5 @@ -2.8.2 release notes | Community Health Toolkit -

    2.8.2 release notes

    Bug fixes

    • Ensure PouchDB doesn’t mis-label TECNO phones as devices running Safari. Issue: #2797
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.8.3/index.html b/core/releases/2.8.3/index.html index 99d6c551f6..3955185268 100644 --- a/core/releases/2.8.3/index.html +++ b/core/releases/2.8.3/index.html @@ -1,5 +1,5 @@ -2.8.3 release notes | Community Health Toolkit -

    2.8.3 release notes

    Performance improvements

    • Remove traffic statistics collection. Issue: #2886
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.8.4/index.html b/core/releases/2.8.4/index.html index 160cafea16..09f45b5b33 100644 --- a/core/releases/2.8.4/index.html +++ b/core/releases/2.8.4/index.html @@ -1,5 +1,5 @@ -2.8.4 release notes | Community Health Toolkit -

    2.8.4 release notes

    Bug fixes

    • Debounce form submissions to stop duplicate submissions. Issue: #2909
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.8.5/index.html b/core/releases/2.8.5/index.html index fa4f79d604..6fd4c9bbc0 100644 --- a/core/releases/2.8.5/index.html +++ b/core/releases/2.8.5/index.html @@ -1,5 +1,5 @@ -2.8.5 release notes | Community Health Toolkit -

    2.8.5 release notes

    • No changes, only a bump in version number to trigger a new release.
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.9.0/index.html b/core/releases/2.9.0/index.html index d7bb9ef042..5ab38f642d 100644 --- a/core/releases/2.9.0/index.html +++ b/core/releases/2.9.0/index.html @@ -1,5 +1,5 @@ -2.9.0 release notes | Community Health Toolkit -

    2.9.0 release notes

    Features

    • Redesign of People tab to introduce patient centric workflows.
    • Create Task and Target based on reports using short patient_id format. Issue: #2986
    • Calculate Z-Score within app workflow form. Issue: #2915
    • Transitions do not run for XForms. Issue: #2864
    • CHWs should not be able to edit their own area. Issue: #2844
    • Allow for people-centric SMS workflows. Issue: #2700
    • Unique “add person” forms to a place. Issue: #2693
    • Store GPS failure. Issue: #2670
    • Progressive Web App. Issue: #2626
    • Remove XML parsing and replace it with JSON views. Issue: #2432
    • Lowercase all user ids. Issue: #2369
    • New person and new place buttons should add person/place to the part of the hierarchy in which they are clicked. Issue: #2335
    • Create default Edit Place forms that allow users to edit a family’s primary contact. Issue: #2333
    • Initial replication feedback. Issue: #2279
    • Make it easy to add translation keys. Issue: #1333

    UI/UX improvements

    • Show parent place after deleting a place/person. Issue: #2936
    • Clean up labels and translations. Issue: #2888
    • First load: briefly displays “No people found” on the people and places tab even if you have contacts. Issue: #2835
    • Add icons to forms. Issue: #2794
    • Forms in Submit Report menu aren’t sorted. Issue: #2760
    • Reported Date is show in ms since epoch. Issue: #2699
    • Add basic sync status to about page. Issue: #2415
    • Display ‘your place’ card upon login. Issue: #2342
    • Only show places you directly manage in LHS unless searching. Issue: #2339
    • Lock ‘Your Place’ at top of left pane. Issue: #2337
    • Remove all filters in Contacts. Issue: #2336
    • Display “disabled for admins” message in tasks and targets page. Issue: #2292

    Bug fixes

    • Rerun transitions on change if the previous run failed. Issue: #2978
    • Allow replication of JSON reports. Issue: #2979
    • On upgrade existing reports are not associated to person/place. Issue: #2970
    • Queries from ANC Analytics do not work. Issue: #2975
    • Set new permissions to the application default when updating. Issue: #2951
    • db-object fields show as editable when readonly="true()". Issue: #2910
    • Calling the doc_summaries_by_id view results in an audit record being created. Issue: #2895
    • Can’t create new person as primary contact to existing place. Issue: #2884
    • Mute button does not work. Issue: #2878
    • Cannot fully replicate dbs behind medic-api due to badly named document. Issue: #2876
    • Deregister Changes callbacks. Issue: #2870
    • Non admins can edit translations. Issue: #2868
    • Error when completing a task. Issue: #2851
    • Editing a report from a person doesn’t pre-populate the person. Issue: #2845
    • Forms appear on the History page when they shouldn’t, based on configuration. Issue: #2837
    • When I click on a report from a contact profile, I see a flash of the History tab before getting to the report view. Issue: #2834
    • When resources change all icons disappear. Issue: #2830
    • Use default revs_limit. Issue: #2787
    • ContactsContent controller modifies doc on render. Issue: #2782
    • Alerts to reporting_unit not working. Issue: #2779
    • Correct form not displayed when going to Submit Report. Issue: #2758
    • Validations and Auto-Replies not triggered for Notifications. Issue: #2755
    • Sending message from Reports tab doesn’t prefill the modal. Issue: #2748
    • Selecting a person’s Report or Task doesn’t load the actual item. Issue: #2718
    • select2 form fields are not being prepopulated. Issue: #2703
    • Exporting Feedback crashes API. Issue: #2692
    • Cannot associate a user with a place. Issue: #2683
    • Missing patient ids and CHW names. Sentinel not fully running on Strong Minds instance. Issue: #2675
    • Cannot update a contact’s phone number without an error. Issue: #2661
    • Add recipients doesn’t work. Issue: #2659
    • Sometimes changes feed is told the wrong ID for user doc. Issue: #2640
    • Submitting a report with an invalid ref id crashes sentinel. Issue: #2636
    • Always use the same pouchdb configuration. Issue: #2625
    • Branch Manager is not getting forms downloaded. Issue: #2620
    • Can crash API with call to /api/v1/users. Issue: #2602
    • TypeError on Messages tab. Issue: #2588
    • context_by_type_freetext view seems to run with no search term. Issue: #2584
    • Once viewing a stock report, clicking the area name makes area stats disappear. Issue: #2580
    • Missing option to change time unit in reporting rate analytics. Issue: #2576
    • Missing “district” selector in Reporting rates Analytics in v2.x. Issue: #2575
    • Rename “stock” widget to “reporting rates”. Issue: #2574
    • No title shown for analytics stock table. Issue: #2573
    • No loader showing when loading data on analytics stock report. Issue: #2572
    • No loader shown when loading locations on Analytics Stock widget. Issue: #2566
    • Back button is broken on analytics stock widget. Issue: #2565
    • Stock widget shows time-period selector before a place has been selected. Issue: #2564
    • Icons wrong on analytics stock widget. Issue: #2563
    • No text labels displayed in analytics stock widget. Issue: #2562
    • Analytics stock widget breaks on error. Issue: #2561
    • No loading animation is displayed when waiting for list of locations on analytics stock widget. Issue: #2559
    • Analytics page should not show menu if there is only one module. Issue: #2557
    • Cannot choose one of the analytics modules. Issue: #2556
    • Reporting Rates has changed name to Stock Monitoring. Issue: #2555
    • Analytics screen has no information on it. Issue: #2554
    • By default gateway users don’t have the can_access_gateway_api permission. Issue: #2549
    • Cannot view Targets configuration. Issue: #2548
    • Send message uses a badly performing API. Issue: #2547
    • Document conflict on starting sentinel. Issue: #2542
    • Bad error when not authed for SMS api. Issue: #2540
    • Editing your own user settings wipes out security settings. Issue: #2539
    • Cannot send messages to unknown numbers. Issue: #2536
    • medic-api doesn’t seem to correctly expose all pending messages. Issue: #2535
    • Outgoing messages are not well formatted in the report view. Issue: #2532
    • SMS from medic-gateway do not appear in Messages tab. Issue: #2530
    • Phantom SMS message response to invalid textform message. Issue: #2525
    • sentinel keeps processing the same backlog. Issue: #2521
    • Errors starting sentinel with DB name other than ‘medic’. Issue: #2513
    • You need to restart medic-api for new translations to make it through. Issue: #2511
    • API connection refused. Issue: #2476
    • Login page not translated. Issue: #2466
    • Language dropdown is empty when adding or editing a user. Issue: #2462
    • Language select modal is blank. Issue: #2459
    • Broken migrations should prevent API from starting. Issue: #2456
    • Geolocation information is not exposed in JSON. Issue: #2450
    • Creating user fails due to invalid reported_date. Issue: #2449
    • App takes minutes to load a person dropdown. Issue: #2445
    • User can’t change their own password. Issue: #2440
    • When switching between reports in History, the “No report selected” page appears momentarily. Issue: #2433
    • \u0000 cannot be converted to text. Issue: #2426
    • The tour keeps popping up on mobile. Issue: #2423
    • In select mode, clicking on a report on the LHS to check the box also marks the report as read. Issue: #2422
    • DeleteDocs modifies the given array. Issue: #2417
    • DeleteDocs fails if the parent’s contact is null. Issue: #2416
    • After clicking “delete” on the RHS of bulk delete, reports do not disappear from the LHS. Issue: #2414
    • Using “select all” when attempting to bulk delete, the number of records on the RHS doesn’t match the LHS. Issue: #2411
    • Logout option no longer works on the MM Android app. Issue: #2407
    • national-admins should be able to edit contacts. Issue: #2395
    • Initial replication gets stuck on Tecno. Issue: #2394
    • Cannot have more than one repeat group that creates people within the same form. Issue: #2393
    • Targets tab doesn’t show correct progress. Issue: #2388
    • Contacts tab takes forever to load on mobile. Issue: #2378
    • RangeError: Maximum call stack size exceeded. Issue: #2377
    • Don’t audit docs that match _local/*. Issue: #2366
    • Fix changes proxy to support heartbeat. Issue: #2363
    • .4 analytics page crashes develop API. Issue: #2352
    • Admin can’t submit report (permissions). Issue: #2351
    • DeleteDoc service breaks replication. Issue: #2331
    • Phones with poor internet connections get an error page when trying to update. Issue: #2328
    • Navigating straight to medic/_design/medic/_rewrite/#/configuration/user breaks editing. Issue: #2294
    • Cannot select contact after bad search. Issue: #2252
    • Incoming message not attributed to contact. Issue: #2230
    • Form recognized, but label in list not updated. Issue: #2215
    • Forms not showing in filter. Issue: #2214
    • Schedule not assigned to registration form. Issue: #2213
    • In date filter for Reports tab, the selected dates are being offset by 1 day. Issue: #2185
    • Exceptions when indexing (presumably) views. Issue: #2173
    • Form title disappears on page reload. Issue: #2156
    • User configuration UI doesn’t correctly load the attached contact / locale. Issue: #2116
    • Display: block in or-appearance-h2 is overriding the disabled class. Issue: #2101
    • Verify/Unverify button falls out of sync with left pane after being clicked. Issue: #1939
    • Place contact should be a child of the place. Issue: #1710
    • Default “New Person” form doesn’t allow editing the parent place. Issue: #2704

    Performance improvements

    • Make medic-audit’s view generation 8-∞ times faster. Issue: #2879
    • Deregister Changes callbacks. Issue: #2870
    • Improve free text search views. Issue: #2853
    • Admin performance on lg.app has regressed. Issue: #2744
    • Improve application performance for high-utilization CHPs. Issue: #2665
    • Create migration to remove obsolete ddocs. Issue: #2597
    • Send message uses a badly performing API. Issue: #2547
    • Consider refactoring how sentinel views are compiled. Issue: #2537
    • sentinel duplicate views. Issue: #2534
    • Remove XML parsing and replace it with JSON views. Issue: #2432
    • Increase stability by looping over changes . Issue: #2430
    • Replication since gets reset when new documents added. Issue: #2404
    • Use db view pagination where possible. Issue: #2371
    • Don’t audit docs that match _local/*. Issue: #2366
    • Create a client side ddoc. Issue: #2206
    • Store translations in a separate doc. Issue: #1706
    • Remove empty parents migration scalability. Issue: #2629
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/2.9.1/index.html b/core/releases/2.9.1/index.html index 33c1b23b3e..e8f9a9d021 100644 --- a/core/releases/2.9.1/index.html +++ b/core/releases/2.9.1/index.html @@ -1,5 +1,5 @@ -2.9.1 release notes | Community Health Toolkit -

    2.9.1 release notes

    Bug fixes

    • Added a migration to fix scheduled messages so they can be sent by medic-gateway. Issue: #3015
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/3.0.0/index.html b/core/releases/3.0.0/index.html index 339d9f6591..4909789118 100644 --- a/core/releases/3.0.0/index.html +++ b/core/releases/3.0.0/index.html @@ -1,5 +1,5 @@ -3.0.0 release notes | Community Health Toolkit -

    3.0.0 release notes

    Upgrade notes

    1. The supported versions for client and server software have been changed significantly. Make sure your software meets the requirements before upgrading to 3.0.0.
    2. The /api/v1/messages endpoint has been removed as it was no longer actively used, and contained bugs. [#3971]
    3. The ANC analytics page and the following APIs have been removed as they are no longer used. [#1002]
      • /api/active-pregnancies
      • /api/upcoming-appointments
      • /api/missed-appointments
      • /api/upcoming-due-dates
      • /api/high-risk
      • /api/total-births
      • /api/missing-delivery-reports
      • /api/delivery-location
      • /api/visits-completed
      • /api/visits-during
      • /api/monthly-registrations
      • /api/monthly-deliveries
    4. The /api/v1/export/messages, /api/v1/export/forms, and /api/v1/export/contacts endpoints have been removed in favor of /api/v2/export/messages, /api/v2/export/reports, and /api/v2/export/contacts respectively. [#1002]
    5. The /api/v1/fti endpoint has been removed due to security concerns and lack of use. [#1002]

    What’s New

    Database upgraded to the latest CouchDB

    Our stack now runs on the latest and greatest version of CouchDB (v2.2) which is a major upgrade and supports clustering for better performance on large projects and more efficient replication.

    Support running our stack on Docker

    We have implemented a containerization solution (Docker) which means our stack can run safely on a range of operating systems, and multiple deployments can run on the same instance. The end result is to better support a project self-hosting the deployment and to save money on deployment by combining some or all of our AWS instances. [#3983]

    Import and export of Settings in Admin Console

    This feature provides UI support to import and export settings. There is now third tab to the Settings page called “Backup/Restore” and includes instructions for downloading as well as uploading. [#3868]

    Uses Horticulturalist for installation and upgrades

    Previous Situation: To install and update the Medic webapp on a server we used an application called Gardener. It is a web-based tool to manage CouchDB applications. It includes a tool, called Dashboard, which lets you install Medic. You could then upgrade the Medic application using that same web-based tool.

    Problem: Although Dashboard made it easy to install and upgrade an application, it was problematic when managing many deployments. For instance, upgrading all instances required manually clicking the Upgrade button in a webpage. This process is tedious, and only worked if you were on a good internet. We learned the hard way that doing an upgrade from a spotty connection could leave your instance in a broken state. Also, you only had the choice to upgrade to the very latest version of the app. That means that if you are on 2.14.0, you could not update just to 2.14.5, the same version but with fixes for bugs. You’d have to upgrade all the way to 2.18.0.

    Solution: Horticulturalist is a new and easy way to deploy and update Medic. Horti replaces the Market, Gardener and Dashboard as our standard way to deploy and manage our software. It can be used from the Medic Mobile admin webapp to select the specific version that you want to upgrade to. You can even select versions from a different branch to help with acceptance testing of new features. It can also be used via a computer terminal using the command line interface. This makes it easy for the Site Reliability Engineering team to manage instances, making sure that projects quickly get updates to use the most stable version of Medic tools.

    [#3993 ….]

    Improvements

    Value for db-doc attribute is case sensitive

    Previously the db-doc attribute in XLSforms only accepted the lower-case “true.” It now accepts “TRUE” as well, which is what we got often got with Excel doing autocorrect. [#3973]

    Targets tab has no Loading spinner on initial load

    There is now a loading spinner on the Targets tab on initial load. [#4241]

    Person with self as parent

    It used to be possible to edit URLs to create a person with themselves as a parent. This has now been fixed. [#4487]

    Replace medic-reporter

    Medic-reporter was a standalone couchapp that we used for sending test messages without needing an SMS or gateway device. Because it was standalone, it frequently broke and was difficult to install.

    We have now reproduced the main functionality of medic-reporter in the admin app, so it’s shipped with the webapp, can be tested and maintained easily, and works. [#4516]

    Enketo summary label icons are misaligned

    Previously, icons on the summary screen were misaligned into a corner. We’ve added padding to center them vertically and horizontally on the summary bar. [#4530]

    Make sure we can’t infinitely recurse in the lineage shared library

    We put guards in place to cleanly throw errors if we detect that we’re hitting an infinite loop. We did this by putting depth guards on potentially problematic loops and throw the error if the number gets outrageously high. This will prevent app hangs and crashes. [#4604]

    Implement access logging in API

    We implemented logging of requests in API for the response status, size, time, etc. [#4622]

    Performance Fixes

    Pull sentinel data out into its own database

    We have taken data that is specific to the running of sentinel and doesn’t need to be replicated down to users out into its own database. This makes our changes feed half the size (or so). [#3423]

    Split the admin tab out as a new app

    Previously, non-admin users have to download and run the admin only code, which was a waste of bandwidth, memory, and disk space. We split it out as a separate webapp. The new app is a desktop-only, online-only single page. The new admin app preserves all the same functionality as before, and will be revisited with a UX/UI update in the near future. [#4145]

    Changes requests are unsustainably large

    Previously, requests to changes feed got very big because we submitted all known doc IDs. Now, whenever a delete comes through in the changes feed, we manually run the view code over it to determine who should see the delete which means docs ids are not included as parameters on the request. [#4172]

    Improve the filtered replication algorithm

    We made significant performance improvements to our filtered replication algorithm. [#4185]

    Write a scalability testing framework

    We wrote a scalability testing framework to determine how many users our app can support. This will help us to work out which aspects to focus on, prove improvements work as expected, and test for regressions. [#4244]

    Subject summaries are loaded one at a time

    Reduce the time it takes to load the Reports and Contacts lists by up to 50% by requesting subject summaries in a batch rather than individually. [#4669]

    And lots more…

    Over 100 individual issues have been fixed in this release - read more.

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/releases/3.1.0/index.html b/core/releases/3.1.0/index.html index 278d1ed7e2..f1f721f7f1 100644 --- a/core/releases/3.1.0/index.html +++ b/core/releases/3.1.0/index.html @@ -1,4 +1,4 @@ -3.1.0 release notes | Community Health Toolkit +3.1.0 release notes | Community Health Toolkit

    3.1.0 release notes

    Upgrade notes

    There are no breaking changes when upgrading from 3.0.x.

    What’s New

    SMS spam protection

    SMS message generation will be skipped if an identical message has been generated recently meaning our software won’t get into an infinite loop with autoresponding robots. [#4715].

    Flexible phone number validation

    Phone number validation can be configured to be strict (default), tolerant, or disabled altogether. This is useful if phone numbers are being incorrectly rejected as invalid which can happen if carriers update their number ranges. [#4127]

    Additional age formats

    JSON forms can now include age in months or years in addition to days or weeks which makes it easier to register older children and adults. [#4597]

    Security

    Improved document level permissions checking

    Private data is now better protected against unauthorized access. [#2733, #2734]

    And more…

    19 individual issues have been fixed in this release - read more.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/3.10.0/index.html b/core/releases/3.10.0/index.html index 451b37d67d..4b00e4d9d8 100644 --- a/core/releases/3.10.0/index.html +++ b/core/releases/3.10.0/index.html @@ -1,4 +1,4 @@ -3.10.0 release notes | Community Health Toolkit +3.10.0 release notes | Community Health Toolkit

    3.10.0 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    Updates to meta databases replication

    The replication of users meta databases to the conglomerate, medic-users-meta database, is no longer configurable. + Create project issue

    3.10.0 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    Updates to meta databases replication

    The replication of users meta databases to the conglomerate, medic-users-meta database, is no longer configurable. This task now runs every day, at 2am UTC, and replicates feedback and telemetry documents to medic-users-meta database.

    Android wrapper update required for new features

    Supporting remote first-time login is only fully functional while using medic-android version 0.5.0 or later.

    Sentinel no longer advances its transition processing seq in _local/sentinel-meta-data

    Instead, it advances the transition processing seq in _local/transitions-seq. Additionally, a new document _local/background-seq advances the deletion processing seq. A new migration is run when upgrading to generate the new local files.

    Supported software

    There are no required changes to the supported software matrix from 3.0.0.

    NodeCouchDBBrowsersSMS bridgeAndroidmedic-androidmedic-couch2pg
    8.11+2.1+Chrome 53+, Firefox latestmedic-gateway4.4+0.4.5+3.0+

    Supporting remote first-time login

    3.10.0 adds a new authentication mechanism that doesn’t require users to know their username or password. Users can now access the app for the first time by clicking on a link that they receive by SMS. Admins can enable or disable this feature for any user. Users with the latest version of medic-android will have the option to open the weblink directly in the CHT app instead of a browser.

    token login

    See the documentation for more information.

    Supporting linked docs

    To support the ability of sending SMS messages to people that are not primary contacts, contact documents now expose a new property, linked_docs, that can contain a collection of ids representing documents that are relevant to the respective contact. Every document listed in linked_docs will be shallowly hydrated when the contact is hydrated, thus providing a means of accessing the contents of these related documents everywhere the contact is used within the app. This feature can have a multitude of applications, from providing a way to access an arbitrary contact’s phone number when resolving an SMS recipient to linking a delivery report to a child person document (linked_docs can also be used in contact-summary).
    See the documentation for more information.

    Privacy Policies

    App builders can now configure privacy policies for every language. When a privacy policy is configured for a language, users who load the app in this language are prompted to accept the policy after logging in, and are blocked from using the app until they have accepted. Privacy policies can be configured using medic-conf or from a new App Management page.

    privacy policies

    See the documentation for more information.

    More granular replication depth configuration for offline users

    A new report_depth option was added to the replication_depth configuration. This option allows offline users to replicate and report on contacts deeper in the hierarchy without having to replicate reports about those contacts that were created by other users.
    See the documentation for more information.

    Improved geolocation data

    3.10.0 improves the way geolocation data is obtained. Previously, if the app was not able to obtain a position lock immediately, the geolocation would not be recorded. Now, the app will allow up to 30 seconds to get a position lock. Additionally, the geolocation property is now updated every time a report is edited, and all previous geolocation entries are saved in a log within the same report.
    This change also supports the updates in medic-android version 0.5.0, where the Location permission is requested at runtime rather than when starting the app for the first time.

    Improved performance of processing deleted docs

    Because processing document deletes, specifically deleting read docs, is relatively time-consuming, increasing linearly with the number of users that exist on the instance, deletion processing is now split into:

    • generating tombstones: no changes, tombstones are generated like before, within the Sentinel transition processing feed.
    • deleting read docs and info docs: done within a new Sentinel scheduled task that doesn’t block the main transitions processing feed
    Time necessary to process 500 deletes for 1000 users

    Sentinel delete performance gains

    Additionally, Sentinel scheduled tasks are no longer executed sequentially, they are now executed in parallel, keeping track of which tasks are running. Every 5 minutes, each scheduled task, that is not already running, is started.

    And more…

    Features

    • cht-core#5817: Ability to configure SMS sending to other contacts that aren’t the primary contact
    • cht-core#6339: Provide an API endpoint for fetching a contact by phone number
    • cht-core#6352: As a data entry clerk I want to access, create, edit people in a place that I can access, and report about them, but not see any of the reports created by others for those contacts.
    • cht-core#6380: Support remote first time log in
    • medic-conf#307: Add a command line flag to accept all prompts to enable scripted execution

    Improvements

    • cht-core#4889: Don’t delete user-settings when deleting users
    • cht-core#5917: Make sure we are storing all debug / output from android when trying to obtain GPS
    • cht-core#6001: Alert when outbound pushes are repeatedly failing
    • cht-core#6094: Make configurable, marking of outgoing messages as duplicates
    • cht-core#6396: Scoping: Investigate IVR options
    • cht-core#6401: Add ability to hide the action/option to create a contact
    • cht-core#6410: Ability to customize icons shown on main tabs (contact, tasks, etc)
    • cht-core#6419: Allow you to configure outbound to send the same record multiple times
    • cht-core#6481: Allow enough space to show list of forms in Reports Filter.
    • cht-core#6492: Touch area of items in the hamburger menu should be larger
    • cht-core#6516: Improve SMS reminders so they can be configured to be sent to places anywhere in the hierarchy
    • cht-core#6538: Adhere to play store privacy policy requirements update
    • cht-docs#259: CI: Add link checker to avoid 404s going live, merge CI docs into one to simplify
    • medic-conf#273: Add configurable hierarchy support to doc references in csv-to-docs

    Performance fixes

    • cht-core#6116: Improve the performance of deleting read docs
    • cht-core#6276: Use http2 to fetch all login resources at the same time

    Bug fixes

    • cht-core#6024: The outbound error response logging is needlessly verbose
    • cht-core#6121: Translations in the App Settings app aren’t recognized until you refresh
    • cht-core#6130: Reports deleted from webapp interface are saved fully hydrated
    • cht-core#6182: Support custom contact types in medic-pyxform
    • cht-core#6313: Use the browser language as the default language
    • cht-core#6370: Replication “isSensitive” logic prevents “boss” from creating any report for users directly under them in hierarchy
    • cht-core#6386: Error on report not cleared
    • cht-core#6404: User name autofilled with “medic”
    • cht-core#6433: Infinite spinners when loading tabs as admin
    • cht-core#6502: Guided tour erratic when user has no access to any tab
    • cht-core#6519: Upgrading doesn’t apply replication configuration which means users-meta is empty
    • cht-core#6562: Switching between tabs while tasks are being calculated can result in having multiple task documents with the same emission id
    • cht-core#6575: Large grey banner shown above content on mobile
    • cht-core#6578: Wrong error message shown when editing a user that replicates too many docs
    • cht-core#6582: Scheduled messages with due date of null end up being processed over and over without being updated
    • cht-core#6583: Users are forced to login one year after they last logged in
    • cht-core#6624: Document update conflicts when clearing schedules
    • horticulturalist#57: Horti doesn’t wam all views when deploying 3.9.x+
    • medic-conf#290: Tests fail locally when Bash completion is not setup
    • medic-conf#315: Error running csv-to-docs with null properties
    • medic-conf#322: Missing optional appliesIf attribute on contact summary cards causes crash
    • medic-conf#332: Uploading of multimedia is extremely limited in medic-conf

    Technical issues

    Security issues

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/releases/3.10.1/index.html b/core/releases/3.10.1/index.html index 608dcd7f95..3ac530b015 100644 --- a/core/releases/3.10.1/index.html +++ b/core/releases/3.10.1/index.html @@ -1,4 +1,4 @@ -3.10.1 release notes | Community Health Toolkit +3.10.1 release notes | Community Health Toolkit

    3.10.1 release notes

    Known issues

    Check the repository for the latest known issues.

    Supported software

    There are no required changes to the supported software matrix + Create project issue

    3.10.1 release notes

    Known issues

    Check the repository for the latest known issues.

    Supported software

    There are no required changes to the supported software matrix from 3.0.0.

    NodeCouchDBBrowsersSMS bridgeAndroidmedic-androidmedic-couch2pg
    8.11+2.1+Chrome 53+, Firefox latestmedic-gateway4.4+0.4.5+3.0+

    Bug fixes

    • cht-core#6700: For targets without idType, the winner emission is not deterministic
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/releases/3.10.2/index.html b/core/releases/3.10.2/index.html index 9064ec180b..8ee155208c 100644 --- a/core/releases/3.10.2/index.html +++ b/core/releases/3.10.2/index.html @@ -1,4 +1,4 @@ -3.10.2 release notes | Community Health Toolkit +3.10.2 release notes | Community Health Toolkit

    3.10.2 release notes

    Known issues

    Check the repository for the latest known issues.

    Supported software

    There are no required changes to the supported software matrix + Create project issue

    3.10.2 release notes

    Known issues

    Check the repository for the latest known issues.

    Supported software

    There are no required changes to the supported software matrix from 3.0.0.

    NodeCouchDBBrowsersSMS bridgeAndroidmedic-androidmedic-couch2pg
    8.11+2.1+Chrome 53+, Firefox latestmedic-gateway4.4+0.4.5+3.0+

    Bug fixes

    • cht-core#6848: Language translations not working when not supported by make-plural

    Features

    • cht-core#6849: Add bootstrap-datepicker translations for Tagalog (tl) Illonggo (hil) and Bisaya (ceb) languages
    • cht-core#6861: Add moment locales for Tagalog (tl) Illonggo (hil) and Bisaya (ceb) languages

    We recognize that it is atypical to have new features in a “bugfix” version. Due to the nature of the features and their relation to the bug being fixed we are handling the 3 changes together as a logical bugfix release.

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/releases/3.10.3/index.html b/core/releases/3.10.3/index.html index f713ab913a..bb7b1b7d59 100644 --- a/core/releases/3.10.3/index.html +++ b/core/releases/3.10.3/index.html @@ -1,4 +1,4 @@ -3.10.3 release notes | Community Health Toolkit +3.10.3 release notes | Community Health Toolkit

    3.10.3 release notes

    Known issues

    Check the repository for the latest known issues.

    Supported software

    There are no required changes to the supported software matrix + Create project issue

    3.10.3 release notes

    Known issues

    Check the repository for the latest known issues.

    Supported software

    There are no required changes to the supported software matrix from 3.0.0.

    NodeCouchDBBrowsersSMS bridgeAndroidmedic-androidmedic-couch2pg
    8.11+2.1+Chrome 53+, Firefox latestmedic-gateway4.4+0.4.5+3.0+

    Upgrade notes

    This release fixes issues around using the default hierarchy and having contact documents that have a default type (person, clinic, health_center or district_hospital) and also have a contact_type property.

    With configurable hierarchies, introduced in 3.7.0, the contact_type property became reserved to determine the type of contact. However, projects could have used this property for internal logic in their configuration code before upgrading to 3.7, and could have contacts that have this property along with a default hierarchy type.

    The complete solution for these issues requires an upgrade to this release and recompiling and redeploying app_settings with medic-conf 3.4.1 or greater.

    Breaking changes

    No breaking changes

    UI/UX changes

    No UI/UX changes

    Bug fixes

    • cht-core#6993: Patients with type: 'person', contact_type: 'other' don’t have tasks appear when viewed on the contacts tab
    • cht-core#6996: Muting transition and update clinics don’t handle contacts that use a hardcoded type and also have a contact_type property correctly
    • cht-core#7003: Recompile default and standard config after medic-conf patch release
    • medic-conf#382: Contacts with hardcoded types that also have a contact_type property don’t get the correct contact_summary fields and don’t count towards target goals
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/releases/3.10.4/index.html b/core/releases/3.10.4/index.html index d8f787adc3..69efaf2ad2 100644 --- a/core/releases/3.10.4/index.html +++ b/core/releases/3.10.4/index.html @@ -1,4 +1,4 @@ -3.10.4 release notes | Community Health Toolkit +3.10.4 release notes | Community Health Toolkit

    3.10.4 release notes

    Known issues

    Check the repository for the latest known issues.

    Breaking changes

    None.

    UI/UX changes

    None.

    Bug fixes

    • #7169: API can return 401 status codes for valid sessions under load, forcing users to be logged out
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/3.10.5/index.html b/core/releases/3.10.5/index.html index 1bbd511b5e..f6ddbd85a9 100644 --- a/core/releases/3.10.5/index.html +++ b/core/releases/3.10.5/index.html @@ -1,4 +1,4 @@ -3.10.5 release notes | Community Health Toolkit +3.10.5 release notes | Community Health Toolkit

    3.10.5 release notes

    This release adds a fix that overrides the default 2 minute timeout of Node HTTP requests. + Create project issue

    3.10.5 release notes

    This release adds a fix that overrides the default 2 minute timeout of Node HTTP requests. Client requests will no longer timeout at API level. Timeouts are still possible, but can only come from the load balancer or CouchDB.

    Known issues

    Check the repository for the latest known issues.

    Breaking changes

    None.

    UI/UX changes

    None.

    Bug fixes

    • #7183: Client requests receive a 502 statuscode after 2 minutes of idle time
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/releases/3.11.0/index.html b/core/releases/3.11.0/index.html index 9897ce3152..dfda27865d 100644 --- a/core/releases/3.11.0/index.html +++ b/core/releases/3.11.0/index.html @@ -1,9 +1,9 @@ -3.11.0 release notes | Community Health Toolkit +3.11.0 release notes | Community Health Toolkit

    3.11.0 release notes

    Known issues

    CHT Android

    • medic-android#127: Image upload forms crash the app. This has been broken for some time and is not easy to fix while supporting Android 4.4 so the resolution has been deferred until we can make this breaking change. Reach out if you require this feature in the near future.

    CHT Core

    Check the repository for the latest known issues.

    Upgrade notes

    • This upgrade can be rolled out remotely. Users will download the new version in the background and be prompted to reload the app when it’s ready. A small amount of data will be needed to download the new version.
    • Upgrading to this version does not require other applications to be upgraded. This release does not drop support for any hardware or software that works with the previous version.
    • This release modifies some CouchDB view definitions so we recommend you Stage the upgrade and wait for the views to be rebuilt before rolling it out to minimize downtime.
    • It is recommended to test the upgrade on a clone of your server first to ensure it works well for your application.

    Breaking changes

    Removal of Reporting Rates feature

    This feature wasn’t documented or tested and hasn’t been used in production for some time so it has been removed. To read more about this feature or if this breaking change affects you, read this forum post.

    UI/UX changes

    Android app

    A few UI/UX changes were made to the medic-android app. It is safe to ignore this section if you are not updating android app right now.

    Add UI for prominent disclosure when requesting permissions

    The app will now require a one time authorization from the user to enable access to location information. This was required to comply with the latest Google Play Store requirements.

    prominent disclosure

    Improve connection errors UX

    If the user does not have a connection the first time the app is opened they will now see a user friendly error message with a retry button.

    connection errors

    Improve UX of crosswalk to webview migration

    When migrating data from a Crosswalk apk to a Webview apk the user is now guided through the migration process so it can be upgraded without support. The migration was introduced in 0.6.0 for Android 10 and 11 devices to maintain compliance with Google Play Store policies. Read more about the transition to webview in this previously released issue.

    migration UX

    Supported software

    There are no required changes to the supported software matrix + Create project issue

    3.11.0 release notes

    Known issues

    CHT Android

    • medic-android#127: Image upload forms crash the app. This has been broken for some time and is not easy to fix while supporting Android 4.4 so the resolution has been deferred until we can make this breaking change. Reach out if you require this feature in the near future.

    CHT Core

    Check the repository for the latest known issues.

    Upgrade notes

    • This upgrade can be rolled out remotely. Users will download the new version in the background and be prompted to reload the app when it’s ready. A small amount of data will be needed to download the new version.
    • Upgrading to this version does not require other applications to be upgraded. This release does not drop support for any hardware or software that works with the previous version.
    • This release modifies some CouchDB view definitions so we recommend you Stage the upgrade and wait for the views to be rebuilt before rolling it out to minimize downtime.
    • It is recommended to test the upgrade on a clone of your server first to ensure it works well for your application.

    Breaking changes

    Removal of Reporting Rates feature

    This feature wasn’t documented or tested and hasn’t been used in production for some time so it has been removed. To read more about this feature or if this breaking change affects you, read this forum post.

    UI/UX changes

    Android app

    A few UI/UX changes were made to the medic-android app. It is safe to ignore this section if you are not updating android app right now.

    Add UI for prominent disclosure when requesting permissions

    The app will now require a one time authorization from the user to enable access to location information. This was required to comply with the latest Google Play Store requirements.

    prominent disclosure

    Improve connection errors UX

    If the user does not have a connection the first time the app is opened they will now see a user friendly error message with a retry button.

    connection errors

    Improve UX of crosswalk to webview migration

    When migrating data from a Crosswalk apk to a Webview apk the user is now guided through the migration process so it can be upgraded without support. The migration was introduced in 0.6.0 for Android 10 and 11 devices to maintain compliance with Google Play Store policies. Read more about the transition to webview in this previously released issue.

    migration UX

    Supported software

    There are no required changes to the supported software matrix from 3.0.0.

    NodeCouchDBBrowsersSMS bridgeAndroidmedic-androidmedic-couch2pg
    8.11+2.1+Chrome 53+, Firefox latestmedic-gateway4.4+0.4.5+3.0+

    Highlights

    Angular upgrade

    The UI framework has been upgraded from the deprecated AngularJS v1.6 to Angular v10.0 which drastically reduces the memory used on the phone. It will make development of the Core Framework easier and more reliable and also makes it easier to keep on the latest version of Angular which means we can keep up to date and on a supported version from now on.

    Angular performance

    RapidPro SMS gateway

    SMS messages can now be sent and received using a RapidPro instance. This allows for integration with a wide range of SMS aggregators for a high scale SMS deployment. Learn more in the feature documentation.

    RapidPro logo

    Improved replication performance

    API CPU usage has been significantly improved when requesting the changes feed, which is a frequently requested endpoint for smartphone applications.

    • cht-core#6577: Improve performance of subject lookups in authorization service

    Performance improvement

    Added a GitHub Action for deploying configuration

    If you wish to automate configuration deployment you can now use our GitHub Action as a building block.

    • cht-core#6758: Re-usable GitHub action for deploying configurations via medic-conf

    And more…

    Features

    Improvements

    Security issues

    Performance fixes

    Bug fixes

    • cht-core#6139: Hyperlinks to tasks don’t load the task in the url
    • cht-core#6385: Error when editing scheduled message from webapp
    • cht-core#6387: Error when selecting incoming message from deleted contact
    • cht-core#6447: Upgrade UI only considers URLS ending in “app.medicmobile.org” as production
    • cht-core#6533: Sentinel might process changes when it shouldn’t
    • cht-core#6557: API service doesn’t boot successfully if there is a label named “submission” in the xform
    • cht-core#6591: Replication of meta data does not occur when the regex doesn’t match
    • cht-core#6611: Exporting should be available to all online roles
    • cht-core#6627: Bug causing feedback “Duplicates in a repeater are not allowed”
    • cht-core#6629: Feedback documents are not created when contact-summary crashes
    • cht-core#6648: Blank screen when launching external apps from CHT Android app
    • cht-core#6663: Type error when running remove_non_admin_users script
    • cht-core#6700: For targets without idType, the winning emission is not deterministic
    • cht-core#6711: Form submissions hang in desktop browsers when location permission requests are ignored by users
    • cht-core#6811: Targets with property “passesIfGroupCount.gte” count instances with “pass: false” when aggregating
    • cht-core#6814: Document update conflicts deleting reviewed report
    • cht-core#6835: Android app version and Server URL not showing in About page
    • cht-core#6839: Race condition in rules engine / task list interaction can result in a stale tasks list
    • cht-core#6920: Users might start upwards replication from 0 after upgrade
    • cht-core#6993: Patients with type: 'person', contact_type: 'other' don’t have tasks appear when viewed on the contacts tab
    • cht-core#6996: Muting transition and update clinics don’t handle contacts that use a hardcoded type and also have a contact_type property correctly
    • cht-core#7022: In the form’s context.expression the summary parameter is always undefined
    • cht-core#7035: Handle compiled messageformat translations throwing errors with missing translation params
    • cht-core#7037: SMS recipients might not be mapped correctly when using places as subjects
    • cht-core#7041: Loading places fails when using an offline user and the place has a primary contact that is not a child
    • medic-android#136: Add UI for prominent disclosure when requesting for permissions
    • medic-android#91: Failure to upload APKs to Play Store prevents APKs being published to GitHub
    • medic-conf#164: Declarative config appliesToType is indicated as optional for fields and cards but we do require them
    • medic-conf#331: Validate xforms before uploading
    • medic-conf#345: Warn config overwrite falsely alerts about changes when uploading certain types of attachments
    • medic-conf#358: Can’t create task action with type “contact”
    • medic-conf#377: Incorrect warning when specifying hidden_fields
    • medic-conf#387: Can’t upload-app-settings on Node lower than 12
    • medic-couch2pg#42: Node 10 build fails a lot in a flakey way
    • medic-couch2pg#54: Add ability to replicate medic-users-meta database to PostgreSQL
    • medic-couch2pg#61: Update contactview_metadata to be compatible with configurable contact hierarchies
    • medic-gateway#139: Don’t mark messages as permanently failed unless they are irredeemable
    • medic-gateway#154: SMSs are stuck in PENDING state after being successfully sent

    Technical issues

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/releases/3.11.1/index.html b/core/releases/3.11.1/index.html index f322857e79..0ad5f79611 100644 --- a/core/releases/3.11.1/index.html +++ b/core/releases/3.11.1/index.html @@ -1,4 +1,4 @@ -3.11.1 release notes | Community Health Toolkit +3.11.1 release notes | Community Health Toolkit

    3.11.1 release notes

    Known issues

    Check the repository for the latest known issues.

    Breaking changes

    None.

    UI/UX changes

    None.

    Bug fixes

    • #7169: API can return 401 status codes for valid sessions under load, forcing users to be logged out
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/3.11.2/index.html b/core/releases/3.11.2/index.html index 5145031c4a..6963f06b48 100644 --- a/core/releases/3.11.2/index.html +++ b/core/releases/3.11.2/index.html @@ -1,4 +1,4 @@ -3.11.2 release notes | Community Health Toolkit +3.11.2 release notes | Community Health Toolkit

    3.11.2 release notes

    This release adds a fix that overrides the default 2 minute timeout of Node HTTP requests. + Create project issue

    3.11.2 release notes

    This release adds a fix that overrides the default 2 minute timeout of Node HTTP requests. Client requests will no longer timeout at API level. Timeouts are still possible, but can only come from the load balancer or CouchDB.

    Known issues

    Check the repository for the latest known issues.

    Breaking changes

    None.

    UI/UX changes

    None.

    Bug fixes

    • #7183: Client requests receive a 502 statuscode after 2 minutes of idle time
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/releases/3.11.3/index.html b/core/releases/3.11.3/index.html index 6541c28a64..ca2e1f1b3a 100644 --- a/core/releases/3.11.3/index.html +++ b/core/releases/3.11.3/index.html @@ -1,4 +1,4 @@ -3.11.3 release notes | Community Health Toolkit +3.11.3 release notes | Community Health Toolkit

    3.11.3 release notes

    This release adds a fix for the phone widget’s validation when editing forms.

    Known issues

    Check the repository for the latest known issues.

    Breaking changes

    None.

    UI/UX changes

    None.

    Bug fixes

    • #7261: Phone field invalid when editing a contact with a valid phone number.
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/3.12.0/index.html b/core/releases/3.12.0/index.html index 3ec42e4d26..deda2ad75c 100644 --- a/core/releases/3.12.0/index.html +++ b/core/releases/3.12.0/index.html @@ -1,9 +1,9 @@ -3.12.0 release notes | Community Health Toolkit +3.12.0 release notes | Community Health Toolkit

    3.12.0 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    • This upgrade can be rolled out remotely. Users will download the new version in the background and be prompted to reload the app when it’s ready. A small amount of data will be needed to download the new version.
    • Upgrading to this version does not require other applications to be upgraded. This release does not drop support for any hardware or software that works with the previous version.
    • This release modifies some CouchDB view definitions, so we recommend you Stage the upgrade and wait for the views to be rebuilt before rolling it out to minimize downtime.
    • It is recommended to test the upgrade on a clone of your server first to ensure it works well for your application.

    Breaking changes

    None.

    Supported software

    There are no required changes to the supported software matrix + Create project issue

    3.12.0 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    • This upgrade can be rolled out remotely. Users will download the new version in the background and be prompted to reload the app when it’s ready. A small amount of data will be needed to download the new version.
    • Upgrading to this version does not require other applications to be upgraded. This release does not drop support for any hardware or software that works with the previous version.
    • This release modifies some CouchDB view definitions, so we recommend you Stage the upgrade and wait for the views to be rebuilt before rolling it out to minimize downtime.
    • It is recommended to test the upgrade on a clone of your server first to ensure it works well for your application.

    Breaking changes

    None.

    Supported software

    There are no required changes to the supported software matrix from 3.0.0.

    NodeCouchDBBrowsersSMS bridgeAndroidmedic-androidmedic-couch2pg
    8.11+2.1+Chrome 53+, Firefox latestmedic-gateway4.4+0.4.5+3.0+

    UI/UX changes

    None.

    Highlights

    Telemetry

    The CHT-Core increased the frequency of telemetry data collection from monthly to daily, this new level of granularity is going to boost the metrics accuracy. Additionally, CHT-Core is gathering new information to support research initiatives, to help monitor and detect errors earlier and to contribute to the analysis of document replication.

    Check the telemetry documentation for more details.

    • #6300: The difference between the client datetime and the server datetime, it helps to detect user’s clock errors.
    • #6915: Access to granular telemetry data in daily frequency for post-hoc analysis, monitoring & research initiatives.
    • #6354: Access to replication information.

    Monitoring

    The Monitoring API version 1 (/api/v1/monitoring) is now deprecated, but will continue to work. The version 2 (/api/v2/monitoring) is introduced with improvements in the returned data structure and new metrics available.

    Check the Monitoring API documentation for more details.

    • #6607: The number of users that have connected to the api in a given number of days, it helps to detect when users are unable to connect to the server.
    • #6572: Outgoing messages information, it helps to determine the SMS failures rate.

    Contact summary, targets and tasks

    The contact summary, targets and tasks now have more data available and a new CHT API with useful functions.

    Check the contact summary, targets and tasks documentation for more details.

    • #6914: Ability to verify user’s permissions from contact summary, targets and tasks by using the CHT API.
    • #6919: Access to UHC “home visits” information from contact summary.

    SMS

    The CHT-Core will send a SMS when the origin phone number is not registered in the app:

    • #6650: Send an error message to users when sys.facility_not_found is generated.

    Muting

    Muting and unmuting are now performed on-device for offline users. Previously, muting and unmuting were performed only on the server which meant that offline users would not see the results of submitting mute/unmute reports until they were synced and processed by the server.

    Check the Muting documentation for more details.

    • #6737: Improve how Muting is handled on device before syncs.

    And more…

    Features

    • #6300: Add offline clock error detection to telemetry data.
    • #6607: Detect and report when users are unable to connect to the server.
    • #6650: Send an error message to users when sys.facility_not_found is generated.
    • #6690: Expose task ID to Enketo.
    • #6914: Ability to verify user’s permissions from contact summary, targets and tasks by using the CHT API.
    • #6915: Access to granular telemetry data for post-hoc analysis, monitoring & research initiatives.
    • #6919: Access to UHC “home visits” information from contact summary.

    Improvements

    • #6354: Add user telemetry for replication requests.
    • #6572: Allow for monitoring and alerting of SMS failure rate.
    • #6737: Improve how Muting is handled on device before syncs.
    • #6741: Add version of android app in the about page and in telemetry.

    Security fixes

    None.

    Performance improvements

    None.

    Bug fixes

    • #6479: Remove redirect from log out.
    • #6485: Getting contact summary cards displayed that should not on a profile.
    • #6602: Triggering a stage for a package that doesn’t exist causes the upgrade page to freeze.
    • #6626: 80% of BRAC feedback documents have cause “Possibly unhandled rejection: {“status”:404,“name”:“not_found”,“message”:“missing”,“error”:true}”.
    • #6634: Schedules depending on later library might not run when expected.
    • #6718: Default config delivery form saves incorrect baby uuid.
    • #6972: Remove ability to set user language in database.
    • #7066: Sort form filter and translation timing.
    • #7068: Remove the ability to set the user’s language via the API.
    • #7091: Can’t submit forms against muted contacts.
    • #7100: Loading the About page throws an error.
    • #7113: Monitoring sentinel backlog values are wrong.
    • #7137: Tasks fail to load when priority labels are empty.
    • #7169: API or CouchDB can return 401 status code for valid sessions under load, forcing users to be logged out.
    • #7183: Disable Node HTTP request timeout.

    Technical improvements

    • #6969: Update dependencies for 3.12.0.
    • #7084: Upgrade MomentJS in CHT-Core and resolve conflicts.
    • #7103: Flaky e2e test “Creating contacts with standard config”.
    • #7118: Flaky e2e test “Export Data V2.0”.
    • #7124: Some e2e tests are breaking consistently.
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/releases/3.12.1/index.html b/core/releases/3.12.1/index.html index 6361d76014..5b0ef2a716 100644 --- a/core/releases/3.12.1/index.html +++ b/core/releases/3.12.1/index.html @@ -1,4 +1,4 @@ -3.12.1 release notes | Community Health Toolkit +3.12.1 release notes | Community Health Toolkit

    3.12.1 release notes

    This release adds a fix for the phone widget’s validation when editing forms.

    Known issues

    Check the repository for the latest known issues.

    Breaking changes

    None.

    UI/UX changes

    None.

    Bug fixes

    • #7261: Phone field invalid when editing a contact with a valid phone number.
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/3.13.0/index.html b/core/releases/3.13.0/index.html index 262840fcef..f9ffa85655 100644 --- a/core/releases/3.13.0/index.html +++ b/core/releases/3.13.0/index.html @@ -1,4 +1,4 @@ -3.13.0 release notes | Community Health Toolkit +3.13.0 release notes | Community Health Toolkit

    3.13.0 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    Configuration for Task Due Date Display

    By default, an overdue task is simply displayed as “Due today”. This enhancement adds configuration to allow for displaying the number of days passed since the task’s due date. This configuration is modified by setting the task_days_overdue value as described in the documentation.

    If the configuration is not set, there will be no UX changes to the way that overdue tasks are displayed.

    Default displayConfigurable display
    • #7235: Add configuration to display the number of days since the due date for an overdue task

    Household Tasks Landing Page

    When a user with the can_view_tasks_group permission has completed a task for a particular household and there are additional uncompleted tasks for that household, a landing page is shown with a list of the remaining tasks. This allows the user to easily access all uncompleted tasks for the current household.

    The behavior for existing users without the permission will remain unchanged.

    • #5886: Make it easier to complete all tasks at a household in the same visit

    Highlights

    Collect form data from external Android apps

    The CHT now supports forms that collect data from generic 3rd-party Android apps. Users can launch the apps from the form and then the output from the app is captured by the CHT and stored with the report. See the CHT documentation for more information on how to use this feature.

    An example use case for this functionality is the CHT reference application for COVID-19 point-of-care testing with Rapid Diagnostic Tests (RDT). This app provisions and captures the RDT data by integrating with Dimagi’s RD-Toolkit app that guides health workers through the use of the RDT.

    This feature requires the CHT External App Launcher functionality included in v0.10.0 of cht-android.

    • #6981: Support collecting form data from external Android apps

    And more…

    Features

    • #5886: Make it easier to complete all tasks at a household in the same visit
    • #6981: Support collecting form data from external Android apps

    Improvements

    • #6545: Change /users/info api to deal with task documents
    • #7121: “Sync Now” should synchronize “meta” database when pressed
    • #7235: Add configuration to display the number of days since the due date for an overdue task
    • #7310: Add settings version to telemetry

    Security fixes

    None.

    Performance improvements

    None.

    Bug fixes

    • #6392: New contact header not translated in desktop view
    • #6660: isSensitive doesn’t stop replication of reports when the patient_id is the subject of a report
    • #6698: Clicking on the Android back button while creating a new report minifies the app and generates feedback doc
    • #6767: Clicking the targets tab twice puts infinite spinner up
    • #6984: api and webapp can disagree on whether a user has an online or offline role
    • #6988: Firefox is generating a Content Security Policy error when submitting a form
    • #6989: Trying to open another report after selecting a verification state will not load the next report
    • #7003: Recompile default and standard config after medic-conf patch release
    • #7034: Meta db sync throws an empty message error when offline, which generates a feedback doc
    • #7046: Error thrown when trying to send message to unknown sender
    • #7203: Can’t re-select message thread after deselecting it
    • #7209: Muting transition can conflict with itself
    • #7223: After search RHS is not getting cleared off
    • #7261: Phone field invalid when editing a contact with a valid phone number

    Technical improvements

    • #7128: Update logo in reference app “Medic Mobile” -> “Medic”
    • #7309: Update dependencies for 3.13
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/3.14.0/index.html b/core/releases/3.14.0/index.html index c30e9b5de7..da544a6a5d 100644 --- a/core/releases/3.14.0/index.html +++ b/core/releases/3.14.0/index.html @@ -1,4 +1,4 @@ -3.14.0 release notes | Community Health Toolkit +3.14.0 release notes | Community Health Toolkit

    3.14.0 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    New dialog showing sync status

    When an offline user manually triggers a sync via the hamburger menu, a notification dialog is opened indicating the status of the sync process.

    Sync in progress: + Create project issue

    3.14.0 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    New dialog showing sync status

    When an offline user manually triggers a sync via the hamburger menu, a notification dialog is opened indicating the status of the sync process.

    Sync in progress:

    Sync complete:

    Sync failed (with retry option):

    #5207: Show dialog when user clicks “Sync Now” in hamburger menu

    Updated icon for the Create Report button

    The icon for the Create Report button has been updated so that its purpose is more clear.

    Old IconNew Icon

    #6403: Change the Create Report button icon to better indicate what it does

    Display all dates in Bikram Sambat format when Nepali locale selected

    For years, the CHT has supported recording the answers to date questions in forms using the Bikram Sambat calendar format when the user has selected the Nepali locale. However, the date values displayed throughout the app would still be shown in the Gregorian calendar format.

    Now the application has been updated to also use the Bikram Sambat format when displaying dates in places such as the reports list.

    Old DatesNew Dates

    Additionally, a new to-bikram-sambat xPath function has been added that converts a date to a string containing the value of the date in the Bikram Sambat format.

    #7294: Use Bikram Sambat dates throughout the webapp when Nepali locale selected

    Properly display tab labels on small screens

    Previously, the labels for the main tabs in the CHT app (Messages, Tasks, People, etc) were not displayed on devices with very small screens leading to some confusion around the different tabs and their purposes.

    We have updated the tab labels to always display even on small screens. Additionally the size of the label text can scale so that the text will display properly even when the tabs are small.

    #7409: Display tab’s label in small screens

    Highlights

    Improved server-side purging

    We have made several improvements to our server-side purging functionality allowing it to perform better at scale.

    • Enhanced logging during purging via a new purgelog document saved in the medic-sentinel database after every purge.
    • If a contact has more than 20,000 records, it will be skipped and none of its records will be purged.
      • A log will appear for the skipped contact and the id of this contact will be saved in a new skipped_contacts property in the purgelog.
    • The batch size for contacts to purge will dynamically adjust based on the number of records associated with the contacts.

    #7280: Server Side Purge does not complete to the end

    Improved login experience

    Users who have experienced issues with login should note that this release fixes some bugs. Specifically:

    • #7343 Deployments using a browser on mobile shouldn’t get erroneously logged out.
    • #6338 Changes to pseudo-static assets, like images and translations, won’t cause unnecessary reloads for app users.
    • #7187 Infinite loops and empty modal boxes at login should not longer be present.

    And more…

    Features

    • #7294: Use Bikram Sambat dates throughout the webapp when Nepali locale selected

    Improvements

    • #5207: Show dialog when user clicks “Sync Now” in hamburger
    • #6403: Change the Create Report button icon to better indicate what it does
    • #6674: Provide clear error message on missing xsltproc dependency
    • #7089: Store users’ selected language in telemetry and feedback docs
    • #7260: Add more granularity to startup telemetry
    • #7409: Display tab’s label in small screens

    Security fixes

    None.

    Performance improvements

    None.

    Bug fixes

    • #6213: Deleting primary contact from a place will retain that person as a contact when editing
    • #6338: Generate service worker at runtime
    • #6670: End time stamp records same value as start time stamp
    • #6802: Report verification does not do proper minification before saving
    • #6964: Select question with appearance “minimal” not picking all the languages set on the form
    • #6986: Changing admin password via webapp doesn’t work
    • #7009: Pushing standard config with latest medic-conf fails with translation errors
    • #7021: Form with truthy expression will be displayed despite permissions
    • #7184: Partial load of contact add/edit forms on slow networks/devices
    • #7186: Changing an offline user’s password during replication will block them in loading error loop
    • #7187: Login page redirect loop when password is changed
    • #7242: Race condition occurs when logging in as offline user with slow connection
    • #7262: Can’t navigate to “new district” form when contact “person” edit is open.
    • #7280: Server Side Purge does not complete to the end
    • #7305: Blank modal when navigating to app instance with missing cookie
    • #7307: Login page is not cached correctly
    • #7320: Admin Upgrade Releases section empty when there are no betas
    • #7343: PWA on mobile logs out when browser is quit
    • #7346: Show error in targets when user is not associated with the configured place
    • #7353: Partner logos don’t show on the about page
    • #7383: Webapp relative dates are not localized after Angular 12 upgrade
    • #7419: Stopping and resuming initial replication could start local purging
    • #7438: Users can view reports in contacts detail page despite lacking permissions
    • #7463: Incorrect place filter label when selecting only one place
    • #7485: Error while submitting form with certain db-doc repeat

    Technical improvements

    • #6420: Move to ESLint 7
    • #7061: CHT hosting improvements for App Developers
    • #7083: Upgrade webapp to latest Angular
    • #7165: Release Notes for v3.8 have link to private repository (broken link for external users)
    • #7349: Update dependencies for 3.14
    • #7361: Upgrade google-libphonenumber
    • #7379: Update COVID-19 RDT reference app
    • #7413: Remove non-code files from repo
    • #7445: Fix reference to install.md that does not exist any more
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/releases/3.14.1/index.html b/core/releases/3.14.1/index.html index d149da4468..ce4f649513 100644 --- a/core/releases/3.14.1/index.html +++ b/core/releases/3.14.1/index.html @@ -1,4 +1,4 @@ -3.14.1 release notes | Community Health Toolkit +3.14.1 release notes | Community Health Toolkit

    3.14.1 release notes

    This release adds a fix for repeat groups in Enketo forms to pick the correct translation for fields.

    Known issues

    Check the repository for the latest known issues.

    Breaking changes

    None.

    UI/UX changes

    None.

    Bug fixes

    • #7515: Translations not working when using repeat groups with dynamic repeat count
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/3.14.2/index.html b/core/releases/3.14.2/index.html index 323967ffb7..ff3ba9dbdd 100644 --- a/core/releases/3.14.2/index.html +++ b/core/releases/3.14.2/index.html @@ -1,4 +1,4 @@ -3.14.2 release notes | Community Health Toolkit +3.14.2 release notes | Community Health Toolkit

    3.14.2 release notes

    This release adds a fix for repeat groups in Enketo forms using choice_filter.

    Known issues

    Check the repository for the latest known issues.

    Breaking changes

    None.

    UI/UX changes

    None.

    Bug fixes

    • #7550: Fix blank labels in forms with repeat group using choice_filter
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/3.15.0/index.html b/core/releases/3.15.0/index.html index d4b67ed065..4a09ac16a5 100644 --- a/core/releases/3.15.0/index.html +++ b/core/releases/3.15.0/index.html @@ -1,4 +1,4 @@ -3.15.0 release notes | Community Health Toolkit +3.15.0 release notes | Community Health Toolkit

    3.15.0 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    None.

    Highlights

    Support reporting of Bikram Sambat dates using JSON forms

    SMS forms can now include a date form for submitting an exact date in Bikram Sambat format. Learn how to use this feature in the documentation.

    #4613: Support reporting of dates as exact date using text forms

    A new API for adding users in bulk

    Previously there was an API for creating a single user but this feature adds an API for creating multiple users in one go. This is particularly useful when setting up a new project or expanding into a new area. Find out more about the API in the documentation.

    #7490: Add API to create many users

    Faster app loading for users on weak internet connections

    The CHT webapp is now fully Offline-First which means the app starts just as quickly whether you have fast internet, slow internet, or no internet at all. In the worst case the application startup used to take over 2 minutes, but with this fix it now takes around 1 second.

    #7492: User stuck on spinner on app load on a poor connection

    And more…

    Features

    None.

    Improvements

    • #4613: Support reporting of dates as exact date using text forms
    • #7139: Require additional permission for accessing the upgrade page and/or upgrade api endpoint
    • #7490: Add API to create many users
    • #7529: Increase Enketo’s size limit for uploading files
    • #7561: Record the form’s version when saving a report

    Security fixes

    None.

    Performance improvements

    None.

    Bug fixes

    • #6960: New users might be prompted to accept the privacy policy twice or shown the tour modal twice
    • #7072: Admin upgrade page shows all upgrades as potentially incompatible
    • #7362: Webapp isn’t a valid PWA
    • #7464: Webapp report filters are not applied to export
    • #7465: Report export doesn’t filter by “verified” correctly
    • #7492: User stuck on spinner on app load on a poor connection
    • #7505: Feedback doc created in race condition when trying to update privacy policy for new user
    • #7538: Not redirecting to login page when clicking on the browser’s back button
    • #7559: Re-enable the ability to get user’s language in Enketo forms

    Technical improvements

    • #6282: Remove the Theme page
    • #7495: Update dependencies for 3.15
    • #7507: Retry resetting user contact doc in e2e tests
    • #7533: Token login e2e test flake
    • #7557: Quick code fixes: import alias, service interface data type, remove unused parameters.
    • #7580: Flakey snackbar tests
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/3.16.0/index.html b/core/releases/3.16.0/index.html index d91c5978a1..a0ae4d30a7 100644 --- a/core/releases/3.16.0/index.html +++ b/core/releases/3.16.0/index.html @@ -1,4 +1,4 @@ -3.16.0 release notes | Community Health Toolkit +3.16.0 release notes | Community Health Toolkit

    3.16.0 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    • #6028: Gracefully handle overflowing form title text

    Highlights

    Support adding many users at once using data from a CSV file

    New CHT users (and associated places) can now be added in bulk by importing the data from a CSV file. Learn how to use this feature in the documentation.

    #7706: Add support for bulk user upload from CSV

    And more…

    Features

    None.

    Improvements

    • #7706: Add support for bulk user upload from CSV

    Security fixes

    None.

    Performance improvements

    None.

    Bug fixes

    • #6028: Gracefully handle overflowing form title text
    • #7617: Add support for localizing Admin app
    • #7654: Ignore invisible characters in Unicode for certain SMS fields

    Technical improvements

    • #7621: Update google-libphonenumber to support new Nepali numbers
    • #7629: Allow Feature Releases to be versioned in CHT Admin UI
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/3.16.1/index.html b/core/releases/3.16.1/index.html index 06ae59ba97..150bfc4368 100644 --- a/core/releases/3.16.1/index.html +++ b/core/releases/3.16.1/index.html @@ -1,4 +1,4 @@ -3.16.1 release notes | Community Health Toolkit +3.16.1 release notes | Community Health Toolkit

    3.16.1 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    None.

    Bug fixes

    • #7912: Calling /api/v1/settings/deprecated-transitions prevents any future transitions to run over new records in API
    • #7933: Error 500 when processing SMS message missing year from Bikram Sambat aggregate
    -

    Last modified 02.12.2022: 3.16.1 release notes (#886) (b738f35d)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/3.17.0/index.html b/core/releases/3.17.0/index.html index 90aa5fa867..e1e8d310e7 100644 --- a/core/releases/3.17.0/index.html +++ b/core/releases/3.17.0/index.html @@ -1,4 +1,4 @@ -3.17.0 release notes | Community Health Toolkit +3.17.0 release notes | Community Health Toolkit

    3.17.0 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    Action button labels now show for all devices

    Action button labels now show on lower resolution phones, making it easier to understand what each action button does.

    • #7721: Icon labels on action buttons are not showing at the default dp for commonly used mobile devices.

    Update to search and filters on the Contacts and Reports tabs

    Search and filters on the Contacts and Reports tabs have been updated to more closely align with Android UX and material design patterns. This is the first of several incremental UI/UX changes that will improve app learnability by providing a familiar user interface that is consistent with other Android apps. To learn more about the other planned material design UI/UX changes, check out this forum post.

    NOTE: The old version of the UI can be temporarily re-enabled by following the instructions in the documentation, but will be completely removed in a future release.

    • #7653: Report’s filter redesign.
    • #7746: Search bar redesign.

    Highlights

    Places created with bulk upload now have a primary contact

    We have improved the bulk upload feature to automatically assign a primary contact to a newly created place.

    • #7724: Places created with bulk upload don’t have a primary contact.

    Privacy policy is now public

    Android app submission now requires a URL to the apps’ privacy policy. To facilitate this process we have made the privacy policy public in the login page.

    • #7662: Expose privacy policy at a public URL.

    To improve clarity, breadcrumbs on the left-hand side list of the Messages and Reports tabs will no longer display the level the user themselves belongs to. Also, breadcrumbs have been added to the Tasks tab to make it easier to know which household the task subject belongs to.

    • #5710: Add “belongs to” breadcrumbs to tasks on the left-hand side list.
    • #5697: Update the levels displayed in breadcrumbs across the app.

    Full list

    Features

    • #7653: Report’s filter redesign.
    • #7746: Search bar redesign.

    Improvements

    • #7662: Expose privacy policy at a public URL.
    • #7724: Places created with bulk upload don’t have a primary contact.
    • #7721: Icon labels on action buttons are not showing at the default dp for commonly used mobile devices.
    • #5710: Add “belongs to” breadcrumbs to tasks on the left hand side list.
    • #5697: Update the levels displayed in breadcrumbs across the app.

    Security fixes

    None.

    Performance improvements

    None.

    Bug fixes

    • #6215: The can_edit permission doesn’t stop you from editing documents.

    Technical improvements

    None.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/3.17.1/index.html b/core/releases/3.17.1/index.html index 4f629a114c..a3e0f208e0 100644 --- a/core/releases/3.17.1/index.html +++ b/core/releases/3.17.1/index.html @@ -1,4 +1,4 @@ -3.17.1 release notes | Community Health Toolkit +3.17.1 release notes | Community Health Toolkit

    3.17.1 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    None.

    Bug fixes

    • #7912: Calling /api/v1/settings/deprecated-transitions prevents any future transitions to run over new records in API
    • #7933: Error 500 when processing SMS message missing year from Bikram Sambat aggregate
    -

    Last modified 02.12.2022: 3.17.1 release notes (#887) (dc4f1f54)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/3.17.2/index.html b/core/releases/3.17.2/index.html index d57e8b2b6b..7475c3ad5b 100644 --- a/core/releases/3.17.2/index.html +++ b/core/releases/3.17.2/index.html @@ -1,4 +1,4 @@ -3.17.2 release notes | Community Health Toolkit +3.17.2 release notes | Community Health Toolkit

    3.17.2 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    None.

    Bug fixes

    • #7670: Same patient id assigned to multiple patients in a same instance
    • #8576: Low performance in Reports tab for users with thousands of places
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/3.2.0/index.html b/core/releases/3.2.0/index.html index 31e94a1de2..08fc75920d 100644 --- a/core/releases/3.2.0/index.html +++ b/core/releases/3.2.0/index.html @@ -1,4 +1,4 @@ -3.2.0 release notes | Community Health Toolkit +3.2.0 release notes | Community Health Toolkit

    3.2.0 release notes

    Upgrade notes

    There are no breaking changes when upgrading from 3.1.x.

    Muting

    You can now mute people and places which stops any scheduled messages from being sent and updates the UI for that contact. [#4767]

    Screenshots

    For more information read the feature overview and the configuration documentation.

    UHC performance

    If you have UHC configured loading contacts is now much faster. [#4768]

    Benchmarks

    The following benchmarks were taken on a Tecno Y4 as a CHW with a representative amount of data.

    SortTask3.1.03.2.0Improvement
    Last visit dateLoad 50 contacts75s30s60%
    Last visit dateLoad 50 contacts after home_visit form submission110s60s45%
    Last visit dateLoad 150 contacts after home_visit form submission195s75s62%
    Last visit dateLoad 150 contacts after contact edit178s50s72%
    AlphabeticallyLoad 50 contacts43s30s31%
    AlphabeticallyLoad 50 contacts after home_visit form submission56s60s-7%
    AlphabeticallyLoad 150 contacts after home_visit form submission130s60s54%
    AlphabeticallyLoad 150 contacts after contact edit144s60s57%

    And more…

    Improvements

    • Update the on/off handling to mark an individual as muted [#4768]

    Bug fixes

    • Automated reply not generated when OFF/ON texfrom is sent without a patient id [#4649]
    • The update_notifications transition is not muting schedules [#3362]
    • CouchDB 2.3.0 has reduced the allowed length of GET requests [#5083]
    -

    Last modified 31.10.2022: Links directly to PDF file (71748cfb)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/3.2.1/index.html b/core/releases/3.2.1/index.html index fd7aa53553..dd05dc5006 100644 --- a/core/releases/3.2.1/index.html +++ b/core/releases/3.2.1/index.html @@ -1,9 +1,9 @@ -3.2.1 release notes | Community Health Toolkit +3.2.1 release notes | Community Health Toolkit

    3.2.1 release notes

    Bug fixes

    More reliable replication

    In earlier versions if a device’s replication connection got interrupted some documents may never be replicated to that device leaving it in an unknown state. To avoid this it is recommended that everyone upgrade to 3.2.1 or above as soon as possible. [#5235]

    Refresh dialog not shown

    When upgrading from 2.x to 3.2.0 the dialog to update isn’t presented to the user. The user is unaware the app updated and would need to do a manual refresh to get the latest app changes. [#5247]

    Performance improvements

    Unable to replicate a large number of documents

    Users with a large number of documents are unable to replicate due to the software attempting to fetch all documents in one request. This fix reverts to the replication strategy used in previous versions. [#5257]

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/3.3.0/index.html b/core/releases/3.3.0/index.html index 5652c51b14..0ecf15cd7d 100644 --- a/core/releases/3.3.0/index.html +++ b/core/releases/3.3.0/index.html @@ -1,4 +1,4 @@ -3.3.0 release notes | Community Health Toolkit +3.3.0 release notes | Community Health Toolkit

    3.3.0 release notes

    Upgrade notes

    There are no breaking changes when upgrading from 3.2.x.

    Purging

    Documents on phones can now be deleted once no longer useful. This allows for space on the device to be recovered and improves performance. Read the configuration documentation for how to add script to indicate that a document is no longer useful and is ready for purging. [#5048]

    Outgoing messages screen

    Administrators can now see all outgoing messages that are either scheduled, due, or unable to be sent. This is useful for monitoring the status of your SMS gateway and checking up on messages that have errored. [#649]

    Screenshots

    Smarter outgoing phone number validation

    For a long time it has been possible block outgoing messages being sent to a configured list of phone numbers. This is really useful for stopping messages to carriers and other automated systems that can cause messaging loops using up all your credit.

    In 3.3.0 we have expanded on this feature to allow blocking of sending to phone numbers shorter than a certain length or recipients that only contain letters. These have been enabled by default so by upgrading your system will no longer SMS short or non-numeric recipients. For more information read the configuration documentation. [#3675]

    ISO week validation

    SMS forms can now validate ISO week fields for a given year using the built in isISOWeek rule function. For more information read the configuration documentation. [#3155]

    Better memory management

    A bug in Node was causing the API service to consume more and more memory eventually causing Node to crash. [#5255]

    When viewing a report about a patient the ID now links to the patient record so you can easily access more information. [#3414]

    Screenshots

    And more…

    Improvements

    • #670: Outgoing message size is not limited
    • #3705: Disable ‘Select’ and ‘Export’ buttons when there are no reports
    • #4013: Add warning dialogue when users navigate away from a form
    • #4097: Log each valid POST /api/v1/users/{username} with field names
    • #4294: On single column pages, remove scroll within a scroll
    • #4680: Translate our startup messages for non-English speakers
    • #4895: Update place icons
    • #5224: Translations for new bootstrapper states added by purging
    • #5246: Pass user context to the purge function

    Performance fixes

    • #4644: Use asynchronous logging in node apps
    • #4834: Move medic-client/feedback view into admin ddoc
    • #5076: PouchDB replication _bulk_get requests are no longer batched

    Bug fixes

    • #3327: Date picker widget is not translated
    • #3391: Not all writes to CouchDB go through the audit layer
    • #3427: medic-api forms controller should use appropriate protocol
    • #3707: GET /api/v1/forms is broken if trailing slash
    • #3899: Can create people born in the future with standard configuration
    • #4331: Reports incorrectly filtered if filter changes before search returns
    • #4511: Timestamps incorrectly cleared when adding a message to an ANC reminder group
    • #4625: Reports page can mistakenly hide fields
    • #4710: Users without admin permissions can view the configuration area
    • #4711: Users without permission to view contacts, tasks, messages can view those pages
    • #4755: The image upload enketo widget relies on the form name matching the xml instance element
    • #4772: If during user creation associated contact is chosen, ‘Place’ field has to become required
    • #4787: Editing a report’s scheduled message is not working
    • #4788: The report details field titles should line wrap
    • #4800: Horti crashes on subsequent upgrade
    • #4802: Cannot get audit log
    • #4816: Exporting messages doesn’t work with reports with scheduled tasks
    • #4825: After searching the RHS should clear of anything you were previously looking at
    • #4829: Unable to access Configuration screen on my local instance of webapp (wrong redirect URL)
    • #4832: Date picker widget is not translated for language Bamanankan (bm)
    • #4837: Font awesome icons not loading in the administration console
    • #4899: Error loading Outgoing Messages tab when there are no results
    • #4905: Contacts controllers search is clearing $stateParams when it shouldn’t
    • #4942: Fix failing test: “Family Survey form Submit Family Survey form”
    • #4962: The accept_patient_reports transition is broken
    • #5045: DOMException crash leaves users stuck at loading spinner
    • #5221: Outgoing messages columns should not wrap on smaller screens
    • #5326: Bootstrap translation doesn’t work on first load
    • #5327: The login page is not translated
    • #5348: Handle Database has a global failure error during replication
    • #5395: Optimize the fix-user-db-security migration for databases with many users
    • #5414: Unknown replication status/last sync
    • #5417: All reports are highlighted
    • #5420: Optimize deletion of read docs for databases with many users

    Technical issues

    • #4865: grunt watch doesn’t deploy inbox.html changes
    • #4955: Bump autoprefixer browser support
    • #4979: Weak passwords should not be used on CouchDB developer boxes
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/3.4.0/index.html b/core/releases/3.4.0/index.html index 7f6fdd50bd..65b51df6c4 100644 --- a/core/releases/3.4.0/index.html +++ b/core/releases/3.4.0/index.html @@ -1,4 +1,4 @@ -3.4.0 release notes | Community Health Toolkit +3.4.0 release notes | Community Health Toolkit

    3.4.0 release notes

    Known issues

    • medic#5617: Broken functionality after upgrade to 3.4 with custom locales

    Upgrade notes

    Breaking changes

    There are no breaking changes when upgrading from 3.3.x.

    Supported software

    There are no required changes to the supported software matrix + Create project issue

    3.4.0 release notes

    Known issues

    • medic#5617: Broken functionality after upgrade to 3.4 with custom locales

    Upgrade notes

    Breaking changes

    There are no breaking changes when upgrading from 3.3.x.

    Supported software

    There are no required changes to the supported software matrix from 3.0.0.

    NodeCouchDBBrowsersSMS bridgeAndroidmedic-androidmedic-couch2pg
    8.11+2.1+Chrome 53+, Firefox latestmedic-gateway4.4+0.4.5+3.0+

    Scale to support more users

    In previous versions users who had an internet connection would maintain a continuous request to the server waiting for any relevant database changes. For projects with thousands of users this caused the server to use a lot of memory to keep track of all of those requests.

    This change removes the continuous replication in favor of making a short lived request when;

    • five minutes has passed since the last replication attempt,
    • the user creates or updates a document,
    • we detect the user has come back online, or
    • the user clicks the new “Sync now” button.

    medic#4805 and medic#3976

    Screenshots

    Customizable branding

    You can now configure the webapp logo, title, and favicon to use your project’s branding medic#4849. You can also add logos of your partners to show on the About page medic#4850. More information is available in the feature overview and the configuration documentation.

    Improved performance loading Contacts tab

    We made several changes to reduce the time it takes to load information on the Contacts tab.

    And more…

    Features

    • medic#2031: Log user statistics on key user interactions to the database

    Improvements

    • medic-android#66: Only allow webview to access expected URLs
    • medic-conf#112: XForm without instanceID will not load
    • medic#3128: Ability to delete a translation key
    • medic#3762: Re-write permissions data structure to be object properties instead of an array to avoid duplicates
    • medic#4209: Show meaningful error when ‘username’ is already taken
    • medic#4598: Message’s translation key sent when translation is missing
    • medic#4599: Allow translation to nest another translation key
    • medic#4682: Remove blank UI elements in Enketo forms
    • medic#4694: Link the report to the scheduled task that caused the report to be created
    • medic#4733: Update the medic-android target API
    • medic#4796: Update the medic-gateway target API
    • medic#4922: Add a logout button in the admin app
    • medic#4960: Improve design and usability of admin app
    • medic#4973: Add a favicon to the admin app
    • medic#5035: Link to webapp from admin app
    • medic#5082: Make content-security-policy more strict
    • medic#5249: Country code and Gateway phone number fields shouldn’t be required
    • medic#5307: Swahili “starting app” translation missing

    Performance fixes

    • medic#3466: Marking a report as verified is slow
    • medic#4936: Add performance checking to release testing workflow
    • medic#5050: Memory leak when switching between tabs
    • medic#5229: Resources doc re-downloaded on every change in list pages

    Bug fixes

    • couch2pg#6: Update couch2pg to escape characters that postgres doesn’t support
    • medic#4279: Countdown timer does not show if there is a relevant
    • medic#4621: Invalid report not being replicated to facility user
    • medic#4697: Renaming an Area after generating a report will not update the report names
    • medic#4885: Schedules are not cleared for offline contacts/reports
    • medic#5018: Noto font not loading on the admin webapp
    • medic#5019: Reports with scheduled tasks and deleted patients fail to load
    • medic#5025: Fix favicon caching
    • medic#5027: Branding doesn’t work for offline users
    • medic#5029: Branding doesn’t update on login page
    • medic#5034: Horticulturalist postCleanup not actually cleaning up
    • medic#5057: Task titles on overdue tasks only are appearing red
    • medic#5062: Cannot start API after changes to the translation messages format
    • medic#5068: The released API bundle is missing shared-libs
    • medic#5071: Loading Targets tab as admin logs an Error
    • medic#5078: Forms still available after deleting them using medic-conf
    • medic#5108: All reports synced doesn’t notice new reports
    • medic#5109: Restarting API always triggers a new version prompt
    • medic#5186: Only send tombstone deletes when document is still deleted
    • medic#5234: Guard against bad HTTP codes in error reporting
    • medic#5250: Adding or deleting languages should update the UI without needing to refresh
    • medic#5270: Bootstrap failed retry button doesn’t work
    • medic#5271: Translations not updated after upgrade
    • medic#5272: Message queue not loading when translations are missing
    • medic#5277: Upgrades via admin upgrade page are broken
    • medic#5319: db-batch gets stuck in a loop
    • medic#5380: Check is missing in checkboxes
    • medic#5390: The accept_patient_reports transition clears sent tasks
    • medic#5409: Errors when navigating away from edited forms
    • medic#5432: Selecting no language and submitting is showing translation keys instead of default app language
    • medic#5482: Countdown timer sound is not playing
    • medic#5486: Logging in with an incorrect password shows an unhelpful error message
    • medic#5488: Reports are being created for the wrong contact
    • medic#5514: Images on the Reports tab are not displaying

    Technical issues

    • medic#3476: Investigate and use travis build stages
    • medic#3925: Fix “db.type() is deprecated and will be removed in a future version of PouchDB”
    • medic#4814: Replace yarn with npm
    • medic#4932: Re-enable flakey e2e test: register by SMS
    • medic#5026: Deprecation warning running add-branding-doc migration
    • medic#5030: Switch to eslint
    • medic#5031: Write a script to import and export translations to POE
    • medic#5038: Fix flakey test: Reports Summary Displays correct LHS and RHS summary Concerning reports using patient_id
    • medic#5099: Fix and re-instate flakey e2e test
    • medic#5227: Clarify process for running e2e tests locally
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/releases/3.4.1/index.html b/core/releases/3.4.1/index.html index 72bb8ece9c..faac173849 100644 --- a/core/releases/3.4.1/index.html +++ b/core/releases/3.4.1/index.html @@ -1,5 +1,5 @@ -3.4.1 release notes | Community Health Toolkit -

    3.4.1 release notes

    Bug fixes

    Unicode form codes not clearing schedules

    When using non-Latin characters in a form code our software failed to find the right schedules to clear so unwanted messages were being sent. This affects versions from 3.0.0 to 3.4.0. medic#5698

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/3.5.0/index.html b/core/releases/3.5.0/index.html index 538effa313..fa4d98fa26 100644 --- a/core/releases/3.5.0/index.html +++ b/core/releases/3.5.0/index.html @@ -1,4 +1,4 @@ -3.5.0 release notes | Community Health Toolkit +3.5.0 release notes | Community Health Toolkit

    3.5.0 release notes

    Known issues

    • A small performance regression when loading the list of Contacts. medic#5755.

    Upgrade notes

    Breaking changes

    There are no breaking changes when upgrading from 3.4.x.

    Supported software

    There are no required changes to the supported software matrix + Create project issue

    3.5.0 release notes

    Known issues

    • A small performance regression when loading the list of Contacts. medic#5755.

    Upgrade notes

    Breaking changes

    There are no breaking changes when upgrading from 3.4.x.

    Supported software

    There are no required changes to the supported software matrix from 3.0.0.

    NodeCouchDBBrowsersSMS bridgeAndroidmedic-androidmedic-couch2pg
    8.11+2.1+Chrome 53+, Firefox latestmedic-gateway4.4+0.4.5+3.0+

    Performance

    Optimizing the history tab

    We found that loading the History tab was taking a long time in projects with many places. This was due to loading the Places filter on the tab. By delaying the initialisation of the filter until needed we found performance improvements in three key areas. medic#4423 and medic#5455.

    Metric3.4.03.5.0Improvement
    Rendering the history tab7247ms1843ms75%
    Rendering the history content pane1002ms732ms27%
    Starting the application1020ms875ms14%
    Idle memory usage453MB202MB55%

    Optimizing sentinel processing

    By reducing the throttling of change processing and reducing the number of database writes we have greatly improved the rate at which Sentinel processes changes. Clearing a backlog of 200 documents used to take 67 seconds, but with 3.5.0 this has been reduced to 33 seconds, a 50% reduction. medic#4128.

    To make Sentinel even more responsive to changes, transitions are now executed synchronously when messages are received. This means that replies are generated immediately and the user is notified of the success or error of their message as soon as possible. medic#4857.

    Reducing the app size

    After some analysis it was found that the webapp included font files that weren’t required. Certain files weren’t minified, and some others weren’t compressed. These fixes combined reduced the size by 2.6MB making the application easier to download and update. medic#5260, medic#5489, and medic#5490.

    Sort muted contacts

    Muted contacts are now sorted to the bottom of the contact list so you can more easily see all your active contacts. medic#5596.

    Sort muted contacts

    Facility EMR integration

    Version 3.5.0 includes an integration with SIH, a facility based application in Mali. This enables patient referral information to be sent to a clinic and treatment data to be returned, closing the loop between the Community Health Worker and the clinic. This integration is possible thanks in part to a new outbound push feature, which is configurable and will facilitate integrations with other systems in the future. medic#5499, and medic#4813.

    And more…

    Features

    • medic#1850: Report version of application and forms being used
    • medic#4811: Expose SMS sending capabilities in android wrapper
    • medic#4812: Support XForms that “compile” into SMS messages

    Improvements

    • horticulturalist#32: Horti should by default install release version and not master
    • medic-conf#117: Bulk user import
    • medic-conf#135: Create-user function always creates a new place
    • medic-conf#139: Allow data sharing and definition self-reference through the this keyword
    • medic-conf#147: Events dueDate function cannot access the reports of the contact
    • medic#5352: Date formatting in exports is not human readable
    • medic#5361: Serve app from root
    • medic#5439: Validate target ID uniqueness
    • medic#5511: Clean up translation exports
    • medic#5537: Add a permission to show the Report edit button
    • medic#5544: UHC metrics are incongruous between contacts and analytics tab due to month_start_date
    • medic#5579: Allow users to specify date for UHC last visited
    • medic#5580: Record the date when verifying a report
    • medic#5589: Allow external report contributions without a phone number
    • medic#5674: Add docs about the new replications feature to app-settings doc in cht-docs

    Performance fixes

    • medic#3543: Sentinel marks reports with the sys.facility_not_found error before it searches and looks for the contact
    • medic#4327: Removes changes watching for online users to improve performance
    • medic#4713: Replace appcache with a service worker cache
    • medic#4898: Move feedback docs to users’ meta dbs

    Bug fixes

    • horticulturalist#36: Horti crashes on invalid json during view warming
    • medic-android#57: Opening the admin console through webapp requires user to close and reopen the app
    • medic-android#72: gradle sync fails because minSDKversion is in the manifest file not the build.gradle file.
    • medic-conf#130: medic-conf uploads puring function “purging” but webapp looks for function named “purge”
    • medic-conf#132: Shell completion for supported actions not working
    • medic-conf#138: subtitle_translation_key should not be a required field for all targets
    • medic-conf#140: Multiple tasks emitted for the same report do not have a unique id
    • medic-conf#141: Task event id should not be required
    • medic-conf#142: Function “modifyContent” parameters are undefined for task applies to “contacts”
    • medic-conf#143: Function “emitCustom” is not invoked for targets when applies to “contacts”
    • medic-conf#144: Function “emitCustom” should be passed in the instance which would have been emitted
    • medic-conf#146: Target “appliesToType” is documented as not required but crashes if not defined
    • medic-conf#148: Multiple tasks emitted for different events do not have a unique id
    • medic-conf#149: Emitted targets have undefined date for date of “reported” when appliesTo “contacts”
    • medic-conf#150: JsToString function is buggy and unnecessary
    • medic-conf#171: Error occurred during backup-all-forms command
    • medic-conf#177: medic-conf –local fails in medic/config/standard
    • medic-conf#185: medic-conf does not backup, upload, or convert forms by default
    • medic-conf#199: Support creating custom locales based on app version
    • medic#3093: Registering for a SMS schedule can start mid-group
    • medic#3986: If server is down app cannot start
    • medic#4404: Fix training data script references non-existent view
    • medic#4483: It’s possible to generate feedback docs with no error attached
    • medic#4498: Cannot send multiple scheduled messages at same time
    • medic#5120: Translation key should be mandatory
    • medic#5121: We should be able to add Keys with no translations
    • medic#5183: User cannot logout while offline
    • medic#5298: Investigate and fix common feedback errors
    • medic#5330: Error loading target details
    • medic#5345: Registration transition invalidates unique ids when creating people
    • medic#5347: The update_scheduled_reports transition runs indefinitely
    • medic#5351: Reminders are not working
    • medic#5357: The conditional_alerts transition should sort docs by reported_date
    • medic#5359: The death_reporting transition does not process reports when submitter has no phone number
    • medic#5365: Unregistered phone number or person getting an autoreply with correct textform format
    • medic#5369: Message recipient defaults to sender when recipient is explicit
    • medic#5376: Report Bug not working for admin users
    • medic#5405: Can’t edit translation terms for new keys
    • medic#5410: Registration transition should calculate dates relative to reported_date
    • medic#5415: ANC visit count includes visits specified as not attended in Standard configuration
    • medic#5447: Remove unused permissions
    • medic#5459: Place select2 dropdown results are badly styled in the admin app
    • medic#5481: Angular $exceptionHandler too sensitive
    • medic#5498: The DDoc Version on the About page isn’t working
    • medic#5501: Reloading the app while offline shows sync status “required”
    • medic#5504: poe export script empties translation files
    • medic#5519: Permission denied when API is trying to create extracted-resources path in docker env
    • medic#5522: Feedback modal doesn’t close after successful submission
    • medic#5531: Tooltip showing as logo
    • medic#5536: Countdown timer does not respect custom timer value
    • medic#5577: Nutrition Screening is linking report to contact that you’re logged in as and not the one it is reported under.
    • medic#5595: Fix UHC styling when using both UHC and muting at the same time
    • medic#5599: Logging in as admin on mobile app you’re unable to leave the admin console
    • medic#5602: People card shows on a person’s profile
    • medic#5617: Broken functionality after upgrade to 3.4 with custom locales
    • medic#5621: Offline users are seeing form errors same user online has no errors
    • medic#5622: JavaScript errors and crash stacks not logged to the console
    • medic#5639: Tasks to be completed for people do not appear in a place’s profile
    • medic#5646: Service worker is broken on older versions of Chrome
    • medic#5653: Upgrading from 3.4 to 3.5 is broken
    • medic#5668: The conditional_alerts transition doesn’t include the current report in the list of relevant reports
    • medic#5671: The accept_patient_reports throws an error
    • medic#5675: Import people blocked by CSP
    • medic#5692: API does not block offline users from editing docs when dbname is not medic
    • medic#5705: Webapp doesn’t support languages that are not pluralized by make-plural
    • medic#5718: New households registered do not show on the contact list until after page reload
    • medic#5741: Mobile “back” button not working as expected when creating reports
    • medic#5742: The translation “contact.no.children” is missing in the default configuration
    • medic#5743: User cannot login when database name is not medic

    Technical issues

    • medic#3768: Check for npm audit vulnerabilities in automated builds and fix existing vulnerabilities
    • medic#4908: Fix flakey test - Submit Add Family form
    • medic#4968: Write a script that collects all aggregate docs from users’ meta dbs
    • medic#5196: Travis builds timing out after ~50 minutes when API fails to start
    • medic#5198: Work out how to run CI tests for outside collaborators
    • medic#5228: Clarify and streamline process for investigating e2e flakey test failures
    • medic#5297: Individual builds can no longer be restarted
    • medic#5300: Remove node domain from export data controller
    • medic#5311: Convert feedback export to streaming csv
    • medic#5340: Testing dev builds on mobile device after service worker change is awkward
    • medic#5546: Move “selected” into redux store
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/releases/3.6.0/index.html b/core/releases/3.6.0/index.html index 191e13cf94..46b849d73e 100644 --- a/core/releases/3.6.0/index.html +++ b/core/releases/3.6.0/index.html @@ -1,4 +1,4 @@ -3.6.0 release notes | Community Health Toolkit +3.6.0 release notes | Community Health Toolkit

    3.6.0 release notes

    Known issues

    None.

    Upgrade notes

    Breaking changes

    There are no breaking changes when upgrading from 3.5.x.

    Supported software

    There are no required changes to the supported software matrix + Create project issue

    3.6.0 release notes

    Known issues

    None.

    Upgrade notes

    Breaking changes

    There are no breaking changes when upgrading from 3.5.x.

    Supported software

    There are no required changes to the supported software matrix from 3.0.0.

    NodeCouchDBBrowsersSMS bridgeAndroidmedic-androidmedic-couch2pg
    8.11+2.1+Chrome 53+, Firefox latestmedic-gateway4.4+0.4.5+3.0+

    Africa’s Talking integration

    Version 3.6.0 includes an integration with the Africa’s Talking SMS aggregator. This allows us to offer a cloud based alternative to managing your own medic-gateway devices. We have created a new routine and a new set of API endpoints to send SMS via Africa’s Talking, process callbacks from Africa’s Talking, and update the status of outgoing messages. Please note that this integration is disabled by default. For more information read the configuration documentation.

    mRDT timestamp metadata

    Version 3.6.0 includes a backward compatible enhancement to the processing of mRDT metadata. Since v0.1.5, the rdt-capture app includes a timestamp representing how long it takes the CHV to capture the RDT. This piece of information is passed to the android wrapper medic-android which, since v0.4.30, passes it to the webapp. Once the mRDT form is submitted, this new field timeTaken will be stored in the resulting document.

    Performance

    When we released 3.5.0, we noted the existence of a performance regression when loading the list of Contacts. This issue has been resolved in 3.6.0.

    And more…

    Features

    • medic#5604: Integration with Africa’s Talking SMS aggregator
    • medic#5695: Reports with mRDT photo should also include timestamp metadata from the mRDT app

    Improvements

    • horticulturalist#41: Support and build against Node versions 8, 10, and 12
    • medic-android#78: After merge to master: build, version, sign, and upload through Travis
    • medic-collect#27: Publish signed builds to GH Releases
    • medic-conf#172: Add a command to update the parents of contacts
    • medic-gateway#145: Publish signed builds to GH Releases
    • medic#1978: Implement heartbeat to detect 401s for admin users
    • medic#4815: Add support for malnutrition reporting to standard
    • medic#5464: Add new permissions to allow users to view outgoing messages and export tabs of the admin app
    • medic#5485: Muso supervisors can’t review mRDT reports while having a replication depth of 1

    Performance fixes

    Bug fixes

    • medic-android#74: Remove reference to stale android-check dependency
    • medic#3254: Trying to send a message when logged out is weird
    • medic#3299: Not showing login screen if logged out until refresh
    • medic#3919: 401s should always redirect to the login page
    • medic#5654: Timer widget beeps even after cancelling the form
    • medic#5750: Admin upgrade page doesn’t update
    • medic#5753: CSP error in Console rendering forms with attached images
    • medic#5760: The initial_replication_date in info docs is always unknown
    • medic#5767: Node environment error after upgrading to 3.5.0 release
    • medic#5789: Muso’s mRDT supervisor tasks cannot resolve

    Technical issues

    • medic#5642: Ensure that all Android apps support 64bit
    • medic#5694: Update apps to use local shared-libs
    • medic#5736: Build deletion script fails to execute
    • medic#5739: Make bundles smaller by deduping dependencies
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/releases/3.6.1/index.html b/core/releases/3.6.1/index.html index a8e9e08994..8ccdbc5927 100644 --- a/core/releases/3.6.1/index.html +++ b/core/releases/3.6.1/index.html @@ -1,5 +1,5 @@ -3.6.1 release notes | Community Health Toolkit -

    3.6.1 release notes

    Bug fixes

    • medic#5844: Source validation failing for Africa’s Talking aggregator integration
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/3.6.2/index.html b/core/releases/3.6.2/index.html index 94a784ae09..e238b6b252 100644 --- a/core/releases/3.6.2/index.html +++ b/core/releases/3.6.2/index.html @@ -1,5 +1,5 @@ -3.6.2 release notes | Community Health Toolkit -

    3.6.2 release notes

    Performance fixes

    • medic#5942: Replace underscore with lodash in replication
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/3.7.0/index.html b/core/releases/3.7.0/index.html index 402cdb5c92..f29171f638 100644 --- a/core/releases/3.7.0/index.html +++ b/core/releases/3.7.0/index.html @@ -1,4 +1,4 @@ -3.7.0 release notes | Community Health Toolkit +3.7.0 release notes | Community Health Toolkit

    3.7.0 release notes

    Known issues

    None.

    Upgrade notes

    Breaking changes

    1. A new OS level dependency (xsltproc) is required for generating XFORM html on the server.
    2. Not fully supported by medic-conf@2.x. For full functionality, users must upgrade to medic-conf@3.x.
    3. With the introduction of configurable hierarchies, additional configuration must be added. More information about how to set up configurable hierarchies.

    Supported software

    There are no required changes to the supported software matrix + Create project issue

    3.7.0 release notes

    Known issues

    None.

    Upgrade notes

    Breaking changes

    1. A new OS level dependency (xsltproc) is required for generating XFORM html on the server.
    2. Not fully supported by medic-conf@2.x. For full functionality, users must upgrade to medic-conf@3.x.
    3. With the introduction of configurable hierarchies, additional configuration must be added. More information about how to set up configurable hierarchies.

    Supported software

    There are no required changes to the supported software matrix from 3.0.0.

    NodeCouchDBBrowsersSMS bridgeAndroidmedic-androidmedic-couch2pg
    8.11+2.1+Chrome 53+, Firefox latestmedic-gateway4.4+0.4.5+3.0+

    Configurable hierarchies

    It is now possible to configure contact hierarchies. This allows for better tailored solutions for new projects and opens up new configuration possibilities: for example having any number of place levels, multiple person types, or unbalanced place trees. @@ -326,7 +326,8 @@ 10.000 documents. This warning is shown before starting initial replication in an attempt to reduce the incidence of misconfigured users.

    Warn when replicating too many docs

    Performance

    Users can expect faster replication, faster application load time and faster form load time:

    ActionTecno-J5 on 3.7Tecno-J5 on 3.6Tecno-WX3 on 3.7Tecno-WX3 on 3.6
    Initial load (*)0:02:290:04:180:02:450:05:01
    Loading reports tab0:00:200:00:320:00:250:00:44
    Process a specific form0:00:190:00:230:00:210:00:34

    Performance chart

    (*) Includes initial replication and and application bootstrapping for the first time.

    We also fixed a bug where a user with a large number of documents trying to replicate could lock up medic-api.

    And more…

    Features

    Improvements

    • medic-conf#109: Support for ES6 in declarative config
    • medic-conf#128: Improve medic-conf testing by using actual form definitions
    • medic-conf#145: Declarative Config - Support for target.idType to return the id of the parent
    • medic-conf#151: Declarative Config - Improved system for file-based componentization
    • medic-conf#200: When uploading custom translations we should indicate better that the admin needs to give the language a name.
    • medic-conf#205: Declarative Configuration - Ability to control which contact is associated with emitted task
    • medic-docs#140: Document how to configure multimedia elements in forms
    • medic-os#48: Update default couchdb configuration
    • medic#1926: Cannot view XForm videos on mobile device when offline
    • medic#4926: Allow a different header label for the list of people on the place profile between different places in the hierarchy
    • medic#5123: Improvements to the report summary page UI
    • medic#5313: Validate email address when creating or editing users
    • medic#5652: Allow xform attachments to have useful names
    • medic#5725: UI/UX updates on admin app Branding page
    • medic#5914: Use CHT reference app as default
    • medic#5919: Improve infodoc replication timestamp accuracy
    • medic#6005: Review and clean XLSForms for CHT reference app
    • medic#6016: Target Revisions: CHT reference app

    Performance fixes

    • medic#4450: Optimise form HTML generation
    • medic#5362: Warn when replicating too many docs
    • medic#5443: Server side purge
    • medic#5475: Reminders update clinic docs
    • medic#5540: Profile application memory usage
    • medic#5724: Loading portions of the reports tab causes the page to lock up.
    • medic#5727: Updating branding images doesn’t delete the obsolete attachments
    • medic#5759: Makes replication _all_docs requests quicker
    • medic#5797: Make replication _bulk_docs requests quicker
    • medic#5859: Lazy load unread count to reduce visible load time
    • medic#5942: Replace underscore with lodash in replication

    Bug fixes

    • medic-collect#29: Cannot manually send reports via Internet
    • medic-conf#176: Node 12 support in medic-conf tests
    • medic-conf#208: Declarative Configuration - appliesToType is not optional when appliesTo: contacts
    • medic-conf#209: Declarative Configuration - Global value user is not accessible from declarative configuration code
    • medic-conf#221: Actions that write files seems to be broken create-users, fetch files.
    • medic#3050: Update prompt should appear only when there is ‘real change’
    • medic#5484: Setting relevant=“false()” is a hard error to troubleshoot in console
    • medic#5507: Creating / syncing meta db-s fails when db name is not “medic”
    • medic#5616: Changing user profile language prevents user from changing back to original language
    • medic#5628: Uncaught Error in production v3.4 - Cannot read property ‘body’ of undefined
    • medic#5672: Webapp messages conversations don’t scroll correctly when loading new unread records
    • medic#5699: Cannot make multiple changes to branding without refresh
    • medic#5715: Primary contact created through “new place form” doesn’t have hierarchy
    • medic#5729: Cannot add more than one household member
    • medic#5747: Ability to hide a field within a section on the Reports tab detail
    • medic#5762: Immunization Condition Card not getting updated as per Collect Immunization Form submission
    • medic#5763: The POE export script incorrectly detects invalid placeholders
    • medic#5768: Incorrect XLSForm meta data values in Enketo
    • medic#5780: View Report on outgoing messages tab brings up blank page
    • medic#5783: e2e test, executed after the service worker e2e test, fails during login
    • medic#5785: Offline users that can_configure are redirected to admin app
    • medic#5801: Warning message about ambiguous data binding doesn’t report which value caused the error
    • medic#5805: The style loading is delayed when launching a new form from the reports tab
    • medic#5809: Creating a user with a simple password using the API hangs
    • medic#5812: Deleting a contact with messages causes the messages tab to error.
    • medic#5813: Error in creating New Area - LG Config
    • medic#5824: Moving contacts doesn’t update the contact who will receive scheduled messages when no patient_id is set.
    • medic#5825: Outbound tasks fail if one of the task documents is deleted
    • medic#5826: If an outbound task fails in mapping it causes all other tasks to fail
    • medic#5840: Reintroduce styling for weeksPregnant filter
    • medic#5845: Access logging outputs the load balancer IP in production
    • medic#5847: Settings shared-lib throws an error if the credential is an empty string
    • medic#5851: Requesting feedback docs fails with Error about long string
    • medic#5852: get_users_meta_docs outputs [] as next value forever when there are no docs available to view
    • medic#5855: Refreshing the tasks page after selecting a task does not load a task correctly
    • medic#5877: Add person icon is not monochrome
    • medic#5884: The PLACE_TYPE-create and PLACE_TYPE-edit xlsx files need attention with configurable hierarchy
    • medic#5888: Search for places in add user does not return custom_type persons.
    • medic#5892: The “New action” popup dialog is partially invisible and in wrong location on narrow screens
    • medic#5894: Report place filter doesn’t load
    • medic#5897: Reports without a patient_id don’t get synced if the patient is moved to a new place
    • medic#5899: Task list in places view doesn’t include the tasks of children
    • medic#5901: Potential race condition in get_users_meta_docs script
    • medic#5920: Theme page content does not scroll
    • medic#5930: Online users have a sync status in the hamburger menu
    • medic#5931: Android App cannot load app on initial replication
    • medic#5933: Report review dropup styling is broken
    • medic#5959: Error when loading reports as an offline user
    • medic#5961: API sometimes generates an empty model.xml and form.html
    • medic#5971: CHT Reference App’s delivery form causes “Invalid XML” in latest master
    • medic#5973: Guided Tour does not show control buttons
    • medic#5978: Language select modal Submit button never enabled
    • medic#5987: Form context is filtering on the admin console
    • medic#6012: Deleting a purge database without restarting API could block users from replicating
    • medic#6017: Transitions missing from default config.
    • medic#6019: Add Target does not work on gamma.dev

    Technical issues

    • medic-android#86: Target API level requirements from August 2019
    • medic-android#88: Enable largeHeap setting in the android manifest since our footprint is large
    • medic-android#92: Set target audience in all android apps on the play store
    • medic#2701: Framework for testing XForms used for app workflows
    • medic#3570: Remove enketo form generation feature
    • medic#5344: Flakey e2e test: registration transition submits new sms messages shows content
    • medic#5479: Investigate Play Store replacements
    • medic#5548: Documentation tooling
    • medic#5590: Update the CheckDate service to store the delta in telemetry
    • medic#5607: Support Node 12
    • medic#5635: Move more scope properties to store and controller
    • medic#5711: Remove unused less variables
    • medic#5829: Flakey unit tests: auth directive
    • medic#5915: Allow for manipulating services in e2e tests on travis
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/releases/3.7.1/index.html b/core/releases/3.7.1/index.html index 4d99e24262..de45460c42 100644 --- a/core/releases/3.7.1/index.html +++ b/core/releases/3.7.1/index.html @@ -1,5 +1,5 @@ -3.7.1 release notes | Community Health Toolkit -

    3.7.1 release notes

    Bug fixes

    • cht-core#6025: Outbound can halt scheduler execution permanently
    • cht-core#6029: Inconsistent field label in CHT Reference App - Blue skin color
    • cht-core#6052: Missing labels for report form fields
    • cht-core#6065: Confirmation notification displayed while navigating away from tasks list
    • cht-core#6071: Configurable contacts not correctly mapped as SMS recipients
    • medic-conf#260: Fields are not showing when custom type is defined for appliesToType
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/3.8.0/index.html b/core/releases/3.8.0/index.html index 1d89563071..dd6e1059c1 100644 --- a/core/releases/3.8.0/index.html +++ b/core/releases/3.8.0/index.html @@ -1,9 +1,9 @@ -3.8.0 release notes | Community Health Toolkit +3.8.0 release notes | Community Health Toolkit

    3.8.0 release notes

    Known issues

    No known issues.

    Upgrade notes

    Breaking changes

    The changes to tasks and targets require a “3.8 compatible” partner configurations to be deployed for the tasks and target tabs to continue to function. You’ll see errors on these tabs and the console error Rules Engine: Updates to the nools schema are required if the configuration is not compatible with 3.8. All “3.8 compatible” configurations are backward compatible and can be deployed safely to any Core Framework version. It is therefore recommended to deploy the “3.8 compatible” configuration before upgrading so your application will work seamlessly once the upgrade completes.

    • For partners using declarative configuration, ensure your configuration is deployed using medic-conf 3.1.0 or greater.
    • For partners maintaining their configuration in the legacy rules.nools.js file, ensure the latest schema changes are deployed.

    These changes also result in new constraints for partner application task and target code:

    • Tasks with multiple actions cannot use different contacts for their actions[n].content.contact values. For example, one action cannot open a form for contact x and a second action for the same task open a form for contact y.
    • Reports “with errors” are now available in all task and target calculations (instead of just some calculations) and partner code is responsible for filtering them as necessary.
    • For partners maintaining their configuration in a rules.nools.js file, multiple contacts emitting tasks with conflicting id values is no longer supported. This will result in errant behavior in 3.8.0.
    • Target emissions with conflicting ids were previously merged based on the order in which the requesting contact was created inside CouchDB. It is now merged based on the order of the requesting contact’s reported_date attribute.
    • The specific meaning of task “resolution” has been augmented to accommodate the additional complexities of reporting a task’s “state” in Postgres. Read the documentation for more information.
    • A task’s content is cached inside the task document. Any timestamps or calculation can be up to seven days old. This is a breaking change for configurations using the current time as part of their calculations.

    High sync times immediately after upgrade

    The changes to tasks and targets may have significant performance implications for some users. Heavy users may see hundreds of new task documents created on their device the first time they load the task or target tabs after upgrading to 3.8.0. Users can expect longer than usual sync times for their first sync after viewing the task or target tab in 3.8.0.

    Supported software

    There are no required changes to the supported software matrix + Create project issue

    3.8.0 release notes

    Known issues

    No known issues.

    Upgrade notes

    Breaking changes

    The changes to tasks and targets require a “3.8 compatible” partner configurations to be deployed for the tasks and target tabs to continue to function. You’ll see errors on these tabs and the console error Rules Engine: Updates to the nools schema are required if the configuration is not compatible with 3.8. All “3.8 compatible” configurations are backward compatible and can be deployed safely to any Core Framework version. It is therefore recommended to deploy the “3.8 compatible” configuration before upgrading so your application will work seamlessly once the upgrade completes.

    • For partners using declarative configuration, ensure your configuration is deployed using medic-conf 3.1.0 or greater.
    • For partners maintaining their configuration in the legacy rules.nools.js file, ensure the latest schema changes are deployed.

    These changes also result in new constraints for partner application task and target code:

    • Tasks with multiple actions cannot use different contacts for their actions[n].content.contact values. For example, one action cannot open a form for contact x and a second action for the same task open a form for contact y.
    • Reports “with errors” are now available in all task and target calculations (instead of just some calculations) and partner code is responsible for filtering them as necessary.
    • For partners maintaining their configuration in a rules.nools.js file, multiple contacts emitting tasks with conflicting id values is no longer supported. This will result in errant behavior in 3.8.0.
    • Target emissions with conflicting ids were previously merged based on the order in which the requesting contact was created inside CouchDB. It is now merged based on the order of the requesting contact’s reported_date attribute.
    • The specific meaning of task “resolution” has been augmented to accommodate the additional complexities of reporting a task’s “state” in Postgres. Read the documentation for more information.
    • A task’s content is cached inside the task document. Any timestamps or calculation can be up to seven days old. This is a breaking change for configurations using the current time as part of their calculations.

    High sync times immediately after upgrade

    The changes to tasks and targets may have significant performance implications for some users. Heavy users may see hundreds of new task documents created on their device the first time they load the task or target tabs after upgrading to 3.8.0. Users can expect longer than usual sync times for their first sync after viewing the task or target tab in 3.8.0.

    Supported software

    There are no required changes to the supported software matrix from 3.0.0.

    NodeCouchDBBrowsersSMS bridgeAndroidmedic-androidmedic-couch2pg
    8.11+2.1+Chrome 53+, Firefox latestmedic-gateway4.4+0.4.5+3.0+

    Task and target data available for analysis

    The task and target system has undergone a significant rework. It is now powered by task documents and target documents which are written to PouchDB and synced to CouchDB/Postgres. This exposes task and target data in CouchDB and Postgres so queries can easily capture how tasks and targets are behaving for users in the field. These changes also set the foundation for powerful collaborative supervisor features to come in the future.

    These changes have resulted in important performance changes for the app.

    Create Places by SMS

    You are now able to create a place via SMS. This is useful if you wish to create a location before creating a person in that location (e.g. creating a new household before registering a member of that household).

    For more information, please read the configuration information.

    Replication improvements

    Sync status has been reworked to be clearer and more helpful. Specifically, it now specifies whether a lack of syncing is due to a missing internet connection:

    unable to sync example

    See the github issue for more information.

    Performance

    The changes to Tasks and Targets will have significant performance implications for projects. Many of these changes are hard to succinctly summarize and will vary greatly across project configurations and user types. Here are some performance changes you can expect in 3.8.0:

    • Less Memory Usage - The retained JS heap size for idle users has been reduced. This reduction is significant for users with many reports or users with large reports. The retained JS heap size for our heaviest known users has been reduced by ~75%!
    • Large First Sync After Upgrade - Expect longer than normal sync times for the first sync after the upgrade to 3.8.0. Some users may see hundreds of new task documents created on their device when they use the app after upgrading. These new documents need to sync to the server.
    • Increased Disk Usage - Writing task and target documents into PouchDB means the Core Framework will require more disk usage.
    • Reduced Time to Bootstrap - The bootstrapping step Loading rules… has been removed. This results in drastically reduced bootstrapping times that remain constant regardless of document count. After the user’s first replication and load, all users should be able to restart the app and bootstrap within one minute.
    • Long First-Load Time for the Tasks and Targets Tabs - The loading previously done during the Loading rules… bootstrap step now loads the first time a user navigates to the “Tasks” tab or the “Targets” tab. Expect long load times the first time one of these tabs is viewed. But it is only once, even if the user closes the app.
    • Net Performance Gains - After the first-load of the “Tasks” tab or the “Targets” tab, the tabs will load from a persistent cache. The cache is much faster than the first-load experience, but the tabs will load slower than in previous Core Framework versions. This is a performance trade-off which aims to reduce the time to complete operations in CHT app. The net result is a drastically reduced bootstrap time, and a slightly increased load time for specific tabs which provides a net reduction in load times for users to complete tasks.

    These trade-offs are complex and we’d love to hear how these changes affect your users and your configuration. Send us your measurements or other feedback on the CHT Forum.

    And more…

    Features

    Improvements

    • cht-core#3977: Improve sync status UI
    • cht-core#5381: Add an SMS page to the admin app to group SMS messaging configuration tasks together
    • cht-core#5382: Add a Display page to the admin app to group UI configuration tasks together
    • cht-core#5383: Move “Settings > Backup & restore” to “Backup app code” in the admin app
    • cht-core#5384: Rename and reorder admin app navigation
    • cht-core#5385: Update text on admin app Display page
    • cht-core#5386: Update text on admin app User page
    • cht-core#5387: Update text on admin app SMS page
    • cht-core#5388: Update text on admin app App forms page
    • cht-core#5389: Update text on admin app Targets page
    • cht-core#5391: Update text on admin app Images page
    • cht-core#5392: Update text on admin app Upgrades page
    • cht-core#5393: Update text on admin app Import & Export page
    • cht-core#5394: Update text on admin app Backup page
    • cht-core#5440: Add explanatory text about permissions to the admin app
    • cht-core#5493: Support a Muso Target - % of families which have received 2 home visits this month
    • cht-core#5608: Add python 2.7 to list of required software for developer environment
    • cht-core#5716: Update colors in the app and in variables.less file
    • cht-core#6032: ADD NO_LABEL TO Contact Objects in the CHT Reference APP
    • cht-core#6103: Retry upwards replication
    • cht-core#6104: Tab name inconsistencies in Guided Tour vs. Default config
    • cht-core#6166: Installation instructions lead to errors
    • medic-android#64: Expose device information in medic-android so we can store it against monthly telemetry readings
    • medic-conf#113: Warn if uploading configuration will overwrite someone elses changes
    • medic-conf#230: Allow relative days for date and timestamp columns in csv-to-docs command
    • medic-conf#236: Unit tests fail when run locally
    • medic-conf#276: Declarative Configuration - Make task/target identification attributes required data integrity of task documents
    • medic-conf#283: Declarative Config - Rename passGroupWithCount to passesIfGroupCount
    • medic-docs#142: Accessing and interpreting client-side telemetry
    • medic-docs#150: Add information about the known field in documentation
    • medic-gateway#137: Make sure that Gateway consumes / forwards all SMS before resuming polling
    • medic-nootils#9: Upgrading to medic-nootils@3.x includes test-only dependencies
    • rdt-capture#32: Must target Android 9.0 (API level 28) or above by November 1 2019

    Performance fixes

    • cht-core#5550: Measure and if possible improve CouchDB GET performance via API
    • cht-core#6023: Timeout option is not set during replication
    • cht-core#6106: Call ‘db.close’ for ephemeral PouchDB objects to avoid memory leaks

    Bug fixes

    • cht-core#2272: Make webapp date widget easier to use
    • cht-core#5564: Expired tasks don’t disappear from the Tasks tab when they expire
    • cht-core#5856: appliesToType on the contact summary cards and fields should accept arrays
    • cht-core#5863: Translation throwing errors in production
    • cht-core#5864: Gateway number validation is too strict
    • cht-core#5866: Reports with errors trigger tasks when created/modified but not on reload
    • cht-core#5949: Telemetry data are not saved under the right month
    • cht-core#5951: Reports page’s filter reset button gets cut off on Tecno Y4
    • cht-core#5963: The icon in condition cards is displaying too large
    • cht-core#5967: Wrap text in contact summary and condition cards
    • cht-core#5982: Uploading a custom translation with special characters breaks the admin page.
    • cht-core#6020: Current password validation error on Update password form
    • cht-core#6040: Age is missing when using unknown for birth date and entering years.
    • cht-core#6053: Use relative date of death in list of deceased persons
    • cht-core#6056: Death reporting transition requires patient_id
    • cht-core#6062: Missing translation warning is displayed in console but doesn’t seem to do anything when provided.
    • cht-core#6063: Number of days ago does not match date in death report
    • cht-core#6084: Timeouts for /api/v1/users leave user at “loading app…” forever
    • cht-core#6087: Reduce inbox.js bundle size to meet targets for 3.7 release (800kb)
    • cht-core#6115: medic-conf errors when trying to build the default config
    • cht-core#6117: Submit not working on Basic Settings tab on SMS admin page
    • cht-core#6118: Deleting a report in mobile view doesn’t redirect to list
    • cht-core#6122: Lineage hydrateDocs yields inconsistent results and possibly crashes when hydrating the same lineage
    • cht-core#6143: Users with a lot of large docs to replicate continuously fail
    • cht-core#6145: Image attachments are stored on the report twice
    • cht-core#6163: Add implicit configs that were lost when config.default.json was removed
    • cht-core#6164: Db-doc access fails when accessing a report about a deleted contact
    • cht-core#6169: Sentinel doesn’t restart its follow feed on error
    • cht-core#6170: Having a doc with a non-numeric reported_date breaks everything
    • cht-core#6185: Unread reports & messages menu bubbles no longer appearing
    • cht-core#6191: Supervisors download all task documents from CHWs they supervise
    • cht-core#6205: Medic ID never generated if a report is created before sentinel ID is generated
    • cht-core#6208: Missing translations
    • horticulturalist#44: The .horticulturalist.lock file prevents horti from restarting when the medic-os docker container restarts
    • medic-conf#168: Declarative Config - Document changes which result in appliesTo=false don’t cause the task to disappear
    • medic-conf#218: Url command-line options are required even for actions which don’t use it (regression)
    • medic-conf#240: convert-*-forms actions silently convert nothing when extraArgs are provided
    • medic-conf#259: Exclamation marks are double escaped in upload-custom-translations
    • medic-conf#278: upload-app-forms crashes when deploying projects/muso

    Technical issues

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/releases/3.8.1/index.html b/core/releases/3.8.1/index.html index 8f4a44420e..6b9b7a9b51 100644 --- a/core/releases/3.8.1/index.html +++ b/core/releases/3.8.1/index.html @@ -1,9 +1,9 @@ -3.8.1 release notes | Community Health Toolkit +3.8.1 release notes | Community Health Toolkit

    3.8.1 release notes

    Upgrade notes

    • CouchDB view code has been modified which will require rebuilding which may take some time depending on how many docs you have. We recommend you use the Stage feature to rebuild the views before upgrading to reduce server downtime.
    • Webapp code has been modified and therefore users will download the application again.

    Supported software

    There are no required changes to the supported software matrix + Create project issue

    3.8.1 release notes

    Upgrade notes

    • CouchDB view code has been modified which will require rebuilding which may take some time depending on how many docs you have. We recommend you use the Stage feature to rebuild the views before upgrading to reduce server downtime.
    • Webapp code has been modified and therefore users will download the application again.

    Supported software

    There are no required changes to the supported software matrix from 3.0.0.

    NodeCouchDBBrowsersSMS bridgeAndroidmedic-androidmedic-couch2pg
    8.11+2.1+Chrome 53+, Firefox latestmedic-gateway4.4+0.4.5+3.0+

    Changes

    Bug fixes

    • cht-core#6259: medic-conf create-users command fails when generate_patient_id_on_people transition is on
    • cht-core#6267: Targets with date: ’now’ show stale counts for first days of a reporting window
    • cht-core#6274: Specifying contactLabel in tasks configuration breaks task list rendering
    • cht-core#6279: Rendering of cascading selects broken after 3.7.x
    • cht-core#6288: Current language not being picked up by elements in repeat groups
    • cht-core#6321: XForms which use “media::image” have a spinner below the image
    • cht-core#6323: Tasks not opening action form directly from on a contact’s profile and on the tasks list
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/releases/3.8.2/index.html b/core/releases/3.8.2/index.html index 653f679b1b..c75c0903d2 100644 --- a/core/releases/3.8.2/index.html +++ b/core/releases/3.8.2/index.html @@ -1,9 +1,9 @@ -3.8.2 release notes | Community Health Toolkit +3.8.2 release notes | Community Health Toolkit

    3.8.2 release notes

    Known issues

    • When logging out (or after being logged out), users could end up in a redirect loop (cht-core#6337).

    Supported software

    There are no required changes to the supported software matrix + Create project issue

    3.8.2 release notes

    Known issues

    • When logging out (or after being logged out), users could end up in a redirect loop (cht-core#6337).

    Supported software

    There are no required changes to the supported software matrix from 3.0.0.

    NodeCouchDBBrowsersSMS bridgeAndroidmedic-androidmedic-couch2pg
    8.11+2.1+Chrome 53+, Firefox latestmedic-gateway4.4+0.4.5+3.0+

    Bug fixes

    • cht-core#6583: Users are forced to login one year after they last logged in
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/releases/3.9.0/index.html b/core/releases/3.9.0/index.html index 20a7cfa1ad..bde0b2ecc5 100644 --- a/core/releases/3.9.0/index.html +++ b/core/releases/3.9.0/index.html @@ -1,4 +1,4 @@ -3.9.0 release notes | Community Health Toolkit +3.9.0 release notes | Community Health Toolkit

    3.9.0 release notes

    Known issues

    None.

    Upgrade notes

    Breaking changes

    Docker image update required

    Prior to initiating an upgrade to 3.9, you will need to update the CHT Docker Image and a few packages inside the medic-os container. + Create project issue

    3.9.0 release notes

    Known issues

    None.

    Upgrade notes

    Breaking changes

    Docker image update required

    Prior to initiating an upgrade to 3.9, you will need to update the CHT Docker Image and a few packages inside the medic-os container. Please closely follow our 3.9 CHT Docker Image Upgrade Process

    This image updates the horticulturalist package to stage ddocs properly.

    Outbound Push only sends each report once

    The implementation of Outbound Push has changed as part of cht-core#6306. Now each configured outbound push will be sent only once per doc. Specifically, the first time your relevant_to function resolves to true is the first and only time a particular record will be send to a configured external service. This is to ensure that pushes are stable and predictable, and additional pushes are not sent unintentionally or unexpectedly. We are working on allowing for multiple pushes, please see cht-core#6419 and contribute your situation to help us determine the best way forward.

    Cleanup operations may impact app load times

    We have implemented a purging function to clean up obsolete feedback and telemetry docs as part of cht-core#5902. This will free up disk space and improve performance over time, however initially may cause slower app load times while the backlog is cleared. The cleanup is capped to 1000 docs per app load to limit the impact on users.

    Supported software

    There are no required changes to the supported software matrix from 3.0.0.

    NodeCouchDBBrowsersSMS bridgeAndroidmedic-androidmedic-couch2pg
    8.11+2.1+Chrome 53+, Firefox latestmedic-gateway4.4+0.4.5+3.0+

    DHIS2 integration

    The CHT now supports calculating and exporting data for DHIS2 integration. The Core Framework can now be configured to calculate DHIS2 dataValues, aggregate patient level data, view aggregate data offline, and export the aggregate data formatted for DHIS2. The data is also available via an API so you can access it from an interoperability layer such as OpenHIM.

    For more information read the documentation.

    Issues: cht-core#5661 and cht-core#6002

    Supervise target progress

    Targets can now be configured to be aggregated and synced to allow supervisors to view the progress of their health workers towards their goals. These new “aggregate” targets show an overview of all workers progress within their branch and can be selected to show each workers progress individually.

    aggregate targets

    Read the feature overview and the technical documentation to see how to configure this new aggregated target.

    Issues: cht-core#4839 and cht-core#4840

    RapidPro integration

    Interactive, structured messaging for health triage and clinical referrals is now possible with the integration of RapidPro. This opens new opportunities for a semi-automated, direct to patient approach to health assessments and care coordination at the community level. The Outbound Push feature has been extended to support submitting data to RapidPro so you can trigger workflows based on changes in the CouchDB database.

    Read the feature overview to learn more about this new integration.

    Issues: cht-core#6207

    Case management workflows

    For reports that aren’t patient or place centric you can now register a case with a generated case_id short code. This allows subsequent reports to identify which case they refer to.

    Read more about configuring the accept_case_reports transition in the CHT documentation.

    Issues: cht-core#6291.

    Instance monitoring

    A new API has been added to allow tracking of a range of metrics in the CHT platform to make monitoring, alerting, and debugging of your instance easier.

    Issues: cht-core#6133

    And more…

    Features

    • cht-core#6286: Link SMS reports to patient associated with the sender phone number
    • medic-conf#297: Add command to bulk-edit contacts on medic-conf

    Improvements

    • cht-core#5738: Trigger a sync immediately when a doc changes locally
    • cht-core#5772: Show a prompt when we detect the user is logged out
    • cht-core#5776: Make it clear that tasks can be completed anywhere in the window, not just on the due date
    • cht-core#5804: Display page tab text labels even on mobile view, whenever there are 3 or fewer tabs
    • cht-core#6188: Record telemetry events for tasks and targets
    • cht-core#6209: Update aggregate target doc after the reporting interval to ensure accuracy
    • cht-core#6211: Ability to make target widgets invisible
    • cht-core#6234: Adjust UHC colors to be consistent with our color palette
    • cht-core#6270: Minor CSS updates to admin app
    • cht-core#6280: Show locale selector on login page
    • cht-core#6306: Send outbound push without delay
    • cht-core#6331: Add an API for hydrating contacts on a doc
    • cht-core#6457: Rename configuration fields to be consistent with DHIS2 and make names localizable
    • medic-conf#104: AppliesToType is not respected for report-based targets
    • medic-conf#271: Only update configurations docs when something has changed
    • medic-conf#292: Refactor to replace renamed dependency “opn”
    • medic-conf#306: Allow for configuring xml2sms in forms
    • medic-conf#323: Improve usage documentation regarding the options parameter
    • medic-gateway#138: Tweak gateway SMS forwarding so you can define how long we wait to send SMS

    Performance fixes

    Bug fixes

    • cht-core#5955: Login page redirection doesn’t work
    • cht-core#6111: Unable to dismiss Guided Tour if you navigate to a different tab at the end of the tour
    • cht-core#6147: Data from user’s meta database is not being replicated to medic-user-meta
    • cht-core#6149: Report content patient hyperlinks point to ‘/contacts’ list
    • cht-core#6161: Fix race condition when deleting reports
    • cht-core#6162: Submitting almost any form from the mobile app throws and catches a Java error
    • cht-core#6183: Map tile fetching blocked by CSP for geopoint questions
    • cht-core#6184: Reverse geocoding blocked by CSP for geopoint questions
    • cht-core#6192: Broken link in Sentinel error message
    • cht-core#6212: Console errors on upgrade page
    • cht-core#6220: Number of tasks for person not shown on profile
    • cht-core#6223: Outbound Push calls code to delete broken tasks unnecessarily
    • cht-core#6231: Misaligned “Title” column in SMS Forms page of the App Management console
    • cht-core#6239: The number of Feedback docs is incorrect
    • cht-core#6249: CouchDB admins can’t login in the app
    • cht-core#6256: API error for weak password is not prescriptive
    • cht-core#6272: Modal dialogs are replaced by the same dialog
    • cht-core#6315: The update settings API reports success but has no effect
    • cht-core#6328: Upgrade page doesn’t show branches available to upgrade to if there are no betas to upgrade to
    • cht-core#6335: Received messages appear as sent messages
    • cht-core#6337: Infinite redirect loop on login
    • cht-core#6356: Search reset button does not clear freetext field
    • cht-core#6374: Labels for select fields are grey instead of black like other fields
    • cht-core#6389: If multiple task documents are created with the same emission id only one of them is ever updated
    • cht-core#6394: LHS Contact Action labels don’t render at 400px or below
    • cht-core#6415: Missing translation “state.duplicated”
    • cht-core#6417: As an offline user, submitting a report without a subject throws an error
    • cht-core#6421: Db-doc doesn’t allow unallocated data_records
    • cht-core#6425: Support resolving tasks concerning unknown contacts
    • cht-core#6440: Sentinel will skip changes entirely in certain conditions
    • cht-core#6450: Images used in a repeat are only showing spinners
    • medic-conf#286: Hierarchy error prevents “move-contacts” action from working with parents using contact_type
    • medic-conf#288: Keep our apps and configurations in sync with medic-conf version
    • medic-conf#300: Error when uploading forms using Windows CMD/Powershell using medic-conf 3.1.0
    • medic-conf#301: Unable to upload an app form after changing its .properties.json
    • medic-conf#303: upload-app-forms skips the upload of forms based only on local changes, ignoring the state of the form on the instance
    • medic-conf#325: Task generation fails when given contactless reports

    Technical issues

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/releases/3.9.1/index.html b/core/releases/3.9.1/index.html index c38169c12f..6eed894f00 100644 --- a/core/releases/3.9.1/index.html +++ b/core/releases/3.9.1/index.html @@ -1,9 +1,9 @@ -3.9.1 release notes | Community Health Toolkit +3.9.1 release notes | Community Health Toolkit

    3.9.1 release notes

    Supported software

    There are no required changes to the supported software matrix + Create project issue

    3.9.1 release notes

    Supported software

    There are no required changes to the supported software matrix from 3.0.0.

    NodeCouchDBBrowsersSMS bridgeAndroidmedic-androidmedic-couch2pg
    8.11+2.1+Chrome 53+, Firefox latestmedic-gateway4.4+0.4.5+3.0+

    Bug fixes

    • cht-core#6562: Switching between tabs while tasks are being calculated can result in having multiple task documents with the same emission id
    • cht-core#6583: Users are forced to login one year after they last logged in
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/releases/3.9.2/index.html b/core/releases/3.9.2/index.html index 687b8d9f7d..739ac785fe 100644 --- a/core/releases/3.9.2/index.html +++ b/core/releases/3.9.2/index.html @@ -1,9 +1,9 @@ -3.9.2 release notes | Community Health Toolkit +3.9.2 release notes | Community Health Toolkit

    3.9.2 release notes

    Supported software

    There are no required changes to the supported software matrix + Create project issue

    3.9.2 release notes

    Supported software

    There are no required changes to the supported software matrix from 3.0.0.

    NodeCouchDBBrowsersSMS bridgeAndroidmedic-androidmedic-couch2pg
    8.11+2.1+Chrome 53+, Firefox latestmedic-gateway4.4+0.4.5+3.0+

    Bug fixes

    • cht-core#6700: For targets without idType, the winner emission is not deterministic
    • cht-core#6756: Backwards compatibility for location gathering
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/releases/4.0.0/index.html b/core/releases/4.0.0/index.html index aec327dabe..4c49f0b8dc 100644 --- a/core/releases/4.0.0/index.html +++ b/core/releases/4.0.0/index.html @@ -1,4 +1,4 @@ -4.0.0 release notes | Community Health Toolkit +4.0.0 release notes | Community Health Toolkit

    4.0.0 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    To prepare for this release, please read through our Preparing to Upgrade documentation.

    New architecture

    This is a significant change how the CHT is deployed and updated which brings a wide range of benefits and enables more opportunities for improvements in future releases. Read more about the new architecture.

    • #6189: Implement Architecture v3

    Updated browser and Android requirements

    The requirements of clients (browsers and Android devices) has been updated to allow the CHT to use modern features and benefit from improved performance and security. Before upgrading to CHT v4.0.0 ensure that:

    • All Android devices are running AndroidOS v5.0 or greater. In most cases this will require replacing older devices.
    • All Android devices are running cht-android v1.0 or greater
    • All Android devices are running Android System Webview v100 or greater
    • Browser users have the most recent version of Chrome or Firefox

    All these updates are backwards compatible so these should be rolled out before upgrading the CHT server.

    Upgraded Enketo

    The software the CHT uses to render forms has been updated with more features, better performance, and many bug fixes. This also introduces a few changes to behavior that may require changes to form configuration. To test your forms

    1. Use the latest cht-conf CLI tool to validate your forms. Some additional validations were added to detect issues you may have.
    2. Upgrade a testing instance to 4.0.0 and manually test the forms to ensure they work as expected.
    • #6345: Update to latest enketo-core
    • #7731: Update today XPath function to not return current time

    Secure credentials migration

    Credentials used for external integration via SIH or Outbound Push features are now stored in a different way which requires a manual upgrade step. Read about how to store credentials in 4.0.0 in the API documentation.

    • #5904: Cluster safe credentials

    Removed deprecated features

    Simprints integration and the update_sent_forms transition weren’t being used so the code has been removed.

    • #5939: Remove update_sent_forms transition
    • #7421: Remove simprints code

    UI/UX changes

    None.

    Highlights

    New architecture

    The major feature of CHT v4.0.0 is a new micro service architecture. This has many benefits over the previous architecture outlined in more detail in this forum post, but in summary this allows deployments to scale to many more users than previously, deployments to be updated more easily in future, better monitoring of services, performance gains, removing quite a lot of legacy code, and easier installation.

    • #6189: Implement Architecture v3

    API startup progress screen

    Previously users who tried to log in before server start up had completed would be shown an error page which wasn’t very user friendly. Now users will be shown loading page with details about the current stage of the start up process.

    • #2967: Report API startup progress instead of 502

    And more…

    Features

    • #2967: Report API startup progress instead of 502
    • #7448: Detect unsupported browser
    • #7694: Add xPath function for easily adding years, months, days, etc to date value

    Improvements

    • #6382: Use Noto font on the login page
    • #6564: Have an upper time limit to generating task documents
    • #7689: Set form version when creating contacts
    • #7731: Update today XPath function to not return current time
    • #7761: Don’t provide a default password
    • #7802: Allow for dynamically setting container and network names
    • #7859: Improve admin upgrade page UX when upgrade service fails to update containers
    • #7879: Switch staging server to a different URL due to breaking change
    • #7884: Ensure all services are set up to automatically restart

    Security fixes

    • #5904: Cluster safe credentials
    • #7800: Don’t hard code COUCHDB_SECRET in docker compose files

    Performance improvements

    • #5549: Work out what we want to do with Enketo XML, and do that thing
    • #7801: Use local log driver in all Docker containers
    • #7813: Call ‘db.close’ for ephemeral PouchDB objects to avoid memory leaks

    Bug fixes

    • #1495: jr:choice-name works for select_one, not select_multiple
    • #4132: Values of non-relevant fields should be maintained until submission
    • #5636: Xform rendering hides choices when relevance condition and choice filter are applied to a secondary question
    • #5980: Forms show error message upon opening
    • #6589: Video and Audio continues playing even after navigating to next screen
    • #6803: Some Enketo radio buttons don’t get checked styling on first click
    • #7222: Empty integer values in Enketo forms evaluate to 0
    • #7237: Broken functions for xForm repeat_groups
    • #7298: “field required” not shown when field is read_only, required, and has calculation
    • #7608: API wants to build form xmls when forms are deleted
    • #7650: Conflicts diff script crashes because of change in view code
    • #7657: The reports raw content has the label in the wrong place
    • #7676: Zero-width unicode characters removed from SMS messages too often
    • #7690: Uploading a form through the app management GUI does not set the xml version
    • #7759: Timeouts for view requests when syncing can leave changes requests hanging forever
    • #7786: Translation labels missing in default config
    • #7792: Fix openrosa-xpath-evaluator issue with numbers using scientific notation
    • #7832: CERTIFICATE_MODE=OWN_CERT fails with “cannot load certificate”
    • #7840: API credentials endpoint fails CouchDb secret is too short
    • #7850: CouchDb Secret does not get set in single node
    • #7855: When using self signed certs, upgrade page will report “Error when fetching progress” after upgrade was successful

    Technical improvements

    • #4517: Import medic-specific functions from openrosa-xpath-extensions
    • #4874: Remove continuous replication code from the API changes endpoint
    • #5939: Remove update_sent_forms transition
    • #6189: Implement Architecture v3
    • #6345: Update to latest enketo-core
    • #6846: Remove custom translations from Bamanankan
    • #7421: Remove simprints code
    • #7771: enketo-transformer is not compatible with npm 8
    • #7775: Wdio E2E test suite broken on master
    • #7812: Don’t default CouchDB password to empty string
    • #7815: CouchDb /_utils faulty redirect
    • #7817: Update release build schema to v2
    • #7826: Publish branch and alpha docker images in public ECR
    • #7864: Expose CouchDb port 5986 to the docker network

    Contributors

    Thanks to all who committed changes for this release!

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/4.0.1/index.html b/core/releases/4.0.1/index.html index aa49d823a9..7dcbe13142 100644 --- a/core/releases/4.0.1/index.html +++ b/core/releases/4.0.1/index.html @@ -1,4 +1,4 @@ -4.0.1 release notes | Community Health Toolkit +4.0.1 release notes | Community Health Toolkit

    4.0.1 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    None.

    Bug fixes

    • #7912: Calling /api/v1/settings/deprecated-transitions prevents any future transitions to run over new records in API
    • #7918: Error loading forms containing countdown-widget
    • #7933: Error 500 when processing SMS message missing year from Bikram Sambat aggregate

    Contributors

    Thanks to all who committed changes for this release!

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/4.1.0/index.html b/core/releases/4.1.0/index.html index 6546b6958e..3c006c76dd 100644 --- a/core/releases/4.1.0/index.html +++ b/core/releases/4.1.0/index.html @@ -1,4 +1,4 @@ -4.1.0 release notes | Community Health Toolkit +4.1.0 release notes | Community Health Toolkit

    4.1.0 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    Bulk delete of reports redesign

    The bulk delete of Reports tab has been redesigned to allow the user to select multiple reports and delete them. You can read more about it in the Reports tab docs.

    • #7778: Bulk delete of reports redesign

    Highlights

    Support replacing a CHW user without any connectivity

    Offline users can now be replaced on a device so that a new user can use that device without needing to immediately sync with the server. You can read more about it in the transitions configuration docs.

    And more…

    Features

    None.

    Improvements

    • #5903: Users in projects have multiple configured roles
    • #7127: Record device id in feedback docs
    • #7472: Correct typo in rapidpro endpoint path
    • #7525: Register feedback doc when forms fail to load
    • #7592: /api/v1/users returned “type” is only the first role, but many users have multiple roles
    • #7595: Fix breatfeeding typo in default config
    • #7703: Ability to replace a CHW user without any connectivity
    • #7900: Reorder hamburger menu so that logout isn’t next to sync

    Security fixes

    None.

    Performance improvements

    None.

    Bug fixes

    • #6994: Reports with empty patient_id fields don’t link to contact in the UI

    Technical improvements

    • #7001: Remove references to “Travis” from code and readmes
    • #7806: Add e2e to verify breadcrumbs in Message tab and Tasks tab
    • #7841: Refactor privacy policy controller to use template service
    • #7862: Use the right palette variables across webapp
    • #7878: Create base for wdio mobile e2e automation and automate bulk delete
    • #7944: Staging URL placeholder replacement uses old builds database

    Contributors

    Thanks to all who committed changes for this release!

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/4.1.1/index.html b/core/releases/4.1.1/index.html index ca60da8caf..ed86d22354 100644 --- a/core/releases/4.1.1/index.html +++ b/core/releases/4.1.1/index.html @@ -1,4 +1,4 @@ -4.1.1 release notes | Community Health Toolkit +4.1.1 release notes | Community Health Toolkit

    4.1.1 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    None.

    Features

    • #7712: Add moment and datepicker translations for Luganda

    Technical improvements

    • #8110: E2E test failing when next year is leap year
    • #8112: Integration E2E test failing when next year is leap year

    Contributors

    Thanks to all who committed changes for this release!

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/4.1.2/index.html b/core/releases/4.1.2/index.html index 086ea52721..c25a37cdad 100644 --- a/core/releases/4.1.2/index.html +++ b/core/releases/4.1.2/index.html @@ -1,4 +1,4 @@ -4.1.2 release notes | Community Health Toolkit +4.1.2 release notes | Community Health Toolkit

    4.1.2 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    None.

    Bug fixes

    • #8173: API Changes watcher skips changes - or becomes blocked
    • #8205: Nginx can’t connect to API after container restarts because of dynamic IP allocation

    Contributors

    Thanks to all who committed changes for this release!

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/4.2.0/index.html b/core/releases/4.2.0/index.html index 72ce0d3599..17523131c4 100644 --- a/core/releases/4.2.0/index.html +++ b/core/releases/4.2.0/index.html @@ -1,4 +1,4 @@ -4.2.0 release notes | Community Health Toolkit +4.2.0 release notes | Community Health Toolkit

    4.2.0 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    Floating Action Button

    The additive actions (creating reports, places, people, etc…) have moved from the bottom action bar to a Floating Action Button that opens a menu with all actions. This change aligns the CHT more closely with Android UX and material design patterns, and applies to the Messages, Reports, and Contacts tab.

    NOTE: The bottom action bar can be temporarily re-enabled for specific users by granting them the can_view_old_action_bar permission; however, it will be completely removed in a future release. See the Feature Flags documentation for more information.



    • #7998: Move additive actions from Action Bar to Floating Action Button

    More Options menu

    The Export, Edit, and Delete actions have moved from the bottom action bar to a new More Option (⋮) menu located on the top right side of the screen. This change aligns the CHT more closely with Android UX and material design patterns, and applies to the Messages, Reports, and Contacts tab.

    NOTE: The bottom action bar can be temporarily re-enabled for specific users by granting them the can_view_old_action_bar permission; however, it will be completely removed in a future release. See the Feature Flags documentation for more information.


    • #7872: Move non-additive actions from Action Bar to “More Options” menu

    Highlights

    Training Cards

    A new feature was added to help health workers learn about changes to their CHT app remotely, directly in their app. Training Card content might include information about a newly deployed feature, changes to a care guide, or simply a reminder about an underused feature or workflow. You can read more about it in the Training Cards documentation and find some template content in the resources section on the documentation site to help you get started with Training Cards.


    • #6598: In app “What’s new” training cards

    Support automatically creating users when contact is added

    When configured, users will automatically be created when certain types of person contacts are added. No intervention from a system administrator is required in this process.

    For example, a supervisor could onboard a new CHW just by creating a new person contact for them with a “create contact” form. The new user for the CHW will automatically be created and the CHW will receive an SMS message containing the token login link. This link will allow them to login as the newly created user.

    You can read more about it in the transitions configuration docs!

    • #7753: Allow supervisors to create health workers

    Extension libraries

    The extension libraries are blocks of code that are cached with the CHT web application giving app developers a powerful tool to extend the CHT. An example of a use for this feature is to provide a function to calculate a risk score based on a machine learning model. The function can then be called passing in values from app forms and return the result to be stored with the report.

    Read more about this feature in the extension libraries docs

    • #7824: Provide a general integration point of arbitrary JS in forms via config

    Initial replication improvements

    After refactoring the initial replication mechanism, which is the process of synchronizing data for the first time between the user’s device and the app’s server, it is now at least 8 times faster than before. Look at this performance comparison table:

    No. concurrent usersBefore (median)After (median)
    1251s18s
    1086s11s
    10025m2.5m
    • #8134: Refactor initial replication to not use PouchDb replicate

    And more…

    Features

    • #6598: In app “What’s new” training cards
    • #7824: Provide a general integration point of arbitrary JS in forms via config

    Improvements

    • #7753: Allow supervisors to create health workers
    • #7998: Move additive actions from Action Bar to Floating Action Button
    • #7872: Move non-additive actions from Action Bar to “More Options” menu “⋮”
    • #7902: Incorrect image tag on release
    • #7926: Show a more useful error message when decrypting using a different secret
    • #7947: Improve wording of the warning when upgrading
    • #7964: Use _all_docs to get forms documents in API
    • #8009: Display version and build number separately in the admin upgrade page
    • #8093: Prevent data loss through re-sync when restoring data from backup
    • #8136: Add timestamp information in feedback document log events
    • #8210: Change extension libs API so libs are modules not functions
    • #6281: Allow app builders to specify disabled languages in config
    • #8134: Refactor initial replication to not use PouchDb replicate

    Security fixes

    None.

    Performance improvements

    None.

    Bug fixes

    • #7425: Schedules don’t have a default value for start_from
    • #7602: Race condition in db-object-widget
    • #7603: Race condition in unloading contact content
    • #7642: Successful users-info request reported as errored
    • #7892: Enketo selected option highlight color changes incorrectly on drag
    • #7910: Confirmation prompt for “Cancel” button in forms
    • #7925: Bikram Sambat date should be validated when users are editing a report and enter an invalid date
    • #7949: Nginx Environment Variables not passed along
    • #7953: 4.x: Version deployed still visible in App Management release list
    • #7957: Sentinel erroneously reporting target for outbound push has expired TLS cert
    • #7976: Unhandled rejection when favicon not found
    • #7986: Container names issue for clustered couchdb setup when running across different nodes
    • #8040: API Crashes on malformed translation docs
    • #8154: Race condition during sentinel startup
    • #8144: Secondary risk factors are not returned if primary risk factors are present in getRiskFactorsFromPregnancy
    • #8130: Haproxy stops routing during stress test
    • #225: Ability to run Tasks.js and Targets.js code without Nools
    • #8205: Nginx can’t connect to API after container restarts because of dynamic IP allocation
    • #8213: Contact and Messages Tab: footer button scrolls with content, not staying pinned
    • #8166: Haproxy crashes instance during scalability test due to high memory usage
    • #8173: API Changes watcher skips changes - or becomes blocked

    Technical improvements

    • #7372: Update mocha to latest
    • #7901: Upgrade test fails release
    • #7905: GH Actions deprecation warning
    • #7952: Change ECR repo alias to medic
    • #7962: Change CouchDb service names to match all hostname specs
    • #7966: Flaky e2e test Submit Default Delivery Report
    • #7980: create_user_for_contacts unit test flakes
    • #7992: Upgrade webapp’s angular to v15.x.x
    • #8060: Update WDIO to latest
    • #8062: Fix e2e test: Should verify that all tasks related with the high risk pregnancy were created
    • #8067: CouchDB entry point always saves couch_httpd_auth and couchdb to cluster-credentials.ini
    • #8076: Update haproxy
    • #8077: Add wdio retries
    • #8103: Protractor test failures are not retried and failed retries clear results from previous failure
    • #8151: E2E builds fail for external contributors because of allure history jobs
    • #8099: Enable easy integration with certbot and nginx container
    • #8024: Update scalability suite to work with 4.x

    Contributors

    Thanks to all who committed changes for this release!

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/4.2.1/index.html b/core/releases/4.2.1/index.html index 72f30ee6c8..4dd84e2f35 100644 --- a/core/releases/4.2.1/index.html +++ b/core/releases/4.2.1/index.html @@ -1,4 +1,4 @@ -4.2.1 release notes | Community Health Toolkit +4.2.1 release notes | Community Health Toolkit

    4.2.1 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    None.

    Improvements

    • #8214: Align nginx and ALB timeout values

    Contributors

    Thanks to all who committed changes for this release!

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/4.2.2/index.html b/core/releases/4.2.2/index.html index c81dcfe17b..7f939843d9 100644 --- a/core/releases/4.2.2/index.html +++ b/core/releases/4.2.2/index.html @@ -1,4 +1,4 @@ -4.2.2 release notes | Community Health Toolkit +4.2.2 release notes | Community Health Toolkit

    4.2.2 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    None.

    Bug fixes

    • #8161: Telemetry doc metadata.versions.app is unknown for all telemetry docs
    • #8359: Infinite scrolling not working on Contacts tab

    Contributors

    Thanks to all who committed changes for this release!

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/4.2.3/index.html b/core/releases/4.2.3/index.html index 2ed4591171..388f53fe40 100644 --- a/core/releases/4.2.3/index.html +++ b/core/releases/4.2.3/index.html @@ -1,4 +1,4 @@ -4.2.3 release notes | Community Health Toolkit +4.2.3 release notes | Community Health Toolkit

    4.2.3 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    None.

    Bug fixes

    • #8539: Sentinel stuck in infinite loop when 100 sequential deletions fail to generate tombstones

    Technical improvements

    • #8558: Skip Protractor e2e tests suites on old supported branches

    Contributors

    Thanks to all who committed changes for this release!

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/4.2.4/index.html b/core/releases/4.2.4/index.html index 6ae3fe96ef..1e2e7c953c 100644 --- a/core/releases/4.2.4/index.html +++ b/core/releases/4.2.4/index.html @@ -1,4 +1,4 @@ -4.2.4 release notes | Community Health Toolkit +4.2.4 release notes | Community Health Toolkit

    4.2.4 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    None.

    Bug fixes

    • #7670: Same patient id assigned to multiple patients in a same instance
    • #8576: Low performance in Reports tab for users with thousands of places
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/4.3.0/index.html b/core/releases/4.3.0/index.html index bf181bbef3..d053fd5e11 100644 --- a/core/releases/4.3.0/index.html +++ b/core/releases/4.3.0/index.html @@ -1,9 +1,9 @@ -4.3.0 release notes | Community Health Toolkit +4.3.0 release notes | Community Health Toolkit

    4.3.0 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Continuous downwards replication (the algorithm through which offline users download docs from the server) has been completely rewritten. This change required a high number of view updates, which implies that staging this upgrade and indexing views before upgrading will be a lengthy process - depending on the size of the database. Additionally, the server might need additional storage while this process is ongoing. + Create project issue

    4.3.0 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Continuous downwards replication (the algorithm through which offline users download docs from the server) has been completely rewritten. This change required a high number of view updates, which implies that staging this upgrade and indexing views before upgrading will be a lengthy process - depending on the size of the database. Additionally, the server might need additional storage while this process is ongoing. Users will receive the usual upgrade popup, and they will need to reload the app in order to resume replication.

    Breaking changes

    None.

    UI/UX changes

    None.

    Highlights

    Adding phone numbers to patients when registering via SMS

    The registration transition now supports storing a phone number field for new patients. This is achieved through an additional field in the SMS form, which can be configured to be parsed and validated as a correct and, optionally unique, phone number. You can read more about it in the registration configuration

    Performance improvement for downwards replication

    Downwards replication has been completely rewritten to improve performance, reduce complexity, remove the necessity of deleted documents tombstones and progress towards supporting eventual consistency, in order to allow for the CHT to work seamlessly with CouchDb high availability setups (multiple replicas). There are no UI/UX changes associated with this update, however users will need to reload the app after the upgrade was successful and they receive the notification popup, in order to resume replication using the new algorithm. The endpoints that the previous replication algorithm was using have been updated or disabled.

    Look at this performance comparison table:

    # docs# purged docs# docs on device# downloaded docsBefore(avg)After(avg)Difference
    1100001100001.4s2.1s0.6
    3700003700005s8s0.6
    58000058000012s10s1.2
    580002500033000010s11s0.8
    58000250003300050039s14s2.7
    580004300015000010s11s0.8
    58000430001500050034.5s15.5s2.2
    580004300015000100060s15.5s3.7

    New Prometheus API endpoint

    A new API endpoint now allows exporting API request performance. The output is formatted for the Prometheus Data Model. The endpoint is available without requiring authentication and is used by CHT-Watchdog. More details are available in API reference docs.

    And more…

    Features

    • #8204: Support adding phone number when registering patients via SMS
    • #8311: Show password when clicking on the eye icon
    • #8328: Show reports of online user’s associated place and below
    • #8426: Expose an API with endpoint performance metrics

    Improvements

    • #7163: Make the CHT Script API available for Purge.js
    • #8039: Allow users to report client-side crashes via screenshots
    • #8162: Monitoring sentinel when “Transitions are disabled” is difficult
    • #8185: Remove Guided Tour and Welcome message
    • #8280: Set default CouchDb log level to info or debug
    • #8389: Improve Nepali translation
    • #8416: Include formatted date in API morgan logging
    • #8447: Support Nepali digits in Medic ID and LMP Date

    Security fixes

    • #6505: Prevent users from opening and filling forms they’re not authorized to see/fill
    • #8335: Use modern TLS versions

    Performance improvements

    • #8296: Refactor downwards continuous replication following the Nairobi protocol

    Bug fixes

    • #6963: Mark for outbound might create tasks for already sent jobs
    • #7250: Race condition in loading contacts can cause previously selected contact to load
    • #7356: Online user stuck in loading screen when offline
    • #7363: Crash in Tasks/Targets system not creating feedback documents
    • #7651: Tasks content page sometimes throws ExpressionChangedAfterItHasBeenCheckedError
    • #8022: Angular’s ExpressionChangedAfterItHasBeenCheckedError exception in Reports and Contacts tabs
    • #8160: Duplicate outbound requests are sent when a document matches multiple config options
    • #8242: Hovering over dates throws error, and default tooltip is displayed
    • #8355: Fix reports review title in modal
    • #8371: Upgrade cannot be initiated when there are no past upgrade_log entries
    • #8385: Show password icon not cached by service-worker
    • #8396: Next page not loading properly after clicking on the back button
    • #8401: Clicking on report case_id does not apply search filter
    • #8457: API fails to start because of large form attachments exceed POST allowed payload
    • #8463: Offline users stuck on spinner on load until certain views are indexed

    Technical improvements

    • #7017: Use the Node eslint plugin to lint api and sentinel
    • #7796: Clean up gruntfile obsolete tasks
    • #8150: Investigate npm workspaces for monorepo
    • #8184: Update initial replication scalability suite post initial replication rewrite
    • #8190: Remove configuration wizard
    • #8244: Upgrade e2e test fails on betas
    • #8252: Flaky: ci-e2e-integration - token login
    • #8273: Add git pre-commit hooks to stop accidental pushes to master as GitHub admins
    • #8281: Change upgrade e2e test to start upgrade from latest release instead of master
    • #8317: Split up the ci-webdriver-default action
    • #8367: Webapp watch not publishing changes
    • #8404: Flaky test: API changes feed should respond to changes even after services are restarted
    • #8412: Flaky test: Submit a death report Should verify that the counter for the Deaths was updated.
    • #8420: Flaky test: ongoing replication “before all” hook for ongoing replication

    Contributors

    Thanks to all who committed changes for this release!

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/releases/4.3.1/index.html b/core/releases/4.3.1/index.html index 4a80048399..9b0a325457 100644 --- a/core/releases/4.3.1/index.html +++ b/core/releases/4.3.1/index.html @@ -1,4 +1,4 @@ -4.3.1 release notes | Community Health Toolkit +4.3.1 release notes | Community Health Toolkit

    4.3.1 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    None.

    Bug fixes

    • #8478: Invalid phone for patient still running triggers

    Contributors

    Thanks to all who committed changes for this release!

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/4.3.2/index.html b/core/releases/4.3.2/index.html index 36b2fb45d2..ecd4fd32ad 100644 --- a/core/releases/4.3.2/index.html +++ b/core/releases/4.3.2/index.html @@ -1,4 +1,4 @@ -4.3.2 release notes | Community Health Toolkit +4.3.2 release notes | Community Health Toolkit

    4.3.2 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    None.

    Bug fixes

    • #7670: Same patient id assigned to multiple patients in a same instance
    • #8576: Low performance in Reports tab for users with thousands of places
    • #8589: Users unable to edit the report they created
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/4.4.0/index.html b/core/releases/4.4.0/index.html index c3ab5a659f..0428f36b9a 100644 --- a/core/releases/4.4.0/index.html +++ b/core/releases/4.4.0/index.html @@ -1,4 +1,4 @@ -4.4.0 release notes | Community Health Toolkit +4.4.0 release notes | Community Health Toolkit

    4.4.0 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    The deprecated hardcoded national_admin role no longer behaves as a CouchDb admin. The role now behaves as an ordinary online user role, with no additional implicit permissions. + Create project issue

    4.4.0 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    The deprecated hardcoded national_admin role no longer behaves as a CouchDb admin. The role now behaves as an ordinary online user role, with no additional implicit permissions. Configurations using national_admin role can either switch to using a CouchDb admin for admin-only tasks and/or granting more permissions to national_admin.

    Breaking changes

    None.

    UI/UX changes

    CHT dialogs.

    CHT dialogs were updated to align with Material guidelines. This includes updating the visual design and text of dialogs to align with best practices.


    As part of this work, some translation keys changed. We have provided translations for all of the languages officially supported by CHT (English, Spanish, French, Indonesian, Hindi, Nepali, Bambara, Swahili). If you have a custom language and have any of these features, you will need to translate these in your configuration files.

    • Bulk delete reports dialog

      • bulkdelete.confirm.title: used for the modal title when deleting one report
      • [NEW] bulkdelete.confirm.title.plural: used for the modal title when deleting multiple reports.
    • Edit SMS message group dialog

      • [NEW] edit_message_group.modal.title: used for the modal title
    • Report a bug dialog

      • [NEW] feedback.modal.submit: used for the modal submit button label.
    • Navigation when form not saved dialog

      • [NEW] confirm.destructive.navigation.title: used for the modal title
      • [NEW] confirm.destructive.navigation.submit: used for the modal submit button label.
    • Training cards dialog

      • [NEW] training_cards.confirm.title: used for the modal title
    • Confirmation when verifying a report dialog

      • [NEW] confirm.verification.title: used for the modal title
      • [NEW] confirm.verification.submit: used for the modal submit button label.
    • #8390: Align “dialog” components with Material design guidelines

    Highlights

    Upgrading CouchDb to v3.3.2

    CouchDb 3 was released in 2020. It packs security fixes, performance and scalability improvements and a plethora of bug fixes.

    Most notable features are:

    1. Improved clustering and node management APIs
    2. Adding the _reshard endpoint, which allows splitting a shard into two shards. In CouchDb v2, adding additional shards was not possible without creating a new database with the desired number of shards, and replicating existent data to the new database. For the CHT, this meant that all users would need to re-sync if additional shards were added.
    3. Adding a new _prometheus endpoint, which exposes CouchDb metrics and can be used out of the box in monitoring visualisation tools, like the CHT Watchdog.
    4. Improved security, where database creation and writes/reads are no longer allowed for all users by default.
    5. Improved security, where only CouchDb admins are allowed to add/edit/delete users.
    6. Dramatically improved multi-core CPU usage, resulting in better performance when running on powerful machines. This results in a 1.5x improvement of request time across the board, with some endpoints being up to 10x faster.

    Replication performance improvement by persistent caching of purged doc ids

    CouchDb request performance analysis has revealed that approximately 25% of CPU time was used querying to determine which documents have been purged for users that are replicating. Because purging is an action that runs on a schedule, relatively infrequently due to how performance expensive it is, caching results from these very expensive queries significantly improves server-side performance.

    As of 4.4.0, the CHT caches the purged doc ids for each user that replicates. The cache is reset when purging runs next time and when user replication settings change. This means that, for every connected user, purged docs are only queried once per purging timeframe, when the user first syncs or replicates.

    This leads to significant improvements in subsequent replication requests.

    4.3.0 vs 4.4.0 Average Synchronization Times

    Features

    None.

    Improvements

    • #6514: Migrate from SW-Precache to Workbox
    • #8179: nginx and haproxy should respect Accept headers when responding with error templates
    • #8390: Align “dialog” components with Material design guidelines
    • #8442: Telemetry geolocation:failure events for geolocation should be unique
    • #8498: Remove targets disable admin option
    • #8506: Update browser support notice to match official versions
    • #8511: Update configuration deployment action to use latest cht-conf

    Security fixes

    • #8443: Masking credentials

    Performance improvements

    • #6289: Upgrade to CouchDB v3
    • #8238: Figure out a different way to use purged docs cache
    • #8451: Add support for HTTP/2 in nginx

    Bug fixes

    • #6180: Any text containing back ticks looks weird in the webapp
    • #8081: Add contact_type and place_id to contacts exported by /api/v2/export/contacts
    • #8243: Can’t turn off permissions for national_admin role in admin app
    • #8490: API crashes when CouchDB is overloaded
    • #8527: Unable to build nginx with symlinks in some environments

    Technical improvements

    • #6132: Files containing tests should use the .spec.js extension
    • #8306: Replace grunt with npm scripts
    • #8414: Flaky test: sms-gateway api returns list and updates state
    • #8459: Update nginx to latest
    • #8469: Scalability suite fails because Jmeter version no longer exists
    • #8494: Update lodash
    • #8516: Remove unused /api/auth/ endpoint
    • #8518: Encode parameters being used in credentials url
    • #8522: Flaky test: More Options Menu - Offline User all permissions disabled
    • #8524: Remove sub-scripts from package.json
    • #8533: Ignore unimportant step failures in CI build

    Contributors

    Thanks to all who committed changes for this release!

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/core/releases/4.4.1/index.html b/core/releases/4.4.1/index.html index 43e49e47d6..00ce23947c 100644 --- a/core/releases/4.4.1/index.html +++ b/core/releases/4.4.1/index.html @@ -1,4 +1,4 @@ -4.4.1 release notes | Community Health Toolkit +4.4.1 release notes | Community Health Toolkit

    4.4.1 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    None.

    Bug fixes

    • #7670: Same patient id assigned to multiple patients in a same instance
    • #8576: Low performance in Reports tab for users with thousands of places
    • #8589: Users unable to edit the report they created

    Contributors

    Thanks to all who committed changes for this release!

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/4.4.2/index.html b/core/releases/4.4.2/index.html index 36d67876a5..f20456af2c 100644 --- a/core/releases/4.4.2/index.html +++ b/core/releases/4.4.2/index.html @@ -1,4 +1,4 @@ -4.4.2 release notes | Community Health Toolkit +4.4.2 release notes | Community Health Toolkit

    4.4.2 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    None.

    Bug fixes

    • #8745: error.loading.form.no_authorized when opening a form from tasks tab because context evaluates to false

    Contributors

    Thanks to all who committed changes for this release!

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/4.5.0/index.html b/core/releases/4.5.0/index.html index 5676219abb..e67f31b31e 100644 --- a/core/releases/4.5.0/index.html +++ b/core/releases/4.5.0/index.html @@ -1,4 +1,4 @@ -4.5.0 release notes | Community Health Toolkit +4.5.0 release notes | Community Health Toolkit

    4.5.0 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    • #8513: Align “Target” components with Material design guidelines
    • #8541: Options with long names look wrong in enketo selects

    Highlights

    Schedules can be created by passing array on start_from

    It is now possible to configure the start_from property to accept array of fields while configuring SMS schedules. This allows users to pass multiple fields and the first existing field will be used to create schedules. Read more about schedules in the documentation.

    Target components are updated to match material design

    The targets UI has been updated to match material design. This is the next step in transitioning to using a modern and intuitive design throughout the UI. Many other components were updated in 4.4.0.


    Outbound tasks can be configured on a schedule

    The CHT now supports scheduling when outbound push tasks are run based on a cron expression. Read about how to set this up in the outbound push documentation.

    Features

    None.

    Improvements

    • #8229: Send outbound tasks on a schedule
    • #8433: Add Enketo telemetry events for contact forms
    • #8513: Align “Target” components with Material design guidelines
    • #8633: Allow schedule to start with multiple fields

    Security fixes

    • #8564: Improve masking of passwords in log files

    Performance improvements

    • #8576: Low performance in Reports tab for users with thousands of places

    Bug fixes

    • #7148: Document update conflict reported in feedback docs
    • #7616: Telemetry records not logged for every day FCHVs are active
    • #7670: Same patient id assigned to multiple patients in a same instance
    • #8089: Caught errors do not generate feedback documents.
    • #8471: Filter section on Reports tab not closing on first tap
    • #8502: Upgrading instance through webapp fails for the first time.
    • #8520: Action button uses wrong font
    • #8541: Options with long names look wrong in enketo selects
    • #8566: Sentinel stuck in infinite loop when 100 sequential docs fail to execute transitions
    • #8589: Users unable to edit the report they created
    • #8669: Enketo labels are not interactable
    • #8686: Fix target UI to match Figma

    Technical improvements

    • #7370: Uplift messageformat to @messageformat/core
    • #7600: Add additional tests for Enketo Form functionality
    • #8237: Upgrade webapp to Angular 16
    • #8375: Flaky test: Muting for an offline user
    • #8501: Remove upgrade e2e’s temporary modal logic
    • #8535: Flaky e2e test: Testing Incorrect locale should work with incorrect locale
    • #8538: Flaky test: Service worker cache adding new languages triggers login page refresh
    • #8549: Update dependencies in scripts folder
    • #8554: Angular testing passes but logs thousands of lines of errors
    • #8556: xpath extensions tests fail locally in my timzone
    • #8578: Scalability workflow failed waiting for CHT to be up.
    • #8582: improve nginx config to avoid “listen … http2 directive is deprecated” warning
    • #8583: Ongoing replication e2e test is flaky
    • #8592: Flaky e2e integration test: Sentinel transition error log error for unknown transition:
    • #8621: Update WDIO
    • #8622: Update sinon and rewire
    • #8671: Flaky test: feedback docs should stop creating feedback docs once the db has over 1000 feedback docs
    • #8685: Chrome 118 broke e2e testing
    • #8692: Flaky e2e test: Default navigation hamburger menu spec failing: Hamburger Menu tests “before all” hook

    Contributors

    Thanks to all who committed changes for this release!

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/4.5.1/index.html b/core/releases/4.5.1/index.html index f35a48c368..4c5ee16e88 100644 --- a/core/releases/4.5.1/index.html +++ b/core/releases/4.5.1/index.html @@ -1,4 +1,4 @@ -4.5.1 release notes | Community Health Toolkit +4.5.1 release notes | Community Health Toolkit

    4.5.1 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    None.

    Bug fixes

    • #8745: error.loading.form.no_authorized when opening a form from tasks tab because context evaluates to false

    Contributors

    Thanks to all who committed changes for this release!

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/4.5.2/index.html b/core/releases/4.5.2/index.html index fbbd954929..795fd4d1e0 100644 --- a/core/releases/4.5.2/index.html +++ b/core/releases/4.5.2/index.html @@ -1,4 +1,4 @@ -4.5.2 release notes | Community Health Toolkit +4.5.2 release notes | Community Health Toolkit

    4.5.2 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    None.

    Bug fixes

    • #8837: Telemetry date not logged on telemetry documents

    Contributors

    Thanks to all who committed changes for this release!

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/4.6.0/index.html b/core/releases/4.6.0/index.html index 4a33cef7bb..3944c1a0bc 100644 --- a/core/releases/4.6.0/index.html +++ b/core/releases/4.6.0/index.html @@ -1,4 +1,4 @@ -4.6.0 release notes | Community Health Toolkit +4.6.0 release notes | Community Health Toolkit

    4.6.0 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    • #6177: Improve look and utility of the “About” page
    • #7770: Browser compatibility modal notice for Chrome version 75-90
    • #8075: Update default branding to CHT logo
    • #8660: Link to Contact’s Profile from Messages tab

    Highlights

    Allow contact searches in forms to be filtered by descendants of the current contact

    A contact selector can be used in forms to allow users to select a contact by searching. In addition to limiting the searchable contacts by their type, now you can configure the search field to only show contacts which are descendants of the current contact. This is useful when you only want to allow a user to select specific contacts (such as members of the current household). Learn how to configure this functionality in the documentation.

    • #8074: Support filtering contact search in forms by descendants of the current contact

    Official configuration for deploying the CHT to Kubernetes

    Configuration files and scripts are now available for deploying the CHT to a Kubernetes cluster. This includes Helm charts, Kubernetes templates, and shell scripts for managing a deployment. See the README documentation for more information.

    • #8695: Provide kubernetes configuration and helm charts for production deployments

    Form performance improvements

    Performance of CHT forms has been significantly improved! Particularly, large forms with complex calculations will load considerably faster and be more responsive. In our tests, we observed ~60% improvement in load times for a large form when compared with 3.15.0 (and a ~33% improvement over 4.5.0). Other partners have also observed similar results!

    • #7599: Update to latest enketo-core

    And more…

    Features

    • #8074: Support filtering contact search in forms by descendants of the current contact
    • #8695: Provide kubernetes configuration and helm charts for production deployments
    • #8846: Add the aggregate date to telemetry’s meta

    Improvements

    • #6177: Improve look and utility of the “About” page
    • #7462: Make code for Enketo forms reusable outside cht-core
    • #7770: Browser compatibility modal notice for Chrome version 75-90
    • #8075: Update default branding to CHT logo
    • #8293: Add more debug information to CHT4 Docker Helper
    • #8660: Link to Contact’s Profile from Messages tab

    Security fixes

    • #6530: Add rate limiting to authentication endpoints
    • #8843: Add script to bulk change list of users passwords

    Performance improvements

    • #7599: Update to latest enketo-core
    • #8771: Update task expiration recalculation queries

    Bug fixes

    • #6299: Sync status sometimes says all reports synced when there are changes yet to sync
    • #6395: Pregnancy registration allows first pregnancy and previous miscarriage risk factors
    • #7110: Homeplace in LHS contacts list is not updated on changes
    • #7288: Searching for a contact name that has a short value will return no results.
    • #7674: Answers to non-relevant questions in forms are not immediately cleared with new Enekto
    • #8002: Bullets not displaying properly in form labels
    • #8038: Admin app fails to get releases when a different staging server is passed through ENV to API
    • #8096: Admin password change breaks CHT, shows wrong error message
    • #8102: Crash in enketo-core - TypeError: Cannot read property ’length’ of undefined
    • #8118: “Send Message” action shouldn’t send message to user that is logged in
    • #8131: Training cards are appearing on top of privacy policies
    • #8585: Asterisk for required field in form located on new line when in summary group
    • #8644: cht-healthcheck stays down after ConnectionRestError
    • #8674: Cannot create user for contacts created via Place Api
    • #8689: Users API not responsive
    • #8730: Actionbar does not include links to create people when users have only the can_create_people permission
    • #8777: Error recording telemetry on Firefox
    • #8778: Error in map/reduce function when aggregating telemetry
    • #8790: /api/deploy-info.version is not semver valid for final releases
    • #8796: api/v1/users-doc-count doesn’t work as expected
    • #8868: Session requests failing after upgrade

    Technical improvements

    • #7167: Upgrade CHT-Conf version in CHT-Core
    • #7993: Upgrade to Node 20
    • #8357: Flaky test: Enabling/disabling languages should disable a language and enable another
    • #8431: Create an e2e test for receiving phone number in SMS
    • #8697: Default config: Consolidate all the enketo commons selectors
    • #8706: Speed up build execution by breaking up longest running jobs
    • #8707: Make update compose URLs action more reliable
    • #8708: Remove separate action for “Test nginx and haproxy”
    • #8727: Upgrade e2e test fails on MacOS
    • #8739: Infinite scrolling e2e test failing with 429 Too Many Requests
    • #8743: Upgrade to couchdb 3.3.3
    • #8757: Remove standard config
    • #8780: Deprecated use of fs.rmdir with recursive
    • #8783: Sometimes manifest.json is empty
    • #8789: Target aggregates test is passing but shouldn’t be
    • #8791: Update deprecated functions used in generate real-world data script
    • #8819: The ci-webdriver-default-workflows job is really slow sometimes
    • #8855: Integration test should display deploy-info to authenticated users is failing on TAG pushes
    • #8263: Kubernetes configuration, Helm Chart templates and a One-Shot Script that Deploys the CHT Locally on a k3d Cluster
    • #8683: Add implicit-arrow-linebreak to eslint
    • #8710: Fix flaky test - permission-enabled and incorrect-locale
    • #8711: Remove script/deploy dir so it can be re-added under correct author
    • #8723: Add new local integration jobs
    • #8729: Retry publish on conflict when testing
    • #8786: Enable alwaysStrict in webapp tsconfig
    • #8795: Remove unused imports and variables
    • #8800: Add nyc test coverage for API and Sentinel
    • #8808: Add an explanation message to 2 assertions in routing.spec.js
    • #8811: Add coverage to all shared libs tests

    Contributors

    Thanks to all who committed changes for this release!

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/4.7.0/index.html b/core/releases/4.7.0/index.html index 8299bf0a9a..c6bff34954 100644 --- a/core/releases/4.7.0/index.html +++ b/core/releases/4.7.0/index.html @@ -1,9 +1,9 @@ -4.7.0 release notes | Community Health Toolkit +4.7.0 release notes | Community Health Toolkit

    4.7.0 release notes

    Known issues

    4.7.0 release notes

    Known issues

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    • #5807: Fix widget wrapping when displaying multiple columns. This is a minor stylistic change that does not require retraining.
    • #8858: Align cards to Material. This is a minor stylistic change that does not require retraining.

    Highlights

    Support getting the difference between two dates in various time formats

    There are four new functions that allow you to get the difference between two dates in various time formats. The functions are:

    • cht:difference-in-days
    • cht:difference-in-weeks
    • cht:difference-in-months
    • cht:difference-in-years

    #8971: Support getting the difference between two dates in various time formats

    Countdown widgets can now be made required in forms

    Countdown widgets can now be made required in forms. This means that the form will not be able to be submitted until the countdown widget has been completed.

    #8681: Support making countdown-widgets required in forms

    And more…

    Features

    • #8462: Expose user device details to admins
    • #8681: Support making countdown-widgets required in forms
    • #8821: Record Apdex score in webapp
    • #8877: /api/v2/users look up users by facility_id and/or contact_id
    • #8971: Support getting the difference between two dates in various time formats
    • #8986: /api/v2/users look up data for single user

    Improvements

    • #5807: Fix widget wrapping when displaying multiple columns
    • #8591: Add SNI details for use in e2e HTTPS environment with TLS termination external to server (eg, kubernetes pod)
    • #8844: Error page doesn’t have a way out or retry option
    • #8858: Align cards to Material
    • #8883: Translate to Spanish
    • #8917: Support Android validation URL endpoints for registering an intent
    • #8925: Enable http-keep-alive in haproxy
    • #8950: Add Swahili Translations L400 to L600
    • #8951: Add Swahili Translations L600 to L800
    • #8952: Add Swahili Translations L800 to L1000
    • #8953: Add Swahili Translations L1000 to L1200
    • #8954: Add Swahili Translations L1200 to last line
    • #8955: Translate to Spanish 300 to 600
    • #8956: Translate to Spanish 600 to 900
    • #8957: Translate to Spanish 900 to 1200
    • #8958: Translate to Spanish 1200 to last line
    • #9012: Save date when transition ran in document infodoc
    • #9023: Remove server check for node version

    Security fixes

    None.

    Performance improvements

    • #8994: Improve Apdex score in contact details page

    Bug fixes

    • #6383: Patient id not populated in reference app
    • #8085: Race condition: Tasks list briefly shows “No more tasks” before displaying available tasks.
    • #8815: Only last 50 reports for contact are provided to contact-summary calculation
    • #8841: Docker Helper error on macOS Apple Silicon: “qemu: uncaught target signal 11 (Segmentation fault)”
    • #8939: Select question grid display in forms broken
    • #8940: Admin app shows error after successful upgrade
    • #8985: Cannot use /api/v1/people to create a person if any place in the lineage has an invalid primary contact

    Technical improvements

    • #8802: Flaky e2e test ongoing replication should download settings updates
    • #8803: Reduce cognitive complexity of formatContacts function
    • #8881: Builds are failing because Webdriver cannot download Chromedriver
    • #8901: CI build for a branch with an underscore fails
    • #8902: External contributor builds fail as unable to find docker images
    • #8944: Use performance service to record performance telemetry metrics.
    • #8962: Scalability tests broken
    • #8982: Update to Angular 17
    • #9035: GitHub artifact being overwritten due to naming collision for minimum browser e2e tests
    • #9042: Upgrade CI workflow fails with image rate limit

    Contributors

    Thanks to all who committed changes for this release!

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/4.7.1/index.html b/core/releases/4.7.1/index.html index 890e1c7664..9fbc375724 100644 --- a/core/releases/4.7.1/index.html +++ b/core/releases/4.7.1/index.html @@ -1,4 +1,4 @@ -4.7.1 release notes | Community Health Toolkit +4.7.1 release notes | Community Health Toolkit

    4.7.1 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    None.

    Bug fixes

    • #9117: Impossible to upgrade from 4.7.0

    Technical improvements

    None.

    Contributors

    Thanks to all who committed changes for this release!

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/4.7.2/index.html b/core/releases/4.7.2/index.html index bdd51207ad..706eb9a22f 100644 --- a/core/releases/4.7.2/index.html +++ b/core/releases/4.7.2/index.html @@ -1,4 +1,4 @@ -4.7.2 release notes | Community Health Toolkit +4.7.2 release notes | Community Health Toolkit

    4.7.2 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    None.

    Bug fixes

    • #9166: “users-meta failed with compilation_error” when upgrading from 4.2.4
    • #9187: Haproxy unit tests are failing due to haproxy patch release

    Technical improvements

    None.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/4.8.0/index.html b/core/releases/4.8.0/index.html index 2ec65605b5..9563276469 100644 --- a/core/releases/4.8.0/index.html +++ b/core/releases/4.8.0/index.html @@ -1,4 +1,4 @@ -4.8.0 release notes | Community Health Toolkit +4.8.0 release notes | Community Health Toolkit

    4.8.0 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    None.

    Highlights

    Performance

    The Contacts page loads much faster now. Using apdex scores and testing on low spec devices, we were able to identify slow areas and make improvements to both the list view and the detail view.

    Apdex Improvements

    • #9006: Improves performance of the list view by reducing the number of rows fetched each time
    • #9019: Improves performance of the detail view by reducing loops for tasks and reports

    Security

    Two security issues with severity level of “high” and one of “low” were fixed in this release. The long standing security issues were detected during routine penetration tests. We recommend upgrading as soon as possible to get the best possible cybersecurity protection.

    • #8886: Protect against a malicious 3rd party using the redirect after login feature to steal credentials
    • #9108: Ensure users with basic access are not allowed to self-promote themselves to a user with more permissions
    • #9122: Ensure users with basic access are not allowed to sensitive configuration files, like translations

    And more…

    Features

    None.

    Improvements

    • #8884: Add Swahili Translations
    • #9118: Use helm repository for cht-core helm-chart

    Performance improvements

    • #9006: Improve Apdex score of contact_list
    • #9019: Reduce loops when processing tasks and reports

    Bug fixes

    • #8599: CouchDB single-node set-up script fails at creating system db’s if admin pw contains special characters

    Technical improvements

    • #8788: Flaky test: Testing Incorrect locale
    • #8929: Bump update-service-kubernetes in helmchart
    • #9069: Server checks test is emitting 10k lines to the console
    • #9079: Integration tests fail with 420 Too Many Requests when running on a fast machine
    • #9080: Add @medic/logger shared library
    • #9085: add tests/helm/values.yaml to .gitignore
    • #9100: Move cht-form tests from e2e to integration
    • #9113: Add a build step to validate PR titles conform to commitlint standard

    Contributors

    Thanks to all who committed changes for this release!

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/4.8.1/index.html b/core/releases/4.8.1/index.html index ad2fed4b6b..8d1115360d 100644 --- a/core/releases/4.8.1/index.html +++ b/core/releases/4.8.1/index.html @@ -1,4 +1,4 @@ -4.8.1 release notes | Community Health Toolkit +4.8.1 release notes | Community Health Toolkit

    4.8.1 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    None.

    Bug fixes

    • #9166: “users-meta failed with compilation_error” when upgrading from 4.2.4
    • #9187: Haproxy unit tests are failing due to haproxy patch release

    Technical improvements

    None.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/4.9.0/index.html b/core/releases/4.9.0/index.html index 4ac97b94a6..a5bb195dde 100644 --- a/core/releases/4.9.0/index.html +++ b/core/releases/4.9.0/index.html @@ -1,4 +1,4 @@ -4.9.0 release notes | Community Health Toolkit +4.9.0 release notes | Community Health Toolkit

    4.9.0 release notes

    Known issues

    Check the repository for the latest known issues.

    Upgrade notes

    Breaking changes

    None.

    UI/UX changes

    This release adds support for assigning multiple places to users. Users who are configured to have multiple places will see some subtle UI changes as described in the Highlights section. For a video walkthrough of the changes, check out the June 2024 CHT Round-up call and this forum post.

    Highlights

    Allow multiple places to be assigned to users

    To better support Supervisors who manage CHWs across multiple areas, it is now possible to assign more than one “Place” (ie Community Health Unit, Health Center, etc…) to a user. To enable this feature, simply assign the can_have_multiple_places permission to the relevant user role(s) and add the desired “Place(s)” to the user.

    Setup

    If the User’s Role has the appropriate permission, it will be possible to assign multiple Places. Note: Selected Places must be at the same level in the hierarchy.

    IMPORTANT: When removing a Place that was previously assigned to a user, app storage needs to be cleared on the physical device. When adding a Place to an existing user, that user will sync the new Place and associated data automatically the next time they sync.

    User Experience

    Users who are assigned multiple Places will see a few changes in the CHT’s User Interface. These changes are summarized below, and are discussed in detail in this forum post.

    Contacts Page: The List View will show all assigned Places (ie CHUs) but will not show the list of child places (ie CHW Areas).

    Contact Detail: The list of child places (ie CHW Areas) can be viewed on this page.

    Messages, Tasks, and Reports Pages: Data for all assigned Places will show up together in the List View. An additional level of lineage has been added to the Breadcrumbs to clarify which Place each item (Message, Task, or Report) is associated to.

    Targets: Personal Targets will look the same. Aggregate Targets will not be shown for users with multiple assigned places, even if the user has the permission. Support for Aggregate Targets for users associated to multiple places will potentially be supported in a later release.

    Signature widget

    For some time now there has been interest in having the CHT support the ability for a CHT to collect a hand drawn signature from a patient. With this release, app developers may now implement the draw widget in “signature” mode. Here is an example of both the draw widget and signature widget on a demo app in the CHT:

    And more…

    Features

    • #6543: Allow for multiple places to be assigned to users
    • #9116: Update the user form to allow assigning multiple places to users.
    • #8308: Add support for Signature Widget in forms

    Improvements

    None.

    Security fixes

    None.

    Performance improvements

    None.

    Bug fixes

    • #8072: Error when saving form with repeated upload inputs
    • #9203: Ensure backward compatibility of facility_id in Admin app

    Technical improvements

    • #9065: Add cht-datasource to support get person by uuid
    • #8557: Investigate using shellcheck for script linting
    • #9134: Upgrade e2e test fails on betas
    • #9153: Update the release notes generation script to add a list of contributors
    • #9155: Permissions to show reports and tasks test is failing often
    • #9173: Add Gaze cwd with path.resolve to watch.js
    • #9179: Integration tests broken on master
    • #9187: Haproxy unit tests are failing due to haproxy patch release

    Contributors

    Thanks to all who committed changes for this release!

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/index.html b/core/releases/index.html index e99c3572aa..34f71f57a3 100644 --- a/core/releases/index.html +++ b/core/releases/index.html @@ -1,9 +1,9 @@ -Core Framework Releases | Community Health Toolkit +Core Framework Releases | Community Health Toolkit

    Core Framework Releases

    Versions currently supported, dependencies, and release notes for the CHT Core Framework

    Supported Versions

    Medic supports minor versions of the CHT Core Framework for three months after the next minor version is made available, and the latest minor of a major version for twelve months after the next major version is made available.

    Once a version is no longer supported it will not receive any further patch releases and upgrading to a supported version will be required to resolve any issues you have.

    It is recommended that all projects update regularly multiple times a year to get the benefits of bug fixes, security patches, and performance improvements. Being on a supported version also makes it easy to start using features coming in future releases. Most upgrades are quick, reliable, and easily adopted by users. Whenever an upgrade does require additional effort this will be outlined in the release notes.

    VersionStatusRelease dateEnd of life
    4.9.xCurrent25-Jun-2024TBA
    4.8.xSupported22-May-202425-Sep-2024
    4.7.xSupported07-May-202422-Aug-2024
    4.6.xSupported20-Mar-202410-Aug-2024
    4.5.xEOL20-Nov-202320-Jun-2024
    4.4.xEOL20-Sep-202320-Feb-2024
    4.3.xEOL18-Aug-202320-Dec-2023
    4.2.xEOL25-May-202318-Nov-2023
    4.1.xEOL12-Dec-202225-Aug-2023
    4.0.xEOL03-Nov-202212-Mar-2023
    3.17.xEOL11-Oct-202203-Nov-2023
    3.16.xEOL3-Aug-202211-Jan-2023
    3.15.xEOL4-May-20223-Nov-2022
    3.14.xEOL11-Feb-20224-Aug-2022
    3.13.xEOL29-Sep-202111-May-2022
    3.12.xEOL28-Jul-202129-Dec-2021
    3.11.xEOL21-Apr-202128-Oct-2021
    3.10.xEOL9-Sep-202021-Jul-2021
    3.9.xEOL19-Jun-20209-Dec-2020
    3.8.xEOL11-Feb-202019-Sep-2020
    3.7.xEOL22-Oct-201911-Jun-2020
    3.6.xEOL17-Jul-201924-Mar-2020
    3.5.xEOL27-Jun-201917-Oct-2019
    3.4.xEOL27-Mar-201927-Sep-2019
    3.3.xEOL22-Feb-201927-Jun-2019
    3.2.xEOL23-Jan-201922-May-2019
    3.1.xEOL21-Nov-201823-Apr-2019
    3.0.xEOL15-Nov-201821-Feb-2019
    2.18.xEOL30-Aug-201815-Nov-2019
    earlierEOL30-Nov-2018

    Requirements

    Dependencies

    The following table shows the dependencies for deploying the CHT.

    cht-coreNodeJSCouchDBSupported browsersSMS bridgeAndroid OScht-androidcht-couch2pg
    4.4.x+N/A3.3.2+Chrome 90+, Android System WebView 90+, Firefox latestcht-gateway5.0+1.0+3.0+
    4.0.x-4.3.xN/A2.xChrome 90+, Android System WebView 90+, Firefox latestcht-gateway5.0+1.0+3.0+
    3.x8.11+2.xChrome 53+, Firefox latestcht-gateway4.4+0.4.5+3.0+
    2.x6+1.6+Chrome 30+, Firefox latestcht-gateway4.4+Any2.0 < 3.0
    0.40.12+1.6+Chrome 30+, Firefox latestSMSSyncN/AN/AN/A

    See Also: Hosting Requirements

    Client Devices

    The following is the minimum specification recommendation for smartphones to handle the typical workload of front line health workers. Users with particularly high workloads or facility or supervisor workloads will require more powerful devices.

    SpecificationMinimumRecommended
    Android version5.09.0+
    Processor1.0GHz dual-core2.0GHz quad-core
    RAM1GB2GB
    Storage8GB16GB

    If CHWs will be collecting GPS data, autonomous GPS sensors in addition to assisted GPS (A-GPS) for areas with poor GSM network connectivity will enhance the quality of GPS data collected. Autonomous GPS is usually labeled in terms of the supported navigation satellite system:

    • Galileo
    • BDS (BeiDou)
    • GLONASS
    • QZSS

    Devices with more navigation systems are more likely to get a more accurate location fix in varied locations. For example, Huawei Y5 has GPS specs listed on gsmarena.com as GPS: Yes, with A-GPS, GLONASS, BDS which makes it a good choice for GPS data collection.

    Release Notes

    4.x

    3.x

    2.x

    Earlier releases


    Hosting

    Guides for hosting, maintaining, and monitoring CHT applications

    Hosting > + Create project issue

    Core Framework Releases

    Versions currently supported, dependencies, and release notes for the CHT Core Framework

    Supported Versions

    Medic supports minor versions of the CHT Core Framework for three months after the next minor version is made available, and the latest minor of a major version for twelve months after the next major version is made available.

    Once a version is no longer supported it will not receive any further patch releases and upgrading to a supported version will be required to resolve any issues you have.

    It is recommended that all projects update regularly multiple times a year to get the benefits of bug fixes, security patches, and performance improvements. Being on a supported version also makes it easy to start using features coming in future releases. Most upgrades are quick, reliable, and easily adopted by users. Whenever an upgrade does require additional effort this will be outlined in the release notes.

    VersionStatusRelease dateEnd of life
    4.9.xCurrent25-Jun-2024TBA
    4.8.xSupported22-May-202425-Sep-2024
    4.7.xSupported07-May-202422-Aug-2024
    4.6.xSupported20-Mar-202410-Aug-2024
    4.5.xEOL20-Nov-202320-Jun-2024
    4.4.xEOL20-Sep-202320-Feb-2024
    4.3.xEOL18-Aug-202320-Dec-2023
    4.2.xEOL25-May-202318-Nov-2023
    4.1.xEOL12-Dec-202225-Aug-2023
    4.0.xEOL03-Nov-202212-Mar-2023
    3.17.xEOL11-Oct-202203-Nov-2023
    3.16.xEOL3-Aug-202211-Jan-2023
    3.15.xEOL4-May-20223-Nov-2022
    3.14.xEOL11-Feb-20224-Aug-2022
    3.13.xEOL29-Sep-202111-May-2022
    3.12.xEOL28-Jul-202129-Dec-2021
    3.11.xEOL21-Apr-202128-Oct-2021
    3.10.xEOL9-Sep-202021-Jul-2021
    3.9.xEOL19-Jun-20209-Dec-2020
    3.8.xEOL11-Feb-202019-Sep-2020
    3.7.xEOL22-Oct-201911-Jun-2020
    3.6.xEOL17-Jul-201924-Mar-2020
    3.5.xEOL27-Jun-201917-Oct-2019
    3.4.xEOL27-Mar-201927-Sep-2019
    3.3.xEOL22-Feb-201927-Jun-2019
    3.2.xEOL23-Jan-201922-May-2019
    3.1.xEOL21-Nov-201823-Apr-2019
    3.0.xEOL15-Nov-201821-Feb-2019
    2.18.xEOL30-Aug-201815-Nov-2019
    earlierEOL30-Nov-2018

    Requirements

    Dependencies

    The following table shows the dependencies for deploying the CHT.

    cht-coreNodeJSCouchDBSupported browsersSMS bridgeAndroid OScht-androidcht-couch2pg
    4.4.x+N/A3.3.2+Chrome 90+, Android System WebView 90+, Firefox latestcht-gateway5.0+1.0+3.0+
    4.0.x-4.3.xN/A2.xChrome 90+, Android System WebView 90+, Firefox latestcht-gateway5.0+1.0+3.0+
    3.x8.11+2.xChrome 53+, Firefox latestcht-gateway4.4+0.4.5+3.0+
    2.x6+1.6+Chrome 30+, Firefox latestcht-gateway4.4+Any2.0 < 3.0
    0.40.12+1.6+Chrome 30+, Firefox latestSMSSyncN/AN/AN/A

    See Also: Hosting Requirements

    Client Devices

    The following is the minimum specification recommendation for smartphones to handle the typical workload of front line health workers. Users with particularly high workloads or facility or supervisor workloads will require more powerful devices.

    SpecificationMinimumRecommended
    Android version5.09.0+
    Processor1.0GHz dual-core2.0GHz quad-core
    RAM1GB2GB
    Storage8GB16GB

    If CHWs will be collecting GPS data, autonomous GPS sensors in addition to assisted GPS (A-GPS) for areas with poor GSM network connectivity will enhance the quality of GPS data collected. Autonomous GPS is usually labeled in terms of the supported navigation satellite system:

    • Galileo
    • BDS (BeiDou)
    • GLONASS
    • QZSS

    Devices with more navigation systems are more likely to get a more accurate location fix in varied locations. For example, Huawei Y5 has GPS specs listed on gsmarena.com as GPS: Yes, with A-GPS, GLONASS, BDS which makes it a good choice for GPS data collection.

    Release Notes

    4.x

    3.x

    2.x

    Earlier releases


    Hosting

    Guides for hosting, maintaining, and monitoring CHT applications

    Hosting > Requirements

    Requirements for hosting CHT applications

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/core/releases/index.xml b/core/releases/index.xml index 9da6b6ad3d..a377881c78 100644 --- a/core/releases/index.xml +++ b/core/releases/index.xml @@ -1,1903 +1,190 @@ -Community Health Toolkit – Core Framework Releaseshttps://docs.communityhealthtoolkit.org/core/releases/Recent content in Core Framework Releases on Community Health ToolkitHugo -- gohugo.ioenCore: 0.x release noteshttps://docs.communityhealthtoolkit.org/core/releases/0.4.15-and-earlier/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/0.4.15-and-earlier/ -<h2 id="0415">0.4.15</h2> -<p><em>March 2, 2017</em></p> -<ul> -<li>Fixed potential race condition with medic-gateway. Issue: medic-projects/issues/1243</li> -<li>Bumped libphonenumber to make phone number validation more up to date. Issue: medic-projects/issues/1005</li> -</ul> -<h2 id="0414">0.4.14</h2> -<p><em>December 16, 2016</em></p> -<ul> -<li>Bug fix for medic-gateway sending scheduled messages. Issue: #2535</li> -</ul> -<h2 id="0413">0.4.13</h2> -<p><em>October 21, 2016</em></p> -<ul> -<li>Option to set birthdate using days old instead of weeks. Issue: #2756</li> -<li>The week/month is off by 2 in the Reporting Rates analytics dashboard. Issue: #2781</li> -<li>Remove socket limit in medic-api. Issue: #2632</li> -</ul> -<h2 id="0412">0.4.12</h2> -<p><em>July 21, 2016</em></p> -<ul> -<li>Fixed bug in reporting rates for weekly time unit. #2429</li> -<li>Log warnings in sentinel when ID collisions happen. #1898</li> -<li>Support integration with <a href="https://github.com/medic/medic-gateway">medic-gateway</a> for sending and receiving SMS medic-api#69</li> -</ul> -<h2 id="0411">0.4.11</h2> -<p><em>February 4, 2016</em></p> -<ul> -<li>Security fix for leaking auth info.</li> -</ul> -<h2 id="0410">0.4.10</h2> -<p><em>Nov 16, 2015</em></p> -<ul> -<li> -<p>Added support for Outgoing Deny List, a comma separated list of phone numbers -or strings to deny outgoing service to. #750</p> -</li> -<li> -<p>Fixed bug in records export. #1273</p> -</li> -<li> -<p>Fixed bugs in uniqueWithin validation. medic-sentinel#74</p> -</li> -<li> -<p>Added link to Help page in main menu.</p> -</li> -</ul> -<h2 id="049">0.4.9</h2> -<p><em>Aug 26, 2015</em></p> -<ul> -<li> -<p>Fixed bug on node 0.12 in felix-couchdb. #1145</p> -</li> -<li> -<p>Improved error handling when notifications (start/stop) configs are -misconfigured. #1144</p> -</li> -<li> -<p>Fixed bug in <code>exists</code> validation where it fails on some unicode characters. #1147</p> -</li> -<li> -<p>Fixed Reporting Rates interface that was neglected and broken. #1030</p> -</li> -<li> -<p>Fixed bug in exporting data by date, it&rsquo;s now inclusive. #1104</p> -</li> -</ul> -<h2 id="048">0.4.8</h2> -<p><em>July 14, 2015</em></p> -<ul> -<li> -<p>Added SMS parser fixes from dev branch:</p> -<ul> -<li>parse string fields with exclamation marks</li> -<li>compact textforms format handles quotes in quotes</li> -</ul> -</li> -<li> -<p>Fixed export bug when using lowercase form codes. Please re-upload your forms -so they are formatted correctly. #998</p> -</li> -<li> -<p>Fixed bug where exporting yields same result with or without date -filter. #1059, #1031</p> -</li> -</ul> -<h2 id="047">0.4.7</h2> -<p><em>June 16, 2015</em></p> -<ul> -<li> -<p>Fixed ODK forms list bug when the list is empty. Issue #886</p> -</li> -<li> -<p>Fixed compact textforms parser bug with exclamation points. Issue #989</p> -</li> -<li> -<p>Fixed bug in compact textforms parser we ignored fields with uppercase -letters in the key of the form definition. Issue #991</p> -</li> -<li> -<p>Fixed bug when creating record with empty message. Issue #990</p> -</li> -<li> -<p>Moved raw message to bottom of report body. Issue: #927</p> -</li> -</ul> -<h2 id="046">0.4.6</h2> -<p><em>June 4, 2015</em></p> -<ul> -<li>Improved boolean expression evaluation in registration configuration.</li> -</ul> -<h2 id="045">0.4.5</h2> -<p><em>May 28, 2015</em></p> -<ul> -<li> -<p>Fixed bug in schedules editor for LMP (last menstrual period) based -schedules. #973</p> -</li> -<li> -<p>Initial support for messages, records and forms API. See -<a href="https://github.com/medic/medic-api/blob/master/API_v1.md">https://github.com/medic/medic-api/blob/master/API_v1.md</a></p> -</li> -</ul> -<h2 id="044">0.4.4</h2> -<p><em>May 21, 2015</em></p> -<ul> -<li>Added support for a new messages parser we&rsquo;re calling Javarosa prefixed with -the <code>J1</code> format code.</li> -</ul> -<h2 id="043">0.4.3</h2> -<p><em>February 26, 2015</em></p> -<h3 id="features">Features</h3> -<ul> -<li> -<p>Major UI enhancements #370</p> -<ul> -<li>Inbox Style with advanced search bar</li> -<li>Admin Panels</li> -<li>Mobile device support</li> -</ul> -</li> -<li> -<p>Configurable date format #577</p> -</li> -<li> -<p>Built-in ANC Analytics #586</p> -</li> -<li> -<p>Easy user feedback mechanism #19</p> -</li> -<li> -<p>Added help and tour</p> -</li> -<li> -<p>Refactored build process, added grunt, bower and jshint.</p> -</li> -<li> -<p>No forms are included by default. You must upload your forms. As usual -configuration (settings and forms) persists through upgrades.</p> -</li> -<li> -<p>Added new translations</p> -</li> -<li> -<p>Default auto-replies</p> -</li> -<li> -<p>There are now two ways to export stuff: 1) from the reports screen click on -export, or 2) on the Configuration &gt; Export screen. The former is simplified -and has no way of changing facility, form, or file format. The latter is -functionally equivalent to 0.4.2.</p> -</li> -</ul> -<h3 id="upgrade-notes">Upgrade Notes</h3> -<ul> -<li> -<p>CouchDB Lucene &gt;= 1.0.2</p> -</li> -<li> -<p>Gardener &gt;= 1.1.0</p> -</li> -<li> -<p>New <a href="https://github.com/medic/medic-os/blob/3aedf0622eb0669aee2e5bbfba95a42faf05b9da/platform/packages/medic-core/settings/medic-core/nginx/nginx.conf">Nginx configuration</a>, close or redirect port 5984, proxy all requests through medic-api.</p> -</li> -</ul> -<h2 id="042">0.4.2</h2> -<p><em>September 4, 2014</em></p> -<ul> -<li>Fixed bug: When searching for a patient identifier using the free-text search feature, results were not returned properly. Index the field appropriately.</li> -</ul> -<h2 id="041">0.4.1</h2> -<p><em>July 31, 2014</em></p> -<ul> -<li> -<p>Minor UI clean up.</p> -</li> -<li> -<p>Allow hash symbol to separate form code and data in structured textform message.</p> -</li> -<li> -<p>Modified build to bundle npm dependencies with attached node modules.</p> -</li> -</ul> -<h2 id="040">0.4.0</h2> -<p><em>July 10, 2014</em></p> -<h3 id="new-features">New Features</h3> -<ul> -<li> -<p>User interface for common settings and translations #484</p> -</li> -<li> -<p>User interface to configure basic ANC messaging workflows #487</p> -</li> -<li> -<p>Nodejs module for API calls and to handle audit transactions</p> -</li> -<li> -<p>Better language support on messages throughout configuration. e.g. you can -define a reminder schedule using multiple languages and the right language -will be used based on the locale of the message. #486</p> -</li> -<li> -<p>Added support to configure locales through settings screen #491</p> -</li> -<li> -<p>Added support for custom forms and uploading JSON form definitions #283</p> -</li> -<li> -<p>Supporting old/obsolete settings via migrations during restore #501</p> -</li> -<li> -<p>Support minute setting for scheduled hours configuration</p> -</li> -<li> -<p>Reduced files size of design doc by minifying javascript and combining files</p> -</li> -<li> -<p>Added support for unique validations across multiple fields. #412</p> -</li> -<li> -<p>External IDs can be added to facilities on the facility page. #503</p> -</li> -<li> -<p>Added <code>columns</code> query parameter to allow callers to messages and data_records -exports to retrieve specific columns in a specific order. #503</p> -</li> -<li> -<p>Added Patient ID field to default data record export</p> -</li> -<li> -<p>Allow specifying of task columns in message export.</p> -<p>To include the group column, include the parameter <code>columns=[&quot;task.group&quot;]</code> -(and any other columns you need, eg: <code>patient_id</code>). #432</p> -</li> -<li> -<p>Allow range searches on number fields in lucene. #481</p> -</li> -</ul> -<h3 id="bug-fixes">Bug fixes</h3> -<ul> -<li> -<p>Fixed bug stopping district admins from being able to delete documents. #509</p> -</li> -<li> -<p>Corrected language Nepalese to Nepali</p> -</li> -<li> -<p>Fixed too many requests for app_settings #511</p> -</li> -<li> -<p>Fixed saving user password, was getting reset/wiped #509</p> -</li> -<li> -<p>Render facilities controls on all facilities tabs. #338</p> -</li> -</ul> -<h3 id="deprecated-and-backwards-incompatible-changes">Deprecated and Backwards Incompatible changes</h3> -<ul> -<li> -<p>deprecated <code>exclude_cols</code> parameter for export integrations. Migrate -applications to use the <code>columns</code> parameter instead.</p> -</li> -<li> -<p>No forms are included by default, you must upload your forms. As usual -configuration (settings and forms) persists through upgrades.</p> -</li> -</ul> -<h2 id="0311">0.3.11</h2> -<p><em>June 19, 2014</em></p> -<ul> -<li> -<p>Fixed bug where scheduled messages were out of order on Chrome #527</p> -</li> -<li> -<p>Fixed bug where app settings was ignored, and the app reverted to defaults. #524</p> -</li> -<li> -<p>Added delivery form to generic ANC forms.</p> -</li> -<li> -<p>Modified behavior of patient reports when <code>silence_for</code> option is empty we -clear the entire schedule instead of a group.</p> -</li> -<li> -<p>Fixed notifications to add the configured response #508</p> -</li> -<li> -<p>Fixed bug and added support for multiple schedules in schedule silencing</p> -<p>When <code>silence_for</code> is specified we should only silence/clear one group, I -introduced a bug a few commits ago that would ignore the group and -silence/clear based on date. Use the <code>silence_for</code> window to match and -clear the first group.</p> -<p>Also added support for comma separate string on <code>silence_type</code> option. -In the MCH case we have two schedules that can be generated depending on -the form submission/registration: ANC Reminders and ANC Reminders LMP. -Now both schedules can be cleared with a single ANCV settings entry.</p> -</li> -</ul> -<h2 id="0310">0.3.10</h2> -<p><em>June 12, 2014</em></p> -<ul> -<li>updated intrahealth-senegal forms</li> -</ul> -<h2 id="039">0.3.9</h2> -<p><em>May 22, 2014</em></p> -<ul> -<li>fixed a bug in settings parser, to conform to the latest app-settings -changes, otherwise configs never get triggered.</li> -</ul> -<h2 id="038">0.3.8</h2> -<p><em>May 22, 2014</em></p> -<ul> -<li> -<p>fixed bug in messages export filters to include all data records, -unstructured messages were being ignored. #502</p> -</li> -<li> -<p>added support for app-settings kanso package.</p> -<p>Saving settings in dashboard was too slow for use because entire ddoc was -being updated. Using app-settings API fixes that.</p> -</li> -</ul> -<h2 id="037">0.3.7</h2> -<p><em>May 13, 2014</em></p> -<ul> -<li>Modified KEMRI form fields to be ordered the same as KEMR form.</li> -</ul> -<h2 id="036">0.3.6</h2> -<p><em>April 28, 2014</em></p> -<ul> -<li>Fixed bug in updating duplicate scheduled reports #483</li> -</ul> -<h2 id="035">0.3.5</h2> -<p><em>April 16, 2014</em></p> -<ul> -<li> -<p>Major bug fix that was introduced in 0.3.3 where visit reports do not get processed.</p> -<p>Fixed accept_patient_reports transition so it calls db for readonly actions -instead of audit.</p> -</li> -<li> -<p>Allow analytics role to download messages and forms. Issue: #477</p> -</li> -<li> -<p>Updated user management to show role for analytics user. Issue: #478</p> -</li> -<li> -<p>Initial version of forms for Miraclefeet India</p> -</li> -</ul> -<h2 id="034">0.3.4</h2> -<p><em>April 14, 2014</em></p> -<ul> -<li> -<p>translate strings on user mgmt facilities select list #474</p> -</li> -<li> -<p>Change ordering of messages export format for better readability based on -when the state is triggered. #475</p> -<p>From: Received, Sent, Pending, Scheduled Cleared, Muted -To: Received, Scheduled, Pending, Sent, Cleared, Muted</p> -</li> -</ul> -<h2 id="033">0.3.3</h2> -<p><em>April 10, 2014</em></p> -<ul> -<li> -<p>Fixed spreadsheet keyboard navigation. #448</p> -</li> -<li> -<p>Validate &lsquo;Everyone at x&rsquo; for at least one valid phone number. #333</p> -</li> -<li> -<p>Audit support for data records and facility data #415</p> -<p>Also includes support for export of audit data as XML or CSV file.</p> -<p>Note: Only new records and record edits will have an audit log entry. So -this means your audit log will only contain changes to records after the -upgrade.</p> -<p>Similarly browsing old revision will stop working for old records because -they lack audit log entries. If this is a major problem for you let us -know and we can add backwards compatible revision browsing in the next -release.</p> -<p>This has been released as a standalone re-useable module for Node and -browser environments: <a href="https://github.com/medic/couchdb-audit">https://github.com/medic/couchdb-audit</a></p> -</li> -<li> -<p>Added support for compact version of TextForms format #428</p> -<p>In compact Textforms fields are delimited by spaces and determined by order. -So no hashes or field keys are required like in classic TextForms format. -If your field value has spaces in it then it must be surrounded by quotes -unless it is the last field.</p> -<p>Examples:</p> -<pre><code> REG 4165550000 John Smith -REG &quot;John Smith&quot; 4165550000 -</code></pre> -</li> -<li> -<p>Include state change timestamps and patient_id in messages export #453</p> -<p>Old Columns:</p> -<p>Record UUID, Reported Date, From, Clinic Contact Name, Clinic Name, Health -Center Contact Name, Health Center Name, District Hospital Name, Message -Type, Message State, Message Timestamp/Due, Message UUID, Sent By, To -Phone, Message Body</p> -<p>New Columns:</p> -<p>Record UUID, Patient ID, Reported Date, Reported From, Clinic Contact Name, -Clinic Name, Health Center Contact Name, Health Center Name, District -Hospital Name, Message Type, Message State, Received Timestamp, Sent -Timestamp, Pending Timestamp, Scheduled Timestamp, Cleared Timestamp, Muted -Timestamp, Message UUID, Sent By, To Phone, Message Body</p> -<p>Note: These are the default column labels and they are configurable.</p> -</li> -<li> -<p>Disable facility select and show loading message until data is loaded. #452</p> -</li> -<li> -<p>Updated font-awesome to 3.2.1 to get extra icons</p> -</li> -<li> -<p>Fixed Help button on the spreadsheet. #455</p> -</li> -<li> -<p>Fixed spreadsheet duplicate rendering when quickly switching tabs. #362 #450</p> -</li> -<li> -<p>Added permissions checks to export lists functions. #456</p> -</li> -<li> -<p>Fixed bug where facility spreadsheet update records when field value is -unchanged. #457</p> -</li> -<li> -<p>Fixed bug where registrations was not using db-wide unique IDs. medic-sentinel#54</p> -</li> -<li> -<p>Fixed duplicate records on ID search #430</p> -</li> -<li> -<p>User Management UX refactor #385, #380, #462, #380, #379, #378, #377, #429</p> -</li> -<li> -<p>Fixed bug on facilities screen where delete functions would stack up and -inadvertently delete a facility. #469</p> -</li> -</ul> -<h2 id="032">0.3.2</h2> -<p>_March 11, 2014 _</p> -<ul> -<li> -<p>fixed facilities spreadsheet bug #451 in Chrome</p> -</li> -<li> -<p>minor user interface tweaks on deletion of facilities modal</p> -</li> -</ul> -<h2 id="031">0.3.1</h2> -<p>_March 3, 2014 _</p> -<ul> -<li> -<p>Fixed a bug where the facilities spreadsheet was holding focus so sending a message doesn&rsquo;t work. #440</p> -</li> -<li> -<p>Added Bulk Messaging support, so you can send messages to multiple recipients. #333</p> -</li> -<li> -<p>Fixed bug to include incoming messages in messages export. #436</p> -</li> -<li> -<p>Fixed bug where it is possible to send a message twice by double clicking the submit button</p> -</li> -<li> -<p>Fixed bug where we failed to retrieve settings on port 80 #438</p> -<p>This was experienced when proxying to couchdb because http proxying will -decode the URL including docid of the show parameter, so the show -returns 404. Solution is to always double URL encode if the show docid -contains special characters.</p> -</li> -<li> -<p>Confirm with user before deleting facility so they can&rsquo;t be deleted by accident. #371</p> -</li> -<li> -<p>Added unique Patient ID validation support. #411 medic-sentinel/pull/50</p> -<p>Use unique(&lsquo;patient_id&rsquo;) in your registration validation rules to validate -new form submissions that are setting the patient ID values via forms.</p> -<p>Validation rules may consist of Pupil.js rules and custom rules. These -cannot be combined as part of the same rule.</p> -<p>Not OK:</p> -<pre><code> rule: &quot;regex('[0-9]{5}') &amp;&amp; unique('patient_id')&quot; -</code></pre> -<p>OK:</p> -<pre><code> rule: &quot;regex('[0-9]{5}') &amp;&amp; max(11111)&quot; -</code></pre> -<p>If for example you want to validate that patient_id is 5 numbers and it -is unique (or some other custom validation) you need to define two -validation configs/separate rules in your settings. Example validation -settings:</p> -<pre tabindex="0"><code>[ -{ -property: &#34;patient_id&#34;, -rule: &#34;regex(&#39;[0-9]{5}&#39;)&#34;, -message: &#34;Invalid: Patient ID {{patient_id}} must be 5 numbers.&#34; -}, -{ -property: &#34;patient_id&#34;, -rule: &#34;unique(&#39;patient_id&#39;)&#34;, -message: &#34;Invalid: Patient ID {{patient_id}} must be unique.&#34; -} -] -</code></pre></li> -<li> -<p>Added Conditional Alerts feature. #437 medic-sentinel/issue/52</p> -<p>Configure the Alerts section of the App Settings to send a message when an -incoming message meets the configured condition.</p> -</li> -</ul> -<h2 id="030-beta39-bugfix">0.3.0-beta.39 (bugfix)</h2> -<p><em>February 25, 2014</em></p> -<ul> -<li> -<p>Fixed textforms whitespace parser bug when using form list field types. #431</p> -<p>Textforms parser wasn&rsquo;t trimming space correctly on a field value, so if -you had a list defined using those values they would never get matched.</p> -<p>Textforms was only matching numeric values of length 2 or more, so if -you submitted a one digit number you would not get a numeric match.</p> -<p>Also if the value didn&rsquo;t match numeric or a date format then the -whitespace was not being trimmed correctly.</p> -</li> -</ul> -<h2 id="030-beta38-debug">0.3.0-beta.38 (debug)</h2> -<p><em>February 13, 2014</em></p> -<ul> -<li>added logging calls to help debug and identify whitespace parsing bug #431</li> -</ul> -<h2 id="030-beta37">0.3.0-beta.37</h2> -<p><em>January 21, 2014</em></p> -<ul> -<li> -<p>Fixes to user roles (@marc)</p> -<p>Matches Transitional V2 in -<a href="https://docs.google.com/a/medicmobile.org/spreadsheet/ccc?key=0Ao9l2yegOFn7dEJRTEw1Z3RmZm0wTEo4Nk92NjVocnc">https://docs.google.com/a/medicmobile.org/spreadsheet/ccc?key=0Ao9l2yegOFn7dEJRTEw1Z3RmZm0wTEo4Nk92NjVocnc</a></p> -</li> -<li> -<p>Added support for Kemri Muvuku Form (KEMR)</p> -</li> -<li> -<p>Added exclude_cols query param to csv/xml form exports. #421</p> -<p>For example include <code>exclude_cols=1,5</code> in your query parameters to -remove the first and fifth column of an export.</p> -</li> -<li> -<p>bugfix on export query params and UX adjustments</p> -<p>Made English CSV export default. SpreadsheetML can be a little buggy -because we&rsquo;re using HTML entities (not valid XML).</p> -<p>Disabled default month value in exports screen since record count does -not reflect the export row totals displayed. It&rsquo;s probably better UX to -have the user set the date knowing they are doing something than having a -default that doesn&rsquo;t make sense with the totals on the screen and having -to guess why that is.</p> -</li> -<li> -<p>Added timezone support to exports #394</p> -<p>Render page contents first and then load the fields data since that was -holding up the page load.</p> -<p>Indexing _id as <code>uuid</code> in field index so you can search for -<code>uuid:10366976d62ab9a31257b2fad16113ee</code> now and it shows up in available -fields index. For some reason I think underscore prefixed fields do -show up in fields listing on Lucene for some reason.</p> -</li> -<li> -<p>Fixed poor loading on search help #422</p> -</li> -</ul> -<ul> -<li> -<p>added timezone support to exports #394</p> -<p>Now dates in the exported spreadsheet should include your locale timezone. Controlled by the <code>tz</code> query param.</p> -</li> -</ul> -<ul> -<li> -<p>Added new Messages Export and removed message data from Forms Export</p> -<p>New http endpoint <code>/export/messages</code> to get messages export. Records are -always latest first (reverse chronological). Message export will include -all records (valid and invalid), the point of the messages export is to -give you access to all your message data, including outgoing error -messages.</p> -<p><strong>Warning</strong>: the following URLs are no longer supported:</p> -<pre tabindex="0"><code>/{form}/data_records.csv -/{form}/data_records.xml -/form_data_records.xml -/form_data_records.csv -</code></pre><p>Use <code>/export/forms/{form}</code> path instead.</p> -<p><strong>Warning</strong>: Existing form data export format has changed. Included UUID of -the related record so data among the two spreadsheets (messages and form -data) can be correlated if need be. Also removed the message -data/columns from form data export. A record can be found via UUID by -using <code>uuid:&lt;the uuid string&gt;</code> in the search box.</p> -<p>Changed default column name of &ldquo;From&rdquo; to &ldquo;Reported From&rdquo;. Note if this -shouldn&rsquo;t change if you have an existing install since it is generated -based on your translation settings.</p> -</li> -</ul> -<pre tabindex="0"><code> The NEW columns (added UUID column and removed message data): -Record UUID -Reported Date -Reported From -Clinic Contact Name -Clinic Name -Health Center Contact Name -Health Center Name -District Hospital Name -[columns for form fields data depending on form] -The OLD columns: -Reported Date -From -Clinic Contact Name -Clinic Name -Health Center Contact Name -Health Center Name -District Hospital Name -[columns for form fields data depending on form] -Incoming Message -Responses -Outgoing Messages -Scheduled Tasks -</code></pre><h2 id="0122">0.1.22</h2> -<ul> -<li>Major improvements in scheduled reminder support in Sentinel.</li> -<li>Support for Twilio in Sentinel.</li> -<li>Minor refactor of records screen: -<ul> -<li>added counters to show sent/scheduled messages in records rows.</li> -<li>displaying scheduled tasks in records expanded view</li> -<li>added RC Code column</li> -<li>combined facility data into one column</li> -<li>improved contact info column, made more prominent by moving clinic contact info and phone together and into second column.</li> -</ul> -</li> -<li>Removed auto-reply messages from form submission that use sentinel/scheduler so only one response is sent on a new form submission.</li> -<li>Fixed bug: Only update records of type &lsquo;data_record&rsquo; on records screen</li> -<li>Preserve css (expando visibility) when row is replaced in records.</li> -<li>Use serial_number instead of patient_id for ohw_registration.</li> -<li>Fixed bug in textforms parser where 01234 was parsed as 1234 and added test.</li> -<li>Fixed unit tests for ONOT and ORPT form updates.</li> -<li>Fixed/Added tests for ohw emergency report (OEMR) responses.</li> -<li>Added PNC test for ohw emergency report (OEMR).</li> -<li>Fixed ohw notifications transition to set correct muted value.</li> -<li>Fixed OHW birth report (OBIR) logic and added tests.</li> -</ul> -<h2 id="0121">0.1.21</h2> -<ul> -<li>Fixed bug: regression from error handling refactor where assigning a record a new facility did not clear <code>facility_not_found</code> errors.</li> -<li>Fixed bug: months based reporting valid value was not being passed through view.</li> -<li>Fixed regression: returning <code>form_invalid</code> response when <code>form_invalid_custom</code> error is found.</li> -<li>Fixed NYAA form custom validation.</li> -<li>Added ability to do bulk deletes and facility updates.</li> -<li>Fixed bug where record is created as valid if callback request is not completed due to disconnection or phone running out of battery. Records are initialized as invalid.</li> -</ul> -<h2 id="0120">0.1.20</h2> -<ul> -<li>HTTP callbacks refactor, now records are created on first HTTP POST.</li> -<li>Tests refactor, replaced some redundant/large tests with more specific ones.</li> -<li>Error handling refactor, now clients should not get system messages.</li> -<li>Updated JSON forms to include Carlos Slim test forms.</li> -<li>SMSSync 2.0 support: parsing of new timestamp format.</li> -<li>Bundled SMSSync 2.0.1-hcb (<a href="https://github.com/mandric/SMSSync/wiki/HTTP-Callbacks">http-callbacks</a>) build.</li> -<li>&rsquo;null&rsquo; form key in filters to find records without a form definition.</li> -</ul> -<p><strong>Upgrade Notes</strong></p> -<ul> -<li>If you upgrade the gateway without upgrading Kujua the timestamps from the messages will not get parsed. So messages will get a timestamp assigned to them by Kujua, which reflect the time the record was created in Kujua rather than the actual time the report/message was sent.</li> -</ul> -<h2 id="0119">0.1.19</h2> -<ul> -<li>Fixed wifi lock bug in SMSSync #54.</li> -<li>Updated docs with new SMSSync build instructions</li> -<li>Fixed labels names and stat placement on Health Center and District reporting screens.</li> -<li>Added help panel to spreadsheet to explain key bindings.</li> -<li>Form revisions for OHW and CDC Nepal</li> -</ul> -<h2 id="0118">0.1.18</h2> -<ul> -<li>Fixed bug in records screen where 1 row was not getting rendered until a scroll.</li> -<li>Refactored messaging, supporting <code>messages_task</code> in json forms.</li> -<li>Fixed bug where non-districts showing up in districts filter.</li> -<li>Keep Kujua Reporting files in-tact so it can be activated with config.</li> -<li>Added validations, messages and ref id labels to forms on gateway testing doc.</li> -<li>Added autocomplete=off to error missing phone form in records screen.</li> -<li>Added css for pointer hover to icon-exclamation-sign.</li> -<li>Renamed <code>use-sentinel</code> to <code>use_sentinel</code> to be consistent in JSON forms.</li> -<li>Added basic unstructured message support.</li> -<li>Fixed bug in records during edit row function where reported date get munged.</li> -<li>Added spreadsheet feature so children get updated when a parent facility is changed.</li> -<li>Fixed bug where using wrong label on main analytics screen.</li> -<li>Always create records, even on empty or unstructured messages.</li> -<li>SMS response messaging refactor, added response messages to gateway testing doc.</li> -<li>Fixed hard coded URL paths in reporting tests.</li> -</ul> -<h2 id="0117">0.1.17</h2> -<ul> -<li>Renamed <code>reference_field</code> property on forms to <code>facility_reference</code>.</li> -<li>Allow phone number to match on any facility type not just clinics.</li> -</ul> -<h2 id="0116">0.1.16</h2> -<ul> -<li>Tell user to login on 403 template.</li> -<li>Added record detail to analytics screen expand div.</li> -<li>Added configuration entries for reporting rates/analytics.</li> -<li>Made labels more configurable.</li> -<li>Added Kujua Sentinel support for reminders and alerts.</li> -<li>Updated spreadsheet columns to use config for labels.</li> -<li>Added support for form_invalid_custom error codes.</li> -<li>Introduced <code>reference_field</code> on forms.</li> -<li>Fixed kujua reporting views to better handle undefined clinic object in facility data.</li> -<li>Make it clear in HC reporting screen that contact name/phone is undefined.</li> -<li>Use <code>__dirname</code> in sentinel so it can be launched from any directory.</li> -<li>Added page for reminder logs.</li> -<li>Fixed district filtering support.</li> -<li>Added lockRows option to spreadsheet.</li> -<li>Fixed bug in XML spreadsheet output where true boolean value shows as 0.</li> -<li>Added support in sentinel to update children when a facility is edited.</li> -<li>Fixed bug on delete and editRow modal window.</li> -<li>Hide all nav items except docs when logged out.</li> -<li>Removed console references and added JSON.stringify to logger call.</li> -</ul> -<h2 id="0115">0.1.15</h2> -<ul> -<li>Saving responses to record and displaying in records screen.</li> -<li>Added timezone offset to reported date in exports and data records screen.</li> -<li>Added datepicker to filter exports by date.</li> -<li>Added support for custom form validation functions</li> -<li>Show better error message for other codes like missing_fields</li> -<li>Fixed buttons in data records screen</li> -<li>Added Kujua Reporting package for easy switching of analytics features</li> -<li>Improved test coverage for missing fields, cleaned/fixed up tests.</li> -<li>Fixed form parsing for fields defined as sub-objects and boolean parsing.</li> -<li>Added cases for sms responses when form is undefined</li> -<li>Display tooltip on input element focus in spreadsheet</li> -<li>Added delete on facilities spreadsheet</li> -<li>Make en/xls default export format</li> -<li>Custom labels via config.js configuration doc</li> -<li>Support for records to key on any facility</li> -<li>Nepalese responses</li> -<li>VPD field updates</li> -<li>added additional form definitions</li> -</ul> -<h2 id="010-pre4">0.1.0-pre.4</h2> -<ul> -<li>Parsing refactor to allow for better unstructured and textforms support, #106, #79</li> -<li>Add validation to spreadsheet with first cut of phone validation. fixes #104</li> -<li>Direct support for json-form definitions #76</li> -<li>Added textforms support for tiny labels in form definition</li> -<li>Generating example messages automatically in Gateway Testing doc</li> -<li>Fixed textforms parser for Couchbase on OSX #46</li> -<li>Using complete field keys instead of tiny labels in textforms data record creation #79</li> -<li>Better generic localized message handling #79</li> -<li>Added delete feature to spreadsheet rows #97</li> -<li>Updated install docs to include require_valid_user steps</li> -<li>Various spreadsheet fixes #103, #98</li> -<li>Better localized string support in form definitions</li> -<li>Include national office data to districts on save</li> -</ul> -<h2 id="010-pre3">0.1.0-pre.3</h2> -<ul> -<li>Added Quick Install Doc</li> -<li>Renamed kujua-export to kujua-base #88</li> -<li>Added dropdown health centers and districts to spreadsheet #99, #66</li> -<li>Various facilities spreadsheet bug fixes #92, #91, #94</li> -<li>Added CouchDB security steps to install doc #88, #66</li> -<li>Fixed authorization passthrough for tasks pending #87</li> -<li>Various data records screen fixes #84, #83, #56</li> -<li>Updated gateway/SMSSync binary and added S3 link to install docs</li> -<li>Removed Export branding</li> -<li>Started on functional tests using zombie.js and vows</li> -<li>Added health centers and districts spreadsheet</li> -<li>Added facilities section and spreadsheet editor #63</li> -<li>Assert forms that pass validation gets saved #78, #75, #46</li> -<li>Fixed bug where form submission fails if it has extra fields #75</li> -<li>Fixed 24-hour time display bug</li> -<li>Added close button to login window #70</li> -<li>Fixed bug with scroll listener binding to other pages #69</li> -</ul> -<h2 id="010-pre2">0.1.0-pre.2</h2> -<ul> -<li>Better textforms support including validation #46</li> -<li>Added optional required field to form defs, all field definitions are required by default.</li> -<li>Close dropdown menus when clicking anywhere on the site #53</li> -<li>Stream export data instead of collecting it in memory #57</li> -<li>Added validation step to all new form submissions #73</li> -<li>Added records filtering by form #56</li> -<li>Moved data records code to module #69</li> -</ul> -<h2 id="010-pre1">0.1.0-pre.1</h2> -<ul> -<li>added facility columns to export formats #54</li> -<li>added fr translations for facility names. #54</li> -<li>The server will not send a response unless a form is recognized.</li> -<li>Filtering on Districts support</li> -<li>Added Records section</li> -<li>Fixed reported date on re-imports, so the dates are preserved based on sent timestamp in sms message.</li> -<li>Added infinite scroll for viewing records on one page instead of paging.</li> -<li>Marking empty fields with question marks.</li> -<li>Marking errors on records that have referral tasks with no recipient defined.</li> -<li>Real-time updates via changes feed</li> -</ul>Core: 2.10.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.10.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.10.0/ -<h2 id="features">Features</h2> -<ul> -<li>Use reference to translation keys in app_settings. Issue: #3127</li> -<li>Add date of birth to person created by SMS. Issue: #3100</li> -<li>Configure the max number of SMS in multipart SMS. Issue: #3095</li> -<li>Load messages script fails to use https. Issue: #3081</li> -<li>Cannot access all fields for contact in select2. Issue: #3069</li> -<li>Configurable contact summary cards. Issue: #3037</li> -<li>Display additional information in contact profile. Issue: #2914</li> -<li>Support additional context for hiding/showing actions. Issue: #2913</li> -<li>Update Tour. Issue: #2212</li> -</ul> -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li>Targets aren&rsquo;t updated when no longer relevant. Issue: #3207</li> -<li>Task issues for restricted user when a report is deleted on the server. Issue: #3189</li> -<li>When navigating to the targets tab, I see a flash of the &ldquo;no analytics modules configured&rdquo; message. Issue: #3177</li> -<li>Update reports when loading the tab. Issue: #3178</li> -<li>Error after submitting form. Issue: #3157</li> -<li>Deleted message persists until refresh. Issue: #3003</li> -<li>Single delete and bulk delete does not immediately remove items from LHS in Reports tab. Issue: #3001</li> -<li>Tasks list says &ldquo;no tasks found&rdquo; before it&rsquo;s loaded. Issue: #1935</li> -<li>Labels not translated for generated report fields. Issue: #3154</li> -<li>Getting 502s after submitting task; Tasks not cleared until refresh. Issue: #3111</li> -<li>Do not know if patient ID is valid when processing Registrations/Report Actions. Issue: #3082</li> -<li>Patient contact creation should happen if a patient contact doesn&rsquo;t already exist. Issue: #3115</li> -<li>Task schedules created using the <code>reported_date</code> of a report do not show/hide at the expected time. Issue: #3097</li> -<li>Patients reports accepted even if no person has the <code>patient_id</code>. Issue: #3075</li> -<li>Registrations that clear previous registrations also clear themselves. Issue: #3074</li> -<li>Ensure useful commands is on medic-os $PATH by default. Issue: #2750</li> -<li>Family Members section header shows on person&rsquo;s profile. Issue: #3108</li> -<li>Uncaught exception triggers 500 response for subsequent requests. Issue: #3099</li> -<li>Broken links in app settings. Issue: #3088</li> -<li>Edit function not working for reports sent by unknown number. Issue: #3087</li> -<li>SMS reports do not show name in summary. Issue: #3084</li> -<li>Auto replies and Scheduled SMS are truncated to fit in single SMS. Issue: #3083</li> -<li>Bubble task count not showing on browser refresh. Issue: #3028</li> -<li>Scheduled messages not showing accurate date. Issue: #3012</li> -<li>SMS API sets messages to <code>scheduled</code> on POST. Issue: #3011</li> -<li>Scheduled messages not being sent. Issue: #3010</li> -<li>JavaRosa Parser should give a better error message when form definition on the web app is mismatched with the submitted message using medic collect. Issue: #2638</li> -<li>Contact <code>person</code>s don&rsquo;t show up in their places. Issue: #2385</li> -<li><code>setup_complete</code> is set too fast, so setup wizard is likely to be skipped. Issue: #2376</li> -<li>Submitting a family survey doesn&rsquo;t clear the task. Issue: #2265</li> -<li>Ages of children showing up strangely. Issue: #2191</li> -<li>Forms and icons fail to replicate on slow connections. Issue: #2113</li> -</ul> -<h2 id="uiux-improvements">UI/UX improvements</h2> -<ul> -<li>Clickable portion of action is smaller than item. Issue: #3104</li> -<li>&ldquo;Targets&rdquo; tab blank for admin users. Issue: #3029</li> -<li>Action button items get lost in RHS. Issue: #3005</li> -<li>Action button should always be left-most button in FAB. Issue: #3004</li> -<li>&ldquo;Up&rdquo; button at bottom of place/person pages. Issue: #2894</li> -<li>Status icon for <code>delivered</code> is orange instead of green. Issue: #2752</li> -<li>Display format for phone numbers. Issue: #1930</li> -</ul> -<h2 id="performance-improvements">Performance improvements</h2> -<ul> -<li>medic-api migration to remove couchmark. Issue: #3068</li> -<li>Extract XML forms into attachments. Issue: #3009</li> -</ul>Core: 2.10.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.10.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.10.1/ -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li>Sending a message from the Messages tab creates a message with uuid equal to database URL. Issue: #3242</li> -</ul>Core: 2.10.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.10.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.10.2/ -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li>Sentinel somehow infinitely loops and continually writes to its metadata file. Issue: #3275</li> -<li>API crashes after <code>/medic/_bulk_docs</code> gets called. Issue: #3268</li> -</ul>Core: 2.10.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.10.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.10.3/ -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li>Unicode support for storing enketo xml. Issue: #3308</li> -<li>Support negative values in xform fields better. Issue: medic/medic-projects#1624</li> -<li>Trigger enketo calc updates when option names are changed. Issue: #3281</li> -<li>New Household button missing. Issue: #3132</li> -<li>Change report form language without requiring refresh. Issue: #3174</li> -<li>Corrupted translation strings. Issue: #3305</li> -</ul> -<h2 id="uiux-improvements">UI/UX improvements</h2> -<ul> -<li>Show report subject name on patient page. Issue: #3309</li> -<li>Translate task schedule group titles. Issue: #3283</li> -<li>Add additional supported moment locales. Issue: #3282</li> -<li>Translate contact forms. Issue: #3323</li> -</ul>Core: 2.11.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.11.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.11.0/ -<h2 id="migration-notes">Migration notes</h2> -<ul> -<li><a href="https://github.com/medic/medic-webapp/issues/3230">#3230</a> changes patient ID generation so it automatically increases the length as needed, up to 13 digits. If you are validating incoming patient_ids in Sentinel, be sure to remove or correct any length restrictions, e.g. <code>^[0-9]{5}$</code> would become <code>^[0-9]{5,13}$</code>.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/3166">#3166</a> adds a new transition that adds patient_ids to every created person: <code>generate_patient_id_on_people</code>. Enable this transition if you want to send SMS about patients that may be created through the webapp.</li> -</ul> -<h2 id="features">Features</h2> -<ul> -<li>Drop id_format app setting in favour of auto-lengthening ids. Issue: #3230</li> -<li>Support for Nepali number characters. Issue: #3192</li> -<li>Show XForm in User&rsquo;s language. Issue: #3174</li> -<li>Sentinel needs to support these patient_id use cases. Issue: #3166</li> -<li>Enable users to export, even if they do not have permission to configure. Issue: #3113</li> -<li>Support <code>required_message</code> and <code>required_message</code> translations in Enketo. Issue: #3056</li> -</ul> -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li>namespace-form-fields migration causing Express toString fail. Issue: #3371</li> -<li>Cannot use Collect with username/password fields. Issue: #3118</li> -<li>No permissions available for configuration on first run. Issue: #3251</li> -<li>Fix outdated npm shrinkwrap entry for enketo-core. Issue: #3352</li> -<li>Set user-agent header in Medic Collect. Issue: #3334</li> -<li>Buttons in LHS FAB disappear. Issue: #3321</li> -<li>Analytics tab Hindi text is not aligning properly. Issue: #3297</li> -<li>Relative times are not translated. Issue: #3282</li> -<li>Submitting feedback no longer works. Issue: #3273</li> -<li>Full access users cannot create users, even when they have the appropriate permissions. Issue: #3262</li> -<li>Sending a message from the Messages tab creates a message with uuid equal to database URL. Issue: #3242</li> -<li>API does not check if <code>COUCH_NODE_NAME</code> is set at startup. Issue: #3226</li> -<li>Support Nepali calendar in outgoing SMS. Issue: #3219</li> -<li>New reports for same time period do not replace previous ones. Issue: #3160</li> -<li>API is picky about trailing slashes for SMS endpoint. Issue: #3152</li> -<li>Couchdb&rsquo;s startup.log does not include timestamps. Issue: #3131</li> -<li>Exporting feedback crashed api and it didn&rsquo;t come back. Issue: #3107</li> -<li>Export server logs from webapp does not work. Issue: #3089</li> -<li>Requesting audit log makes server unresponsive. Issue: #1789</li> -<li>Unrecognized input type found when trying to reset. Issue: #1655</li> -</ul> -<h2 id="uiux-improvements">UI/UX improvements</h2> -<ul> -<li>Contact profile fields collapse on mobile. Issue: #3306</li> -<li>Disable cancel buttons when saving. Issue: #1650</li> -</ul> -<h2 id="performance-improvements">Performance improvements</h2> -<ul> -<li>enketo-core package.json is included in inbox.js bundle. Issue: #3293</li> -<li>Use enketo xml cache for contact forms too. Issue: #3325</li> -</ul>Core: 2.11.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.11.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.11.1/ -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li>Cannot report via SMS about people who are registered in the web app. Issue: #3401</li> -<li>Results page CSS messed up in v2.11. Issue: #3369</li> -<li>The user needs an associated contact to create a contact. Issue: #3394</li> -<li>Error when adding Place with new person. Issue: #3420</li> -<li>Error after canceling and re-opening any contact creation form. Issue: #3448</li> -<li>namespace-form-fields migration : report bulk errors. Issue: #3371 (second part)</li> -</ul>Core: 2.11.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.11.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.11.2/ -<h2 id="performance-improvements">Performance improvements</h2> -<ul> -<li>Slow initial replication for users with lots of docs. Issue: #3508</li> -</ul>Core: 2.11.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.11.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.11.3/ -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li>The namespace-form-fields migration conflicts itself. Issue: #3534</li> -<li>In the create-patient-contacts migration provide a more complete list of potential patient_name locations. Issue: #3372</li> -</ul>Core: 2.12.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.12.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.12.0/ -<h2 id="features">Features</h2> -<ul> -<li>Add sync status indicator for offline users. Issue: #3357</li> -<li>Add gateway message delivery statuses. Issue: #3073</li> -<li>Add a replication_date property to records. Issue: #2180</li> -<li>Change patient id generation to store the length of id it&rsquo;s generating. Issues: #3505</li> -<li>Allow form upload through Form Configuration UI. Issue: #3433</li> -</ul> -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li>On small screen, cannot re-open date filter in history tab. Issue: #3467</li> -<li>Debug section of the About screen has some weird extra text. Issue: #3463</li> -<li>Medic Gateway runs into document update conflicts while trying to upload sms status. Issue: #3443</li> -<li>Stop <code>maintain_info_doc</code> transition from writing sentinel metadata. Issue: #3424</li> -<li>Webapp does not supply XML forms (XForms) to Collect. Issue: #3390</li> -<li>Cannot render form in Firefox. Issue: #3354</li> -<li>False positive error uploading translations. Issue: #3350</li> -<li>Exporting server logs fails with api 500. Issue: #3209</li> -<li>AWS EC2 AMI Regression: Does not currently boot. Issue: #3173</li> -<li>Form exits on Refresh/Reload in Tasks tab. Issue: #3090</li> -<li>Facility reference code fails to match when using integers and textforms. Issue: #1058</li> -</ul> -<h2 id="uiux-improvements">UI/UX improvements</h2> -<ul> -<li>Bullet displayed incorrectly. Issue: #3020</li> -<li>File chooser for importing translations should filter for .properties files. Issue: #3474</li> -<li>Show loading progress when app is starting. Issue: #3384</li> -</ul>Core: 2.12.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.12.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.12.1/ -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li>Improved error messages for SMS endpoint. Issue: #3587</li> -<li>Allow for empty SMS message Content. Issue: #3656</li> -<li>Implement 500 item max for bulk delete. Issue: #3605</li> -</ul> -<h2 id="security">Security</h2> -<ul> -<li>Fixed kanso packages that inadvertently cached credentials. Issue: #3648</li> -</ul>Core: 2.12.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.12.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.12.2/ -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li>Accept patient reports for patients created in app. Issue: #3740</li> -<li>Stop accept_patient_reports transition clearing messages for unrelated registrations. Issue: #3742</li> -</ul>Core: 2.12.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.12.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.12.3/ -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li>Accept messages with empty from or content. See: <a href="https://github.com/medic/medic-api/pull/185">https://github.com/medic/medic-api/pull/185</a></li> -</ul>Core: 2.12.4 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.12.4/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.12.4/ -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li>Fix issue with /api/v1/records. Issue: #3770</li> -</ul>Core: 2.12.5 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.12.5/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.12.5/ -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li>Fix bug where id generation wouldn&rsquo;t automatically increase id length when it ran out of ids. Issue: #3790</li> -</ul>Core: 2.13.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.13.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.13.0/ -<h2 id="migration-notes">Migration notes</h2> -<ul> -<li><a href="https://github.com/medic/medic-webapp/issues/2635">#2635</a> changes the context available to the configured contact summary script. The <code>contact</code> parameter no longer has information about parents. This information is now in an array called <code>lineage</code>. More information is available in the <a href="https://github.com/medic/medic-docs/blob/master/configuration/contact-summary.md">configuration documentation</a>.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/3546">#3546</a> changes the implementation of the <code>contact_summary</code> so instead of declaring the output on the last line of the script, now you have to return the output. Usually this is as easy as adding a return on the last line, so <code>output;</code> becomes <code>return output;</code>. More information is available in the <a href="https://docs.communityhealthtoolkit.org/apps/reference/contact-page/#contact-summary">configuration documentation</a>.</li> -</ul> -<h2 id="features">Features</h2> -<ul> -<li>multi_report_alerts transition added to sentinel. See the documentation in <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/transitions/">cht-docs</a>. Issue: <a href="https://github.com/medic/medic-webapp/issues/3416">#3416</a></li> -<li>Specify which forms can be downloaded using Collect. Issue: <a href="https://github.com/medic/medic-webapp/issues/3607">#3607</a></li> -<li>Validate sentinel transition configs at transition load time. Issue: <a href="https://github.com/medic/medic-webapp/issues/3585">#3585</a></li> -<li>Information from the contact-summary is now available as input to forms. Issue: <a href="https://github.com/medic/medic-webapp/issues/3413">#3413</a></li> -<li>Allow users to enter Bikram Sambat dates in Enketo forms on Android phones. Issue: <a href="https://github.com/medic/medic-webapp/issues/3513">#3513</a></li> -<li>Allow users to enter Bikram Sambat dates in Enketo forms in the web app. Issue: <a href="https://github.com/medic/medic-webapp/issues/3404">#3404</a></li> -<li>Registration of a person from a report/action form is now possible. Issue: <a href="https://github.com/medic/medic-webapp/issues/2912">#2912</a></li> -</ul> -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li>Couch responds with 200 when a bad app_settings file is uploaded. Issue: <a href="https://github.com/medic/medic-webapp/issues/3674">#3674</a></li> -<li>Select All in bulk delete doesn&rsquo;t work. Issue: <a href="https://github.com/medic/medic-webapp/issues/3646">#3646</a></li> -<li>Only serve collect-specific XML forms to collect. Issue: <a href="https://github.com/medic/medic-webapp/issues/3642">#3642</a></li> -<li>Exporting when filtered by report type exports more reports than are displayed in the web app. Issue: <a href="https://github.com/medic/medic-webapp/issues/3615">#3615</a></li> -<li>Bulk delete fails when deleting more than a few hundred records. Issue: <a href="https://github.com/medic/medic-webapp/issues/3605">#3605</a></li> -<li>Exporting reports filtered by place results in an empty xml. Issue: <a href="https://github.com/medic/medic-webapp/issues/3593">#3593</a></li> -<li>Requesting forms should respond with 4xx on client error. Issue: <a href="https://github.com/medic/medic-webapp/issues/3569">#3569</a></li> -<li>Can&rsquo;t view contacts for restricted user. Issue: <a href="https://github.com/medic/medic-webapp/issues/3517">#3517</a></li> -<li>Bad error message when associated contact is not available in the local DB. Issue: <a href="https://github.com/medic/medic-webapp/issues/3499">#3499</a></li> -<li>Gardener bug on startup when module_name is undefined. Issue: <a href="https://github.com/medic/medic-webapp/issues/3481">#3481</a></li> -<li>Reports list showing when user doesn&rsquo;t have proper permission. Issue: <a href="https://github.com/medic/medic-webapp/issues/3452">#3452</a></li> -<li>select2 in a repeat group does not work as expected in an Xform. Issue: <a href="https://github.com/medic/medic-webapp/issues/3430">#3430</a></li> -<li>{{patient_name}} not found when patient was created by xform. Issue: <a href="https://github.com/medic/medic-webapp/issues/3419">#3419</a></li> -<li>Search doesn&rsquo;t work in Nepali or with accented characters. Issue: <a href="https://github.com/medic/medic-webapp/issues/3392">#3392</a></li> -<li>Remove nested contacts. Issue: <a href="https://github.com/medic/medic-webapp/issues/2635">#2635</a></li> -</ul> -<h2 id="uiux-improvements">UI/UX improvements</h2> -<ul> -<li>Sync status cut off in mobile view. Issue: <a href="https://github.com/medic/medic-webapp/issues/3703">#3703</a></li> -<li>Hide Collect XForms from filter list in History tab. Issue: <a href="https://github.com/medic/medic-webapp/issues/3625">#3625</a></li> -<li>Split the form configuration page into JSON and XML tabs. Issue: <a href="https://github.com/medic/medic-webapp/issues/3559">#3559</a></li> -<li>Wrap text in tasks list. Issue: <a href="https://github.com/medic/medic-webapp/issues/3525">#3525</a></li> -<li>Task summary screen looks ugly on desktop. Issue: <a href="https://github.com/medic/medic-webapp/issues/3521">#3521</a></li> -<li>Send Report dropdown menu items are misaligned. Issue: <a href="https://github.com/medic/medic-webapp/issues/3469">#3469</a></li> -<li>Disable tasks tour for admins. Issue: <a href="https://github.com/medic/medic-webapp/issues/3144">#3144</a></li> -<li>Confirmation popup should not show on &lsquo;Error loading form&rsquo;. Issue: <a href="https://github.com/medic/medic-webapp/issues/3045">#3045</a></li> -<li>&lsquo;May lose data&rsquo; warning displayed when form has no fields. Issue: <a href="https://github.com/medic/medic-webapp/issues/1601">#1601</a></li> -</ul> -<h2 id="performance-improvements">Performance improvements</h2> -<ul> -<li>Remove modules.js attachment. Issue: <a href="https://github.com/medic/medic-webapp/issues/3684">#3684</a></li> -<li>Use alternative pagination method for running batched migrations. Issue: <a href="https://github.com/medic/medic-webapp/issues/3553">#3553</a></li> -<li>The read status of documents is now stored in a user specific database to reduce unnecessary doc updates. Issue: <a href="https://github.com/medic/medic-webapp/issues/2418">#2418</a></li> -</ul> -<h2 id="security">Security</h2> -<ul> -<li>Password validation so when creating or updating users the new passwords have to be at least 8 characters long and reasonably complex. Issue: <a href="https://github.com/medic/medic-webapp/issues/1472">#1472</a></li> -<li>Don&rsquo;t eval() user input. Issue: <a href="https://github.com/medic/medic-webapp/issues/3546">#3546</a></li> -<li>Set <code>Secure</code> setting on AuthSession cookie for HTTPS pages. Issue: <a href="https://github.com/medic/medic-webapp/issues/3182">#3182</a></li> -</ul>Core: 2.13.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.13.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.13.1/ -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li>Fix bug in extract-person-contacts migration introduced in 2.13.0 <a href="https://github.com/medic/medic-webapp/issues/4031">#4031</a></li> -<li>Remove the now invalid erlang migrations <a href="https://github.com/medic/medic-webapp/issues/4033">#4033</a></li> -</ul>Core: 2.13.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.13.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.13.2/ -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li>Force outputs to recalc on form load <a href="https://github.com/medic/medic-webapp/issues/4111">#4111</a></li> -</ul>Core: 2.13.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.13.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.13.3/ -<h2 id="improvements">Improvements</h2> -<ul> -<li>Bump libphonenumber to the latest</li> -</ul>Core: 2.13.4 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.13.4/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.13.4/ -<h2 id="improvements">Improvements</h2> -<ul> -<li>Bump libphonenumber for Nepal Smart Telecom.</li> -</ul>Core: 2.13.5 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.13.5/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.13.5/ -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li>Update Notification transition crashes sentinel if the patient id is misconfigured. <a href="https://github.com/medic/medic-webapp/issues/4121">#4121</a></li> -</ul>Core: 2.13.6 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.13.6/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.13.6/ -<h2 id="performance-improvements">Performance improvements</h2> -<ul> -<li>Drastically improve performance of form loading when the patient context is used, and that context is very large (e.g. you&rsquo;re including lineages). <a href="https://github.com/medic/medic-webapp/issues/4430">#4430</a></li> -</ul>Core: 2.13.7 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.13.7/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.13.7/ -<h2 id="performance-improvements">Performance improvements</h2> -<ul> -<li>Various PouchDB performance improvements were backported from 2.14. This includes increasing the PouchDB version and removing our use of pouchdb-worker.</li> -</ul>Core: 2.14.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.14.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.14.0/ -<p>Additional release notes are available <a href="../images/2.14.0.pdf">here</a>.</p> -<h2 id="migration-notes">Migration notes</h2> -<ul> -<li><a href="https://github.com/medic/medic-webapp/issues/3449">#3449</a>: We included a feature which makes it unnecessary to use a <code>repeat-relevant</code> node in Enketo forms to workaround a bug which created an empty child. This node should now be removed.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/3629">#3629</a>: We added more configurable text to the target widgets. Also, configuring an array of target titles is now deprecated in favor specifying a single translation key. Reconfigure your targets to specify values for <code>translation_key</code>, <code>subtitle_translation_key</code>, and <code>percentage_count_translation_key</code> properties. <a href="https://docs.communityhealthtoolkit.org/apps/reference/targets/">Full documentation</a>.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/3818">#3818</a>: We changed the way groups of scheduled messages are silenced when using <code>silence_for</code>. Previously, only the first group found to be in the silence window was silenced. Now, all groups are.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/4134">#4134</a>: Review use of <code>calculate=&quot;.&quot;</code> in all forms. If you have any XForm with with <code>calculate=&quot;.&quot;</code> you will need to modify the corresponding XLSForm, reconvert, and upload. The changes to make are: -<ul> -<li>From <em>type</em> <code>calculate</code> to <code>string</code></li> -<li>Make sure the <em>calculation</em> column is <strong>empty</strong></li> -<li>Have the <em>appearance</em> as <code>hidden</code></li> -<li>The <em>label</em> can be <code>NO_LABEL</code> to avoid warnings and bloat in the form</li> -</ul> -</li> -</ul> -<h2 id="features">Features</h2> -<ul> -<li><a href="https://github.com/medic/medic-webapp/issues/3096">#3096</a>: Allow users to take a photo while filling in an xform in Enketo and upload the photo with the form.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/3459">#3459</a>: Add format-date-tr() to our custom xpath functions to support translations of days and months in xforms.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/3450">#3450</a>: Show the logout button in the hamburger menu for android users who have the new <code>can_log_out_on_android</code> permission set.</li> -</ul> -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li><a href="https://github.com/medic/medic-webapp/issues/3944">#3944</a>: Unread reports bubble not working with deleted docs.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/3563">#3563</a>: Sentinel scheduling EDDs on a Sunday for all ANC registrations.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/3821">#3821</a>: Export api doesn&rsquo;t handle errors during export gracefully.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/4111">#4111</a>: Enketo or-output never shows initial value.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/4166">#4166</a>: People created via sentinel transitions are not replicated.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/4200">#4200</a>: Sentinel nulls out parent when multiple docs generated from one form submission.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/4201">#4201</a>: PNC schedule is not generated when a delivery report is submitted.</li> -</ul> -<h2 id="uiux-improvements">UI/UX improvements</h2> -<ul> -<li><a href="https://github.com/medic/medic-webapp/issues/3945">#3945</a>: Update the icons used for contacts.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/3904">#3904</a>: Make <code>user type</code> required in the edit user screen.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/3758">#3758</a>: Title of form is misaligned in list of reports and reports detail pane.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/3736">#3736</a>: Configurable profile field UI changes.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/3735">#3735</a>: Reports content row improvements.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/3734">#3734</a>: Tasks content row improvements.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/3732">#3732</a>: Message content row improvements.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/3731">#3731</a>: Update content row UI for consistency &amp; improved readability.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/3720">#3720</a>: Improve display of icons in configurable profile cards.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/3719">#3719</a>: Person &amp; place profile page UI changes.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/3704">#3704</a>: Improve password strength validation error messages.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/3629">#3629</a>: Update target widget cards and targets page layout.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/3597">#3597</a>: Update fonts to Noto.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/3561">#3561</a>: Display required fields when creating a &ldquo;restricted to a place&rdquo; user.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/2522">#2522</a>: Percentage target values are confusing.</li> -</ul> -<h2 id="performance-improvements">Performance improvements</h2> -<ul> -<li><a href="https://github.com/medic/medic-webapp/issues/3950">#3950</a>: Remove stats collection in API as it&rsquo;s no longer used.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/3913">#3913</a>: Sentinel&rsquo;s fetchHydratedDoc function should use only fetch contacts that aren&rsquo;t already present in the lineage list.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/4174">#4174</a>: Remove WebWorker for improved client database performance.</li> -</ul> -<h2 id="security">Security</h2> -<ul> -<li><a href="https://github.com/medic/medic-webapp/issues/3873">#3873</a>: Escape output to defend against javascript injection.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/3239">#3239</a>: Accessing webapp without logging in is possible.</li> -</ul>Core: 2.14.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.14.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.14.1/ -<h2 id="performance-improvements">Performance improvements</h2> -<ul> -<li><a href="https://github.com/medic/medic-webapp/issues/4430">#4430</a>: Drastically improve performance of form loading when the patient context is used, and that context is very large (e.g. you&rsquo;re including lineages).</li> -</ul>Core: 2.14.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.14.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.14.2/ -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li><a href="https://github.com/medic/medic-webapp/issues/3099">#3099</a>: Uncaught exception triggers 500 response for subsequent requests.</li> -</ul>Core: 2.14.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.14.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.14.3/ -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li><a href="https://github.com/medic/medic-webapp/issues/4457">#4457</a>: The z-score enketo widget is not usable.</li> -<li><a href="https://github.com/medic/medic-webapp/issues/4460">#4460</a>: Uncaught Exception: write after end error.</li> -</ul>Core: 2.15.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.15.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.15.0/ -<h2 id="whats-new">What&rsquo;s New</h2> -<h3 id="view-clinic-places-in-places-filter">View &lsquo;clinic&rsquo; Places in Places Filter</h3> -<p><em>You might have noticed that for SMS projects, CHW areas went missing from the places filter in the Reports tab. Good news! They are back.</em></p> -<p>When we started having CHWs log into the Medic app and register families, the places filter on the reports page became crowded with thousands of families, creating a performance issue. To get around that issue, we removed <code>'clinic'</code> level places from this filter drop-down so that users would only see health centers and CHW areas, no families. This meant that no one could filter by family, but that wasn&rsquo;t a needed feature.</p> -<h4 id="filter-bar-place-filter-highlighted">Filter bar, place filter highlighted</h4> -<p><img src="../images/2.15.0-3902-1.png" alt="image"></p> -<p>However, as we started using version 2 with our SMS projects, we needed the ability to filter by CHW area. In those projects, CHW areas are the <code>'clinic'</code> level places. In order to avoid further performance issues for projects that register families, we made this a configurable option.</p> -<p><img src="../images/2.15.0-3902-2.png" alt="image"></p> -<p>The default is to show only the <code>district_hospital</code> and <code>health_center</code> levels in the places filter. If you only want to see those two levels, there is no need to change your config. However, if you want to see the <code>clinic</code> places as well, you will need to add the following anywhere in your <code>app_settings.json</code> file:</p> -<pre tabindex="0"><code> &#34;place_hierarchy_types&#34;: [&#34;district_hospital&#34;, &#34;health_center&#34;, &#34;clinic&#34;], -</code></pre><p><strong>DO NOT</strong> display <code>clinic</code> places if your project is registering families as this will cause performance issues with admin or program manager (full access) users rendering the reports page.</p> -<p>[<a href="https://github.com/medic/medic-webapp/issues/3902">#3902</a>]</p> -<h3 id="just-in-time-messages">Just-in-Time Messages</h3> -<p><em>Just-in-time messages are available just in time for you to get those messages to that CHW who changed her phone number last week. And if she got any messages in the wrong language, we&rsquo;ve got you covered.</em></p> -<p>Previously, the phone number and outgoing message language were set at the time that a schedule was created. If a CHW changed her phone number, only new registrations would go to the new phone number. Any schedules that were ongoing would still send messages to her old phone number.</p> -<p>With this new feature, any updates to phone numbers are reflected in real time for any scheduled messages that are not yet pending or sent. In addition, if there are messages in your schedule going to the nurse or a CHW supervisor and they have to change their phone number, any messages in existing schedules that are going to be sent to them in the future will go to the new numbers.</p> -<p><img src="../images/2.15.0-3627-1.png" alt="image"></p> -<p>After updating Elizabeth&rsquo;s phone number, you&rsquo;ll see the following. Note that the auto-reply will have been sent to her old phone number because it was sent immediately after registration. If you look at the outgoing messages, you&rsquo;ll see that all have been updated to use Elizabeth&rsquo;s new number.</p> -<p><img src="../images/2.15.0-3627-2.png" alt="image"></p> -<p>This improvement also affects outgoing message language. If the outgoing message language is changed, all future messages, including those in ongoing schedules, will be sent in the new language. So if a CHW registers a pregnancy and the outgoing messages are generated in English initially, if the project changes the outgoing language to Swahili, any remaining messages will be sent out in Swahili.</p> -<p><img src="../images/2.15.0-3627-3.png" alt="image"></p> -<p>These changes will occur as soon as you reload the report(s) affected by the update. You should see the new phone number appear as the recipient of any future scheduled messages. The future scheduled messages should also be in the new language for outgoing messages, if you&rsquo;ve updated that.</p> -<p>In order for this feature to work for both phone number and translation updates, you must make sure your config:</p> -<ol> -<li><strong>Uses only translation keys for ALL outgoing messages</strong>, including scheduled messages, auto-replies, and notifications. You must remove ALL message arrays and replace with translation keys.</li> -<li><strong>Uses the correct alias for each message recipient for all scheduled messages</strong>. We are no longer recommending that you use <code>reporting_unit</code> as the message recipient for message schedules. Instead, use <code>clinic</code> (to send to the patient&rsquo;s CHW), <code>health_center</code> (to send to the nurse / primary contact of the health center), or <code>district</code> (to send to the primary contact at the district level). This will help ensure that phone numbers are updated in real time. Using <code>reporting_unit </code>for auto-replies and notifications is completely fine as these go out immediately after a report is received. The exception to this might be in cases where both nurses and CHWs are confirming visits and/or deliveries via SMS and you want to notify the CHW.</li> -</ol> -<p><a href="#improved-nurse-enabled-workflows-on-sms">More on this below</a>.</p> -<p>Your config will look something like this (**See the Standard <code><a href="https://github.com/medic/medic-projects/blob/master/standard/app_settings.json">app_settings.json</a></code> file for a full example)</strong>:</p> -<pre tabindex="0"><code>{ -&#34;name&#34;: &#34;ANC Reminders LMP&#34;, -&#34;translation_key&#34;: &#34;schedule.anc_lmp&#34;, -&#34;summary&#34;: &#34;&#34;, -&#34;description&#34;: &#34;&#34;, -&#34;start_from&#34;: &#34;lmp_date&#34;, -&#34;messages&#34;: [ -{ -&#34;translation_key&#34;: &#34;messages.schedule.anc.reminder&#34;, -&#34;group&#34;: 1, -&#34;offset&#34;: &#34;12 weeks&#34;, -&#34;send_day&#34;: &#34;monday&#34;, -&#34;send_time&#34;: &#34;09:00&#34;, -&#34;recipient&#34;: &#34;clinic&#34; -}, -{ -&#34;translation_key&#34;: &#34;messages.schedule.anc.followup&#34;, -&#34;group&#34;: 1, -&#34;offset&#34;: &#34;13 weeks&#34;, -&#34;send_day&#34;: &#34;monday&#34;, -&#34;send_time&#34;: &#34;10:00&#34;, -&#34;recipient&#34;: &#34;clinic&#34; -}, … -</code></pre><p>[<a href="https://github.com/medic/medic-webapp/issues/3627">#3627</a>]</p> -<h3 id="reports-tab-shows-patient-name">Reports Tab Shows Patient Name</h3> -<p><em>Have you been wondering why your CHW has been pregnant 58 times? Now you&rsquo;ll be able to see the patient name associated with the pregnancy from the main list of the Reports tab.</em></p> -<p>Previously, the Reports tab displayed the name of the person who submitted a particular report. We received feedback that it was more useful to display the name of the patient, so we have updated the reports list and the report detail page to show the patient&rsquo;s name at the top.</p> -<p>In the reports list, we show the patient&rsquo;s name first so that a CHW or manager can easily see which patient the report is about. Next is the type of form, which is the same as in previous versions. Below the name of the form, we have included the patient&rsquo;s hierarchy so that it&rsquo;s easy to see at a glance which CHW area that patient belongs to.</p> -<p><img src="../images/2.15.0-4053-1.png" alt="image"></p> -<p>If you then click on a report, you get to the report detail view, which again shows the patient&rsquo;s name at the top. Below is the name of the form, which is the same as in previous versions. After the form name is the patient&rsquo;s hierarchy, so it&rsquo;s clear which CHW area the patient belongs to. Finally, we list the person who sent the report and that person&rsquo;s phone number. In this case it&rsquo;s the CHW, Brandon Bone, but it could also be a nurse or another person in the health system. If the phone number does not match anyone in the Medic system, it will simply show the phone number of the person who submitted the report with no name listed.</p> -<p><img src="../images/2.15.0-4053-2.png" alt="image"></p> -<p>[<a href="https://github.com/medic/medic-webapp/issues/4053">#4053</a>]</p> -<h3 id="export-to-csv">Export to CSV</h3> -<p><em>Ever use our export to XML feature? Nope? We didn&rsquo;t either. Now you can export to CSV for all of your data analysis needs. Get your pivot table on!</em></p> -<p>Starting in 2.15.0, you can now export data directly from the Medic webapp in CSV format. You can choose to export all records or you may use the filters on the reports page to export a subset of reports. Whatever is appearing when you click Export is what will download, so if some filters have been applied before clicking Export, then your CSV will only include the reports that fit your filter criteria. Every export will include all forms and all fields, so if you download more than one type of form, you may find that you have a lot of columns, but this is just to ensure that we have a column for each field in each form since forms have different field names. Only admins can export because exporting returns all reports so it&rsquo;s only available to users who are allowed to access all data. \</p> -<p>Note that after you click Export, it may take 10 seconds or so before the Export starts downloading, so be patient! [<a href="https://github.com/medic/medic-webapp/issues/3594">#3594</a>]</p> -<h3 id="fine-grained-time-on-reports-list">Fine-Grained Time on Reports List</h3> -<p><em>At 15:02 on June 6th, we made it possible for you to see the exact time your reports came in.</em></p> -<p>Starting in 2.15.0, reports received today show the exact time. This makes it easier to see how many reports have come in today at a glance. This way you can see if the gateway is working properly. Once you get to the next day, reports will use relative times and dates as they had prior to this change.</p> -<p><img src="../images/2.15.0-3613.png" alt="image"></p> -<p>[<a href="https://github.com/medic/medic-webapp/issues/3613">#3613</a>]</p> -<h3 id="updated-styling-for-tasks-due-today">Updated Styling for Tasks Due Today</h3> -<p><em>Now you have no excuse not to complete your tasks, especially if you hate the color blue.</em></p> -<p>We made a small update to the Tasks list so that tasks due today are easier to find in the list. Starting in 2.15.0, tasks due today now have bold, blue due dates. This helps to distinguish them from tasks due in the past, which are bold and red, and tasks due in the future, which are still gray and regular weight.</p> -<p><img src="../images/2.15.0-4000.png" alt="image"></p> -<p>[<a href="https://github.com/medic/medic-webapp/issues/4000">#4000</a>]</p> -<h3 id="updated-icon-for-chw-area">Updated Icon for CHW Area</h3> -<p><em>Wondering what that petri dish icon was for? We&rsquo;re experimenting with a new icon for CHW areas.</em></p> -<p>Please swap out for your projects! The new icon is available in medic-projects and we&rsquo;ll be shipping it as the default icon starting in 2.16.0.</p> -<p><img src="../images/2.15.0-chw-area-icon.png" alt="image"></p> -<h3 id="new-date-filter">New Date Filter</h3> -<p><em>Now you can format dates on condition cards in the way that most of the world talks about them: date then month. Up next: how to get the US on the metric system?</em></p> -<p>We added a new date filter to provide more flexibility in how dates are displayed in condition cards. Now you can display dates like birthdates or EDDs in the format shown below (DD Month). This makes the date easier to read at a glance. If you want your date to show up in this format, use the <code>dayMonth</code> filter for date fields in your <code>contact-summary.js</code> file.</p> -<p><img src="../images/2.15.0-3721.png" alt="image"></p> -<p>[<a href="https://github.com/medic/medic-webapp/issues/3721">#3721</a>]</p> -<h3 id="improved-nurse-enabled-workflows-on-sms">Improved Nurse-Enabled Workflows on SMS</h3> -<p><em>Nurses are busy and can&rsquo;t always sit down in front of the webapp. We&rsquo;re now able to notify a CHW of when a nurse submits a patient report via SMS.</em></p> -<p>This feature is an enhancement for SMS workflows where nurses are confirming visits or deliveries using Textforms or Collect. We&rsquo;ve made it possible for the CHW to receive an SMS notification when a nurse confirms a visit or delivery via SMS. The diagram below describes how the messages would flow.</p> -<p><img src="../images/2.15.0-3412.png" alt="image"></p> -<p>To help make configuration easier, we have some new aliases available (<a href="#just-in-time-messages">mentioned earlier in this doc</a>). The new aliases are <code>clinic</code>, <code>health_center</code>, and <code>district</code> and they are calculated off of the patient. The <code>clinic</code> is the CHW area, so the message goes to the CHW; <code>health_center</code> is the facility level, so the message goes to the primary contact of the health facility; and <code>district</code> is the district hospital level, so the message goes to the primary contact of the district hospital.</p> -<p>If your project has both the CHW and the nurse confirming visits, you&rsquo;ll want to send only an auto-reply when the CHW reports a visit but you&rsquo;ll want to send both an auto-reply and a notification when the nurse reports a visit so that the nurse knows the visit report was received and the CHW knows the patient went to the facility. You can use a <code>bool_expr</code> on the CHW notification so that it only goes out when it&rsquo;s a nurse sending in the visit confirmation.</p> -<p>[<a href="https://github.com/medic/medic-webapp/issues/3412">#3412</a>]</p> -<h3 id="updated-enketo-ui">Updated Enketo UI</h3> -<p><em>UI fairy dust alert! We made forms prettier.</em></p> -<p>Starting in 2.15.0, Enketo forms have a larger font for the questions, more space between questions, and larger clickable areas for radio buttons and checkboxes. On Desktop, all forms have a header that persists for all pages of the form so that the user knows which form she is filling in.</p> -<p><img src="../images/2.15.0-3961-1.png" alt="image"></p> -<h4 id="before">Before</h4> -<p><img src="../images/2.15.0-3961-2.png" alt="image"></p> -<h4 id="after">After</h4> -<p><img src="../images/2.15.0-3961-3.png" alt="image"></p> -<p>[<a href="https://github.com/medic/medic-webapp/issues/3961">#3961</a>]</p> -<h3 id="allow-users-to-compare-translations-to-translation-keys">Allow Users to Compare Translations to Translation Keys</h3> -<p><em>We&rsquo;re making it easier to keep track of what you&rsquo;re translating so we&rsquo;re all speaking the same language, even when we&rsquo;re not.</em></p> -<p>In 2.15.0, we added an option to the left side drop-down so that you can display the translation keys. This makes it easier to know what you are translating and where it will appear in the app. It also allows PMs to more easily update translations for scheduled messages, targets, and tasks titles, among other things.</p> -<p><img src="../images/2.15.0-3022.png" alt="image"></p> -<p>[<a href="https://github.com/medic/medic-webapp/issues/3022">#3022</a>]</p> -<h3 id="death-reporting">Death Reporting</h3> -<p><em>More than a feature, we&rsquo;re now supporting death reporting workflows which allow a user to report a death, another user to confirm that death, and all users to see which family members are deceased on both person and household profiles. Also includes the ever-popular &ldquo;resurrection&rdquo; feature which allows us to undo a death.</em></p> -<p>Our app now supports an expanded workflow for death reporting. The sketch below outlines the basic death reporting workflow that can be done on SMS or in our app, or with a combination of both app and SMS. Some health systems may not require a confirmation of the death by someone other than the CHW, or the person doing the confirmations may not be a user of our tools. In that case, the workflow can be simplified to just step one, where the CHW reports the death.</p> -<p>Step one (death report) can be submitted by the CHW via SMS or the mobile app. Once the death report is submitted, the manager or nurse can get an SMS notification or a task to follow up with the community to confirm the death (until the supervisor app is built, the manager/nurse would need access to patient reports in order to get this task). The manager or nurse would then confirm the death and submit a death confirmation report. Once the death is confirmed, the person is marked as deceased in the app.</p> -<p><img src="../images/2.15.0-3956-1.png" alt="image"></p> -<p>There are a few things that can happen when someone is marked as deceased. First, the app can update a person&rsquo;s profile to make it clear the person is deceased. It can also automatically update the family (or CHW area for SMS projects) profile to separate any deceased people from other people in the family or area. Finally, any deceased people will appear at the bottom of search results. <strong>Tech leads</strong>: make sure you also configure all tasks so that they are cleared when a person is confirmed to be deceased. The app will add a <code>date_of_death</code> field to the person&rsquo;s doc once the death is confirmed.</p> -<p><img src="../images/2.15.0-3956-2.png" alt="image"></p> -<p>What happens if a death is confirmed but it turns out to have been done in error? We also support a workflow for undoing a death. A CHW can request a correction and if the nurse or manager agrees that a correction is needed, she can undo the death. This reverses all of the profile changes noted above.</p> -<p>To configure a death reporting workflow, you will need:</p> -<ul> -<li>Death report form</li> -<li>Death confirmation form</li> -<li>Request correction form</li> -<li>Undo death form</li> -<li>Task or SMS notification for nurse or manager to confirm the death</li> -<li>Task or SMS notification for nurse or manager to undo the death</li> -<li>New configuration for all person-level tasks so that they clear as soon as the death confirmation form is submitted if that form indicates the person is actually deceased</li> -<li>Enable the <code>death_reporting</code> Sentinel transition</li> -<li>A <code>death_reporting</code> property in <code>app_settings</code> so that you can indicate which form should confirm deaths and which form should undo deaths</li> -</ul> -<p>Here&rsquo;s an example of what you would add to <code>app_settings</code> to get the profile updates to work:</p> -<pre tabindex="0"><code>&#34;transitions&#34;: { -&#34;death_reporting&#34;: true -}, -&#34;death_reporting&#34;: { -&#34;mark_deceased_forms&#34;: [ -&#34;death_confirmation&#34; -], -&#34;undo_deceased_forms&#34;: [ -&#34;undo_death&#34; -] -} -</code></pre><p>The Sentinel transition simply adds a date of death of the person to their profile doc. Currently, it will be the <code>reported_date</code> of the death confirmation form. This is not currently customizable, but you can choose to display the date the CHW entered as the date of death on the person&rsquo;s profile. We are planning to improve this in 2.16.0 by allowing you to use a <code>date_of_death</code> field in your death confirmation form as the date that is transferred to the person&rsquo;s doc. If you want to record dates of death for people but not have the profiles update, just use the transition and don&rsquo;t add the <code>death_reporting</code> property to <code>app_settings</code>.</p> -<p>[<a href="https://github.com/medic/medic-webapp/issues/3956">#3956</a>]</p> -<h3 id="report-linkages">Report Linkages</h3> -<p><em>Please accept our apology&hellip; We know you loved showing off your SQL skills, but we&rsquo;ve made it easier to link certain forms together in PostgreSQL with the addition of a few new fields.</em></p> -<p>We&rsquo;ve added some new fields to reports to help make it easier to write queries in analytics. You&rsquo;ll need to run sentinel transitions for these to work. All of these fields can be added to SMS forms and some can be added to app forms. Here&rsquo;s a list of what&rsquo;s been added:</p> -<ul> -<li>Any report that runs the <code>registration</code> transition and invokes the <code>add_patient</code> (or deprecated <code>add_patient_id</code>) trigger will create a patient doc with a <code>source_id</code> field referencing the report and a <code>created_by</code> field referencing the ID of the contact who submitted the report. -<ul> -<li>This makes it possible to know who created each person and links the person to the form used to register them.</li> -</ul> -</li> -<li>Any report that clears a schedule either via the <code>accept_patient_reports</code> transition or the <code>clear_schedule</code> trigger in the <code>registration</code> transition will have the ID of the latest matching registration recorded in the <code>registration_id</code> field. -<ul> -<li>If you register a pregnancy and then submit a V form, the V form will have a <code>registration_id </code>which is the ID of the most recent pregnancy registration.</li> -<li>If you submit a D form for a pregnancy that was previously registered, the D form will have a <code>registration_id</code> which is the ID of the most recent pregnancy registration.</li> -<li>This also applies to any other schedule that is generated by registering a patient (PNC, Immunizations, etc.). Any visit form will have the ID of the most recent registration.</li> -</ul> -</li> -<li>Any scheduled message that is cleared before being sent will have a <code>cleared_by</code> field with the report that caused the task to be cleared. -<ul> -<li>If you register a pregnancy and receive the first visit reminder (Please remind Janet to go to the clinic for ANC), then submit a V form, the second reminder (Did Janet attend her visit?) would be cleared. That reminder would have a <code>cleared_by</code> field which is the ID of the V form.</li> -<li>If you register a pregnancy and then send in a D form, all remaining reminders are cleared. Each of the reminders would have a <code>cleared_by</code> field which is the ID of the V form.</li> -<li>This also applied to any other scheduled messages that are cleared - we will always know which report cleared the message.</li> -</ul> -</li> -</ul> -<p>[<a href="https://github.com/medic/medic-webapp/issues/3959">#3959</a>]</p> -<h3 id="permissions-to-showhide-call-and-message-buttons">Permissions to Show/Hide Call and Message Buttons</h3> -<p><em>Call me, maybe.</em></p> -<p>We&rsquo;ve added two new permissions to our list so that you can determine whether users should see the call and message buttons when they are viewing a person&rsquo;s profile. These buttons will appear by default, but you can remove a user type&rsquo;s access to them, in case you want to prevent CHWs from having the call or message options, for example.</p> -<p><img src="../images/2.15.0-3657.png" alt="image"></p> -<p>[<a href="https://github.com/medic/medic-webapp/issues/3657">#3657</a>]</p> -<h2 id="bug-fixes">Bug Fixes</h2> -<h3 id="contact-pagination-fixed-for-restricted-users">Contact Pagination Fixed for Restricted Users</h3> -<p><em>Call off the search party, your missing families have been found!</em></p> -<p>You might have noticed that some contacts were not appearing on the people tab. Turns out we had a pagination bug. Sorry for those who couldn&rsquo;t find their missing families! This is now fixed.</p> -<p>[<a href="https://github.com/medic/medic-webapp/issues/4085">#4085</a>]</p>Core: 2.16.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.16.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.16.0/ -<h2 id="whats-new">What&rsquo;s New</h2> -<h3 id="view-date-last-visited-for-places-on-the-people-tab">View date last visited for places on the people tab</h3> -<p><em>Because knowing where you&rsquo;ve been helps you know where you&rsquo;re going next! - Medic proverb</em></p> -<p>In order to help CHWs achieve full coverage of every family or household they care for, we&rsquo;ve added an optional feature to update the list of families or areas to display the date that family or area was last visited. You can use any patient- or family-level form or forms to update the date last visited. You may run into performance issues if you configure this to look at forms submitted very frequently. For example, we expect five forms submitted only once a month to work better than two forms submitted every day. Make sure you test!</p> -<p><img src="../images/2.16.0-4526-1.png" alt="Screenshot"></p> -<p>In order to see the date last visited instead of the primary contact, make sure you give your CHWs (or whichever users need to see date last visited) the <code>can_view_last_visited_date</code> permission. Once that permission is enabled, you&rsquo;ll be able to display the date the family was last visited in the list. Make sure you have this permission enabled in <code>app_settings</code> to avoid overwriting the permission when you upload your config:</p> -<pre tabindex="0"><code>{ -&#34;name&#34;: &#34;can_view_last_visited_date&#34;, -&#34;roles&#34;: [ -&#34;chw&#34;, -&#34;district_admin&#34; -] -} -</code></pre><p>When you first implement this, you&rsquo;ll see a long list of &ldquo;Last visit unknown&rdquo;. You&rsquo;ll need to indicate which forms should be included in the calculation for the date last visited. You can use forms at the person or family level, though it is preferable to limit the number of forms included to the minimal number to avoid performance issues.</p> -<p>Indicating that a form should be included in the calculation for the date last visited is as simple as adding a field called <code>visited_contact_uuid</code> to your form. This should be a <code>calculate</code> field that contains the family/household (<code>clinic</code> level place) UUID. Make sure this is a top-level field (not in any group and will be in <code>doc.fields.visited_contact_uuid</code> when you submit). To test, upload your form with this field, fill it out and submit, and your people list will update with the correct relative date. Note that you may need to refresh the people tab by either clicking refresh or navigating to another tab and then back.</p> -<p>Example XLSForm (this is a patient-level form):</p> -<p><img src="../images/2.16.0-4526-2.png" alt="Screenshot"></p> -<p>[<a href="https://github.com/medic/medic-webapp/issues/4526">#4526</a>]</p> -<h3 id="sort-places-by-date-last-visited">Sort places by date last visited</h3> -<p><em>Puts the households who haven&rsquo;t been seen in the longest time at the top, because you know how you hated it in school when the kids whose last names ended in &ldquo;A&rdquo; always got to be first in line?</em></p> -<p>When you enable the permission to see the date last visited in the people tab list, you will notice that a new icon appears in the top bar. This icon is for sorting the list of people. The default sort is alphabetical, as you have probably noticed. The new icon allows you to sort your families or households by the date last visited. You will need to enable the permission for viewing the date last visited and configure one or more forms to be included in the calculation of the last visit date for each family (<a href="#view-date-last-visited-for-places-on-the-people-tab">see previous section</a>). Once you&rsquo;ve done that, the sort will just work. By default, when you sort by &ldquo;date last visited,&rdquo; the households you saw <em>least</em> recently will be at the top, gradually increasing to those you saw most recently. Because you know, you should prioritize the households who haven&rsquo;t been seen in the longest amount of time.</p> -<p>Fun fact: We drew this feature for the first time while chatting about visit practices in a workshop with LG CHPs in Uganda. See the drawings on the lower right that shows a family list being reordered by date, with those visited least recently at the top.</p> -<p><img src="../images/2.16.0-4524-1.jpg" alt="Image"></p> -<p><img src="../images/2.16.0-4524-2.png" alt="Comparison"></p> -<p><a href="https://github.com/medic/medic-webapp/issues/4524">#4524</a></p> -<h3 id="toggle-universal-health-coverage-mode-on-and-off">Toggle Universal Health Coverage mode on and off</h3> -<p><em>We&rsquo;re not trying to play bi-partisan politics, we&rsquo;re just trying to test some new features.</em></p> -<p>Showing the date last visited and being able to sort contacts by the date they were last visited by a CHW are the first two features designed to achieve Universal Health Coverage (UHC). While the world is quickly catching onto the importance of this, we know that not all programs are ready to implement this approach. Feature #4525 makes it possible to turn these UHC features on or off. It&rsquo;s still very early in our UHC design process so we&rsquo;ll be closely monitoring these features and conduct feedback sessions, so the UHC features previously described may still undergo some iteration. Check on the status of these features before implementing them in your project.</p> -<p><a href="#view-date-last-visited-for-places-on-the-people-tab">As was described earlier</a>, enabling UHC mode is as simple as giving a user the permission <code>can_view_last_visited_date</code>. This will give the user the last visit date in the people list and the ability to sort by the last visit date. Make sure you add the needed field to the forms you want to include as part of the last visit date calculation to avoid having a long list of &ldquo;Last visit unknown&rdquo;. Once CHWs start submitting these forms, the dates on the list will slowly update.</p> -<p><img src="../images/2.16.0-4525.png" alt="Comparison"></p> -<p>[<a href="https://github.com/medic/medic-webapp/issues/4525">#4525</a>]</p> -<h3 id="expand-use-of-the-verify-button-for-manager-review-of-chw-reports">Expand use of the verify button for manager review of CHW reports</h3> -<p><em>The exciting world of data cleaning just got better.</em></p> -<p>We&rsquo;ve created a new and improved verification workflow for managers. Before, we just had two states for each report, verified and unverified. A report was verified by clicking the verify button.</p> -<p><img src="../images/2.16.0-4529-1.png" alt="Comparison"></p> -<p>With the new feature, clicking on the verify button opens a small menu with two options: Correct and Has errors. This allows a manager to review a report and mark it correct if it looks good and mark it as having errors if they need to follow up with the CHW for further verification. The status will also be synced to the CHW&rsquo;s phone so they can see which reports were marked correct by their manager and which were marked as having errors.</p> -<p><a href="#ability-to-add-more-user-roles">With our new configurable roles</a>, it is also possible for managers to have the ability to review reports while preventing CHWs from having this option, even if both are restricted/offline-first users.</p> -<p>This feature was built to support the mRDT error-checking workflow for the Muso Innovation Network. Supervisors must log in to view photos of the mRDTs (<a href="https://docs.google.com/document/d/1uXSqntenhxlGOeFtP7ScLcFmoid3kagPYn-EDoodP3s/edit#heading=h.4bwl8oo2mtpi">built in 2.14.0</a>) and check that the diagnosis reported by the CHW matches the photos. If the supervisor sees an error, they can now use this feature to mark it as such. Error rates are part of the CHW&rsquo;s performance metrics so supervisors can see which CHWs are in need of additional training on implementing mRDTs. To complement this workflow, we built a widget on the CHP&rsquo;s Targets page to accompany this workflow that tracks their monthly error rate.</p> -<p><img src="../images/2.16.0-4529-2.png" alt="Comparison"></p> -<p>[<a href="https://github.com/medic/medic-webapp/issues/4529">#4529</a>]</p> -<h3 id="allow-supervisors-to-review-specific-patient-reports">Allow supervisors to review specific patient reports</h3> -<p><em>We call this feature Super-Vision! Supervisors now have the magical ability to see reports about patients without being able to view the patients themselves. #superpowers-for-supervisors</em></p> -<p>Apart from Standard, where we have very small numbers of users, supervisors are not able to view all patient reports due to device storage limits and app performance issues. However, there may be times when there are specific reports about patients that supervisors need to see, review, or take action on. This feature is not meant to allow supervisors to view all patient reports about every patient and it does not give supervisors the ability to see patient profiles. It is meant to better support workflows like reviewing mRDT images or confirming death reports. Currently, if a supervisor wanted to review an mRDT image in a patient assessment form or get a death confirmation task, she would need to have access to the patient. With this new feature, the supervisor only needs access to the patient assessment form or the death report form in order to do the review or receive the task.</p> -<p>Below you&rsquo;ll see the Reports page from the supervisor&rsquo;s view. In this example, the supervisor has access to families and family reports, but does not have access to patients. Using this feature, we&rsquo;ve configured it so that the supervisor <strong>does</strong> have access to patient assessment forms so that they can be reviewed, even though she doesn&rsquo;t have access to the patients themselves. As you can see in the screenshots below, the CHW has access to all patient- and family-level reports while the supervisor has access to all family-level reports AND the patient assessment report, but no other patient-level reports.</p> -<p><img src="../images/2.16.0-4591-1.png" alt="Comparison"></p> -<p>If you want a report submitted by the CHW about a patient to be viewable by the supervisor, you&rsquo;ll need to make sure that the supervisor has access to families (<code>replication_depth</code> of 2 in most cases) but no access to patients. You will also need to add a field to the patient-level form that the supervisor needs access to. The field should be a <code>hidden</code> field called <code>needs_signoff</code> that must be set to true in cases where the report needs to be reviewed by a manager. For example, if you only wanted a manager to review patient assessments where an mRDT was conducted, come up with a conditional statement to set the variable to true only for assessments where the mRDT was done. This hidden field must be a top-level field (not in any group and will be in <code>doc.fields.needs_signoff</code> when you submit). To test, upload your form with this field, fill it out and submit. Log in as a user who is the supervisor of the CHW who submitted the form and make sure you are able to see the form you just submitted.</p> -<p>Example XLSForm: -<img src="../images/2.16.0-4591-2.png" alt="XLSForm"></p> -<p>[<a href="https://github.com/medic/medic-webapp/issues/4591">#4591</a>]</p> -<h3 id="add-new-statuses-to-the-status-filter-on-the-reports-page">Add new statuses to the status filter on the reports page</h3> -<p><em>Because even Superheros could use some help organizing their Inbox.</em></p> -<p>With the <a href="#expand-use-of-the-verify-button-for-manager-review-of-chw-reports">new feature that allows managers to review reports</a>, we&rsquo;ve updated the status filter to help managers easily find reports they need to review or reports they marked as having errors that need follow up. With these changes, we have retained the ability to filter by valid and invalid SMS reports. Here&rsquo;s what happens when you select each of these filter options:</p> -<ul> -<li><strong>Manager review - &ldquo;Not reviewed&rdquo;:</strong> displays all valid reports (SMS and app) that have not been marked as correct or having errors by a manager</li> -<li><strong>Manager review - &ldquo;Reviewed: errors&rdquo;:</strong> displays all reports (app or SMS) that were marked as having errors by a manager</li> -<li><strong>Manager review - &ldquo;Reviewed: correct&rdquo;:</strong> displays all reports (app or SMS) that were marked as correct by a manager</li> -<li><strong>SMS validity - &ldquo;Valid SMS&rdquo;:</strong> displays all SMS reports that were valid when received (regardless of whether they are &ldquo;Not reviewed&rdquo;, &ldquo;Reviewed: errors&rdquo;, or &ldquo;Reviewed: correct&rdquo;)</li> -<li><strong>SMS validity - &ldquo;Invalid SMS&rdquo;:</strong> displays all SMS reports that were invalid when received and have red dots (applied automatically)</li> -</ul> -<p>For example, if a manager wanted to review any recent patient assessments that they might have access to, she would filter by the patient assessment form type and the &ldquo;Manager review - Not reviewed&rdquo; status filter. This would give a list of assessment forms that have not yet been reviewed.</p> -<p><img src="../images/2.16.0-4577-1.png" alt="Comparison"></p> -<h4 id="before">Before</h4> -<p><img src="../images/2.16.0-4577-2.png" alt="Screenshot"></p> -<h4 id="after">After</h4> -<p><img src="../images/2.16.0-4577-3.png" alt="Screenshot"></p> -<p>[<a href="https://github.com/medic/medic-webapp/issues/4577">#4577</a>]</p> -<h3 id="use-family-equity-score-in-tasks-and-actions-about-family-members">Use family equity score in tasks and actions about family members</h3> -<p><em>Jokes aside, this features is an important step in being able to deliver a preferential option for the poor. Read more about this here: <a href="https://www.pih.org/article/in-the-company-of-the-poor">https://www.pih.org/article/in-the-company-of-the-poor</a></em></p> -<p>Last year, we began implementing an equity survey in Kenya that determines a household&rsquo;s wealth quintile based on a number of questions about their home, possessions, and access to safe drinking water, among other things. Although the wealth quintile could be used to generate tasks for the household, we wanted to make the equity quintile useful in targeting interventions at the patient level. With this feature, we&rsquo;re now able to use the equity score to generate specific tasks about patients, such as increasing the number of pregnancy follow-ups for women in poorer households. We&rsquo;re also able to display specific notes or questions within forms for patients in lower wealth quintiles, such as asking additional questions about children in poorer households when they are being screened for malnutrition. This feature makes the equity score more usable in patient workflows and is the first step toward greater targeting of services.</p> -<p>We&rsquo;re still learning how to deliver this feature well. We&rsquo;ve heard from CHWs that they resist the approach that some families in their community would be &ldquo;labeled&rdquo; as poor in the app, even if they acknowledge that they prioritize these families differently in their daily workflows. Though this feature allows that &ldquo;labeling&rdquo; to happen on the backend, it may be helpful and important for a CHW to know why a family or patient has been asked these additional questions or given more frequent home visits. The Impact and Design teams are currently getting feedback on appropriate options. Please consult your designer and share back what you&rsquo;re thinking and trying so we can all learn together!</p> -<p>The feature works by pulling specific fields from a form filled at the family level and copying them to the profile of each family member in the household. For now, we are limited to the national quintile and the urban quintile. We don&rsquo;t pull the raw scores, just the quintiles, which are easier to use in tasks and forms. To make sure your quintiles get copied, simply make sure that you have fields called <code>NationalQuintile</code> and <code>UrbanQuintile</code> defined as top-level fields in the form you&rsquo;re using at the household level. These must end up at <code>doc.fields.NationalQuintile</code> and <code>doc.fields.UrbanQuintile</code> for this feature to work correctly.</p> -<h4 id="sample-xlsform">Sample XLSForm</h4> -<p><img src="../images/2.16.0-4700.png" alt="XLSForm"></p> -<p>[<a href="https://github.com/medic/medic-webapp/issues/4600">#4600</a>]</p> -<h3 id="ability-to-add-more-user-roles">Ability to add more user roles</h3> -<p><em>Because CHW Janet, CHW Supervisor Ann, and Paul the Data Guy don&rsquo;t wear the same hat in real life and now they don&rsquo;t have to in the app, either.</em></p> -<p>In order to make it possible to show the UHC mode features for some CHWs and not others, we decided to go ahead and implement configurable user roles. Up until now, we were limited to one user role for users that need offline access to their data. You have probably heard of the &ldquo;restricted&rdquo; user - this is the user type that allowed a CHW or manager to access data offline. However, this was limiting because every user that had offline data access had to have the same set of permissions. So, for example, a CHW and a supervisor had to have the same set of permissions, requiring both to have the permission to do something (such as delete reports) or neither would have permission to do it. This led to restrictions in what supervisors could do in order to avoid giving too many permissions to CHWs.</p> -<p>With 2.16.0, we now have configurable roles! This means that a CHW and a supervisor can both access data offline but can have unique roles so that they can have different permissions. For example, a CHW might not be able to delete data, review reports, or bulk delete, but a supervisor could be given the ability to do one or more of these actions without affecting the permissions of the CHW.</p> -<p>New roles can be created in the configuration pages. Instead of a permissions page, we now have a roles &amp; permissions page. On this page we have a new tab for creating and managing user roles. Creating a new user role is as simple as creating a name and translation key for the user role and making sure you select the &ldquo;Offline&rdquo; checkbox if this user type needs access to data offline.</p> -<p><img src="../images/2.16.0-4525-1.png" alt="Screenshot"></p> -<p>The permissions page has also been updated to better accommodate a greater number of roles. We&rsquo;ve changed the layout to a new format. This layout is temporary for now and we expect to improve it later in the year. You can see in this example that we now have roles for Supervisor, CHW and Online User, but you can create as many roles as you need, both for users that need access to data offline and for those accessing the app online.</p> -<p><img src="../images/2.16.0-4525-2.png" alt="Screenshot"></p> -<p>[<a href="https://github.com/medic/medic-webapp/issues/4525">#4525</a>]</p> -<h3 id="changed-use-of-colored-dot-on-the-reports-page">Changed use of colored dot on the reports page</h3> -<p><em>Seeing spots? Or do we mean dots? Either way, you&rsquo;re about to see a whole lot less of them. If they don&rsquo;t go away, consult a tech lead or your family eye doctor.</em></p> -<p>Our use of the controversial &ldquo;green dot&rdquo; and &ldquo;red dot&rdquo; on the reports page hearkens back to the days when 100% of Medic users where using SMS. It was a quick way to see which reports were valid and which were not. Once we started having users on the mobile app, this feature became less useful because every single report was valid. This is because we have more control over how the data is entered and do not rely on the end user entering a valid Medic ID. We decided to modernize our use of red and green dots so that they make more sense across our SMS and app projects. Here&rsquo;s our new approach:</p> -<ul> -<li>We&rsquo;ve removed the green dot completely from valid incoming reports. No dot = all good here = no attention needed. This is regardless of whether it came from the mobile app, a TextForm, or Medic Collect.</li> -<li>Without green dots on every report, red and green dots now have more meaning. Using our new manager review feature (#4529), a manager can now use a green dot to indicate that a form is correct, and a red dot if it contains an error.</li> -<li>We also know that it&rsquo;s important to be able to see invalid reports coming in over SMS, either TextForms or Collect forms. For these invalid SMS reports, the red dot with an &ldquo;x&rdquo; is automatically applied. You can filter to see all invalid SMS reports by using the status filter dropdown. Here&rsquo;s a summary of how the red and green dots now work:</li> -</ul> -<p><img src="../images/2.16.0-4574-1.png" alt="Comparison"></p> -<p>Here&rsquo;s a look at the reports list so you can see the before and after:</p> -<p><img src="../images/2.16.0-4574-2.png" alt="Comparison"></p> -<p>The screenshot on the left shows the current use of dots for an SMS project. On a mobile app project, there&rsquo;s a green dot on every report.</p> -<p>In the screenshot on the right, we&rsquo;ve removed the green dots from valid reports. The green dot can be applied when a supervisor has reviewed a report and marked it correct. The red dot for invalid SMS reports is still automatically applied. Additionally, a supervisor can review a report and apply the red dot if the report has errors.</p> -<p>The supervisor review feature is optional and should only be encouraged if it&rsquo;s part of their workflow. It&rsquo;s also perfectly fine for supervisors to use it selectively - to only mark reports with errors or to only review certain reports (such as mRDT, but not child assessments). The intention isn&rsquo;t to give supervisors more review work but to reduce the noise on the Reports page and make the information that gets shown there more meaningful.</p> -<p>[<a href="https://github.com/medic/medic-webapp/issues/4574">#4574</a>]</p> -<h3 id="updated-header-for-reports">Updated header for reports</h3> -<p><em>UI fairy dust alert! We made viewing reports on mobile prettier.</em></p> -<p>We&rsquo;ve updated the header when you&rsquo;re viewing reports in the Reports page. The new design works much better on smaller screens:</p> -<ul> -<li>We&rsquo;ve removed the detailed date</li> -<li>We&rsquo;ve moved the relative date to the left side, below the sender name.</li> -<li>If you hover over the relative date, the full date and exact time the report was received will appear. If you&rsquo;re on a touchscreen, do a long tap on the relative date to see the detailed date.</li> -<li>If a manager has reviewed this report and marked it correct or marked it as having errors, the green or red dot would appear at the top right.</li> -</ul> -<p><img src="../images/2.16.0-4576-1.png" alt="Comparison"></p> -<h4 id="before-1">Before</h4> -<p><img src="../images/2.16.0-4576-2.png" alt="Screenshot"></p> -<h4 id="after-1">After</h4> -<p><img src="../images/2.16.0-4576-3.png" alt="Screenshot"></p> -<p>[<a href="https://github.com/medic/medic-webapp/issues/4576">#4576</a>]</p> -<h3 id="allow-death-confirmation-form-to-specify-the-date-of-death">Allow death confirmation form to specify the date of death</h3> -<p><em>Because the only thing worse than getting your birthday wrong is…</em></p> -<p>In 2.15.0, <a href="./2.15.0.md#death-reporting">we added support for death reporting workflows</a>. One glitch we realized was that the date of death was being set to the date that the death confirmation form was submitted. With 2.16.0, we&rsquo;ve added an improvement to the workflow. It&rsquo;s now possible to set a specific date of death in your death confirmation form. This makes the workflow more flexible by allowing a nurse to enter the confirmed date of death in the death confirmation form.</p> -<p>In order to include a user-selected date of death as the official date of death listed in the Medic app, you&rsquo;ll need to make sure there is a field in the death confirmation form that lists the exact date of death and then indicate in your <code>app_settings</code> that this field should be used for the date of death.</p> -<p>Example XLSForm with <code>date_of_death</code> field: -<img src="../images/2.16.0-4636.png" alt="Screenshot"></p> -<p>Example <code>app_settings</code> configuration:</p> -<pre tabindex="0"><code>&#34;transitions&#34;: { -&#34;death_reporting&#34;: true -}, -&#34;death_reporting&#34;: { -&#34;mark_deceased_forms&#34;: [ -&#34;death_confirmation&#34; -], -&#34;undo_deceased_forms&#34;: [ -&#34;undo_death&#34; -], -&#34;date_field&#34;: &#34;fields.date_of_death&#34; -} -</code></pre><p>[<a href="https://github.com/medic/medic-webapp/issues/4636">#4636</a>]</p> -<h2 id="bug-fixes">Bug Fixes</h2> -<h3 id="updated-google-libphonenumber">Updated google-libphonenumber</h3> -<p><em>Now you can change your number to avoid your ex without missing a single Medic reminder.</em></p> -<p>We&rsquo;ve updated to the latest google-libphonenumber to make sure our app will accept new phone numbers across all of the countries where we work. The current version is 3.1.8. [<a href="https://github.com/medic/medic-webapp/issues/4665">#4665</a>]</p> -<h3 id="fixed-a-performance-issue-with-loading-contacts">Fixed a performance issue with loading contacts</h3> -<p><em>CHWs see a lot of people every day. That&rsquo;s a lot of searching! We&rsquo;ve improved search performance by 30% which really adds up.</em></p> -<p>Loading contacts is now done with an <code>allDocs</code> request instead of using custom views. This improves performance of searching on the people tab and on the reports tab. It&rsquo;s a workaround for now as we look further into the root cause of the slowness. [<a href="https://github.com/medic/medic-webapp/issues/4666">#4666</a>]</p>Core: 2.16.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.16.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.16.1/ -<h2 id="whats-new">What&rsquo;s New</h2> -<h3 id="show-an-icon-next-to-families-or-areas-that-are-overdue-for-a-visit">Show an icon next to families or areas that are overdue for a visit</h3> -<p><em>As the ICONic Britney Spears once sang, &ldquo;Show me a siiiiiign… [visit] me, baby, one more time!&rdquo;</em></p> -<p>In order to help CHWs achieve full coverage of every family or household they care for, we added a feature <a href="https://docs.google.com/document/d/1pPk6FAuLUPKUYnCRgruPk6Lh5IeWzu6IPD1KTFOi6YQ/edit#heading=h.1mauws8bn005">in 2.16</a> to update the list of families or areas to display the date that family or area was last visited. Now, we&rsquo;ve added an icon next to families that were visited more than a month ago to help CHWs quickly spot whom they might visit. Currently, the time frame of &ldquo;more than a month ago&rdquo; is not configurable. [<a href="https://github.com/medic/medic-webapp/issues/4747">#4747</a>]</p> -<p><img src="../images/2.16.1-4747.png" alt="Screenshot"></p>Core: 2.17.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.17.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.17.0/ -<h2 id="whats-new">What&rsquo;s New</h2> -<h3 id="show-pictures-in-the-report-view-history-tab">Show pictures in the report view (History tab)</h3> -<p><em>Your selfies are safe with us (and now visible in the Reports tab).</em></p> -<p>Photos uploaded by CHWs are now visible in the Reports view, or History tab. This is currently being used in the mRDT workflow by supervisors to confirm that CHWs read the mRDT test results correctly. [<a href="https://github.com/medic/medic-webapp/issues/4742">#4742</a>]</p> -<p><img src="../images/2.17.0-4742.png" alt="Screenshot"></p> -<h3 id="improve-styling-of-mrdt-enketo-widget">Improve styling of mRDT Enketo widget</h3> -<p><em>&ldquo;OK, but make it pretty.&rdquo; Fine, here you go: ✨ UI fairy dust ✨</em></p> -<p>We&rsquo;ve improved styling by:</p> -<ul> -<li>Moving the &ldquo;Take Photo&rdquo; button **above ** where the photo is;</li> -<li>Adding a space between the photo and the &ldquo;Take Photo&rdquo; button;</li> -</ul> -<p><img src="../images/2.17.0-4745.png" alt="Screenshot"></p> -<p>[<a href="https://github.com/medic/medic-webapp/issues/4745">#4745</a>]</p> -<h3 id="include-mrdt-in-an-android-release">Include mRDT in an android release</h3> -<p>We made changes in the Android container to make mRDT-related features available. [<a href="https://github.com/medic/medic-webapp/issues/4744">#4744</a>]</p> -<h3 id="include-mrdt-in-a-webapp-release">Include mRDT in a webapp release</h3> -<p>We included mRDT-related features in a webapp release. [<a href="https://github.com/medic/medic-webapp/issues/4743">#4743</a>]</p>Core: 2.18.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.18.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.18.0/ -<h2 id="whats-new">What&rsquo;s New</h2> -<h3 id="make-people-page-default-sort-configurable-uhc-mode">Make People page default sort configurable (UHC mode)</h3> -<p>Previously, the default sort for the People page list was alphabetical. We&rsquo;ve now made the default sort configurable. For UHC mode, this would likely mean sorting by last visited date. In the future, it could include sorting by number of visits this month or another value.</p> -<p>To enable default contact sorting to be based on last visited date, you need to configure it in app_settings. <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/">Please visit the following documentation in cht-docs to learn how</a>. [<a href="https://github.com/medic/medic-webapp/issues/4752">#4752</a>]</p> -<h3 id="add-display-of-visit-counts-and-conditionally-style-them-uhc-mode">Add display of visit counts and conditionally style them (UHC mode)</h3> -<p>We added visit count numbers to the right side of each family row. The visit count will display a count of the number of times that family has been visited so far within the current month. The exact definition of the calendar month is configurable. This is because some partners may follow the Western calendar literally, while other partners have different definitions of a month (ex: Muso counts from the 26th of one month until the 25th of the next month). Whatever forms have been configured to calculate &ldquo;Date last visited&rdquo; will be the same forms used to calculate &ldquo;Visits this month.&rdquo; Please note this will be a straightforward count of forms submitted and cannot be configured to only include a max number of forms in a particular time frame, such as &ldquo;per day.&rdquo;</p> -<p>These visit counts are also available to be conditionally styled. If the partner has no specific goals, just like with Targets, the text is normal black. If the partner does have specific goals, we use red to indicate &ldquo;bad&rdquo;, yellow to indicate &ldquo;ok&rdquo; and green to indicate &ldquo;good&rdquo; or &ldquo;goal met.&rdquo; It is also possible to display an icon next to the count number. This icon is only available as an option if the partner has a goal. If the partner does not have a goal, there will never be an icon. The icon is positioned to the left of the count number on the same baseline.</p> -<p>Note: We removed the red warning icon by the date last visited text that we implemented in 2.16.1. The new functionality and location of the icon as described here replaces what we did in 2.16.1. It used to be tied to date last visited. It is now tied to visits this month.</p> -<p><img src="../images/2.18.0-4758.png" alt="Screenshots"></p> -<p>To enable the specific goal (which color codes the number of visits), you need to configure it in app_settings. <a href="https://docs.communityhealthtoolkit.org/apps/reference/app-settings/#optional-settings">Please visit the following documentation in cht-docs to learn how</a>. [<a href="https://github.com/medic/medic-webapp/issues/4758">#4758</a>]</p> -<h2 id="improvements">Improvements</h2> -<h3 id="lhs-list-doesnt-update-out-of-page-items">LHS list doesn&rsquo;t update &ldquo;out of page&rdquo; items</h3> -<p>LiveList and Search webapp services previously did not support sorting items by fields/values that are likely to change (e.g. by last visited date). Now, when the list is multiple pages long and it&rsquo;s not entirely loaded, if an existent item receives a change that would push it lower than the current number of loaded items, it will refresh and move to appropriate position in the list. [<a href="https://github.com/medic/medic-webapp/issues/4782">#4782).</a>]</p> -<h3 id="there-are-view-generation-errors">There are view generation errors</h3> -<p>[<a href="https://github.com/medic/medic-webapp/issues/4612">#4612</a>]</p>Core: 2.18.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.18.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.18.1/ -<h2 id="whats-new">What&rsquo;s New</h2> -<h3 id="count-two-visits-on-the-same-day-as-one-visit">Count two visits on the same day as one visit</h3> -<p>If a family is visited twice on the same day it now only counts as one visit in UHC mode. [<a href="https://github.com/medic/medic-webapp/issues/4897">#4897</a>]</p> -<h3 id="inputs-group-not-saved-when-its-relevance-is-set-to-false">Inputs group not saved when its relevance is set to false</h3> -<p>Form inputs are now always saved on the reports even when they are marked as not relevant to help with analytics and editing forms. [<a href="https://github.com/medic/medic-webapp/issues/4875">#4875</a>]</p>Core: 2.6.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.6.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.6.0/ -<p>This release contains breaking changes from 0.x versions. Updating from 0.x versions may result in the application no longer operating as expected.</p> -<ul> -<li>The app can now be used offline and synced back to the server later.</li> -<li>Added an android app for accessing the webapp from mobile.</li> -<li>Added Tasks feature for rich event scheduling.</li> -<li>Forms can now be provided in XForm format for rich form UIs.</li> -<li>Added a configurable Target analytics module.</li> -</ul>Core: 2.6.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.6.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.6.1/ -<ul> -<li>User&rsquo;s fullname is not showing up in /configuration/users. Issue: #2200</li> -<li>Deleted documents cause sentinel log spam. Issue: #1999</li> -<li>Disable nools for unrestricted users. Issue: medic-projects#149</li> -<li>Update libphonenumber and use strict validation. Issue: #2159 #2196</li> -<li>Contacts export response garbled. Issue: #2187</li> -</ul>Core: 2.6.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.6.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.6.2/ -<ul> -<li>Update PouchDB to improve replication reliability and performance. Issue: #2134 #2167</li> -<li>When editing a CHP Area, previously set values for CHP, Branch, and Supervisor do not show up. Issue: #2223</li> -<li>Dropdowns in CHP Area create and edit forms have no blank option. Issue: #2227</li> -<li>allow-new appearance in Enketo doesn&rsquo;t make the &ldquo;New&rdquo; option appear. Issue: #2251</li> -<li>Improve performance of Enketo db-object-widget. Issue: #2161</li> -<li>Ensure roles are always available on user-settings. Issue: #2199</li> -<li>Form type filter doesn&rsquo;t include all forms. Issue: #1409</li> -<li>Added APIs for creating Users, People, and Places. Issue: #2046</li> -</ul>Core: 2.6.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.6.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.6.3/ -<ul> -<li>&ldquo;console not defined&rdquo; error when loading page. Issue: #2277</li> -<li>Pouch doesn&rsquo;t update seq unless something has changed. Issue: #2288</li> -<li>Snackbar showing all the time. Issue: #2306</li> -<li>Support external_id property on user-settings docs. Issue: #2310</li> -</ul>Core: 2.7.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.7.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.7.0/ -<h2 id="features">Features</h2> -<ul> -<li>Bulk delete reports. Issue: #1000</li> -</ul> -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li>Report list item summaries aren&rsquo;t translated. Issue: #2100</li> -<li>Fix form type filter. Issue: #1409</li> -</ul> -<h2 id="performance-improvements">Performance improvements</h2> -<ul> -<li>Replication performance. Issue: #2286</li> -<li>Improve search performance. Issue: #2302</li> -<li>Don&rsquo;t fetch form titles for each Contact report. Issue: #2300</li> -<li>Only fetch relevant data for the Users service. Issue: #2262</li> -<li>Remove clinics from the Facility filter dropdown. Issue: #2218</li> -<li>Optimize admin bandwidth concerns. Issue: #2211</li> -<li>We request facilities from the server over and over again. Issue: #2210</li> -<li>Don&rsquo;t audit _local docs. Issue: #2366</li> -<li>All requests to CouchDB time out after 10 seconds. Issue: #2325</li> -<li>Long delay loading contact dropdowns. Issue: #2326</li> -</ul>Core: 2.7.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.7.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.7.1/ -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li>Creating user via fails due to invalid reported_date. Issue: #2449</li> -</ul> -<h2 id="performance-improvements">Performance improvements</h2> -<ul> -<li>App takes minutes to load a person dropdown. Issue: #2445</li> -<li>Cannot load Configuration Users page. Issue: #2444</li> -</ul>Core: 2.7.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.7.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.7.2/ -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li>Connection refused when trying to load app. Issue: #2476</li> -</ul>Core: 2.7.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.7.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.7.3/ -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li>Remove maxSockets limit to allow more concurrent connections. Issue: #2492</li> -</ul>Core: 2.8.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.8.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.8.0/ -<h2 id="features">Features</h2> -<ul> -<li>Pass user&rsquo;s info to rule to customize Tasks per user type or location. Issue: #2408</li> -<li>Add context to target types and goals. Issue: #2409</li> -<li>Update default translations</li> -<li>Add ageInDays and ageInMonths functions to the XML forms context utilities. Issue: #2650</li> -<li>Users can now only access an optionally configured number of hierarchy levels below their facility. Issue: #2648</li> -</ul> -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li>Android back button doesn&rsquo;t work as expected. Issue: #2600</li> -<li>In date filter for Reports tab, the selected dates are being offset by 1 day. Issue: #2185</li> -<li>&lsquo;New Contact&rsquo; option does not appear without a search. Issue: #2516</li> -<li>Place contact should be a child of the place. Issue: #1710</li> -<li>Geolocation information is not included in submitted form. Issue: #2450</li> -<li>Cannot update a contact&rsquo;s phone number without an error. Issue: #2420</li> -</ul>Core: 2.8.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.8.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.8.1/ -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li>If initial sync fails without syncing anything subsequent syncs get no results. Issue: #2770</li> -<li>Initial sync fails if server doesn&rsquo;t respond within 30 seconds. Issue: #2771</li> -<li>Targets tab is blank on first access. Issue: #2739</li> -</ul> -<h2 id="performance-improvements">Performance improvements</h2> -<ul> -<li>Adding a space to a contact search term performs poorly. Issue: #2769</li> -<li>Local DB grows without limit. Issue: #2434</li> -</ul>Core: 2.8.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.8.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.8.2/ -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li>Ensure PouchDB doesn&rsquo;t mis-label TECNO phones as devices running Safari. Issue: #2797</li> -</ul>Core: 2.8.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.8.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.8.3/ -<h2 id="performance-improvements">Performance improvements</h2> -<ul> -<li>Remove traffic statistics collection. Issue: #2886</li> -</ul>Core: 2.8.4 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.8.4/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.8.4/ -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li>Debounce form submissions to stop duplicate submissions. Issue: #2909</li> -</ul>Core: 2.8.5 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.8.5/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.8.5/ -<ul> -<li>No changes, only a bump in version number to trigger a new release.</li> -</ul>Core: 2.9.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.9.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.9.0/ -<h2 id="features">Features</h2> -<ul> -<li>Redesign of People tab to introduce patient centric workflows.</li> -<li>Create Task and Target based on reports using short <code>patient_id</code> format. Issue: #2986</li> -<li>Calculate Z-Score within app workflow form. Issue: #2915</li> -<li>Transitions do not run for XForms. Issue: #2864</li> -<li>CHWs should not be able to edit their own area. Issue: #2844</li> -<li>Allow for people-centric SMS workflows. Issue: #2700</li> -<li>Unique &ldquo;add person&rdquo; forms to a place. Issue: #2693</li> -<li>Store GPS failure. Issue: #2670</li> -<li>Progressive Web App. Issue: #2626</li> -<li>Remove XML parsing and replace it with JSON views. Issue: #2432</li> -<li>Lowercase all user ids. Issue: #2369</li> -<li>New person and new place buttons should add person/place to the part of the hierarchy in which they are clicked. Issue: #2335</li> -<li>Create default Edit Place forms that allow users to edit a family&rsquo;s primary contact. Issue: #2333</li> -<li>Initial replication feedback. Issue: #2279</li> -<li>Make it easy to add translation keys. Issue: #1333</li> -</ul> -<h2 id="uiux-improvements">UI/UX improvements</h2> -<ul> -<li>Show parent place after deleting a place/person. Issue: #2936</li> -<li>Clean up labels and translations. Issue: #2888</li> -<li>First load: briefly displays &ldquo;No people found&rdquo; on the people and places tab even if you have contacts. Issue: #2835</li> -<li>Add icons to forms. Issue: #2794</li> -<li>Forms in Submit Report menu aren&rsquo;t sorted. Issue: #2760</li> -<li>Reported Date is show in ms since epoch. Issue: #2699</li> -<li>Add basic sync status to about page. Issue: #2415</li> -<li>Display &lsquo;your place&rsquo; card upon login. Issue: #2342</li> -<li>Only show places you directly manage in LHS unless searching. Issue: #2339</li> -<li>Lock &lsquo;Your Place&rsquo; at top of left pane. Issue: #2337</li> -<li>Remove all filters in Contacts. Issue: #2336</li> -<li>Display &ldquo;disabled for admins&rdquo; message in tasks and targets page. Issue: #2292</li> -</ul> -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li>Rerun transitions on change if the previous run failed. Issue: #2978</li> -<li>Allow replication of JSON reports. Issue: #2979</li> -<li>On upgrade existing reports are not associated to person/place. Issue: #2970</li> -<li>Queries from ANC Analytics do not work. Issue: #2975</li> -<li>Set new permissions to the application default when updating. Issue: #2951</li> -<li><code>db-object</code> fields show as editable when <code>readonly=&quot;true()&quot;</code>. Issue: #2910</li> -<li>Calling the <code>doc_summaries_by_id</code> view results in an audit record being created. Issue: #2895</li> -<li>Can&rsquo;t create new person as primary contact to existing place. Issue: #2884</li> -<li>Mute button does not work. Issue: #2878</li> -<li>Cannot fully replicate dbs behind medic-api due to badly named document. Issue: #2876</li> -<li>Deregister Changes callbacks. Issue: #2870</li> -<li>Non admins can edit translations. Issue: #2868</li> -<li>Error when completing a task. Issue: #2851</li> -<li>Editing a report from a person doesn&rsquo;t pre-populate the person. Issue: #2845</li> -<li>Forms appear on the History page when they shouldn&rsquo;t, based on configuration. Issue: #2837</li> -<li>When I click on a report from a contact profile, I see a flash of the History tab before getting to the report view. Issue: #2834</li> -<li>When resources change all icons disappear. Issue: #2830</li> -<li>Use default revs_limit. Issue: #2787</li> -<li>ContactsContent controller modifies doc on render. Issue: #2782</li> -<li>Alerts to <code>reporting_unit</code> not working. Issue: #2779</li> -<li>Correct form not displayed when going to Submit Report. Issue: #2758</li> -<li>Validations and Auto-Replies not triggered for Notifications. Issue: #2755</li> -<li>Sending message from Reports tab doesn&rsquo;t prefill the modal. Issue: #2748</li> -<li>Selecting a person&rsquo;s Report or Task doesn&rsquo;t load the actual item. Issue: #2718</li> -<li>select2 form fields are not being prepopulated. Issue: #2703</li> -<li>Exporting Feedback crashes API. Issue: #2692</li> -<li>Cannot associate a user with a place. Issue: #2683</li> -<li>Missing patient ids and CHW names. Sentinel not fully running on Strong Minds instance. Issue: #2675</li> -<li>Cannot update a contact&rsquo;s phone number without an error. Issue: #2661</li> -<li>Add recipients doesn&rsquo;t work. Issue: #2659</li> -<li>Sometimes changes feed is told the wrong ID for user doc. Issue: #2640</li> -<li>Submitting a report with an invalid ref id crashes sentinel. Issue: #2636</li> -<li>Always use the same pouchdb configuration. Issue: #2625</li> -<li>Branch Manager is not getting forms downloaded. Issue: #2620</li> -<li>Can crash API with call to /api/v1/users. Issue: #2602</li> -<li>TypeError on Messages tab. Issue: #2588</li> -<li><code>context_by_type_freetext</code> view seems to run with no search term. Issue: #2584</li> -<li>Once viewing a stock report, clicking the area name makes area stats disappear. Issue: #2580</li> -<li>Missing option to change time unit in reporting rate analytics. Issue: #2576</li> -<li>Missing &ldquo;district&rdquo; selector in Reporting rates Analytics in v2.x. Issue: #2575</li> -<li>Rename &ldquo;stock&rdquo; widget to &ldquo;reporting rates&rdquo;. Issue: #2574</li> -<li>No title shown for analytics stock table. Issue: #2573</li> -<li>No loader showing when loading data on analytics stock report. Issue: #2572</li> -<li>No loader shown when loading locations on Analytics Stock widget. Issue: #2566</li> -<li>Back button is broken on analytics stock widget. Issue: #2565</li> -<li>Stock widget shows time-period selector before a place has been selected. Issue: #2564</li> -<li>Icons wrong on analytics stock widget. Issue: #2563</li> -<li>No text labels displayed in analytics stock widget. Issue: #2562</li> -<li>Analytics stock widget breaks on error. Issue: #2561</li> -<li>No loading animation is displayed when waiting for list of locations on analytics stock widget. Issue: #2559</li> -<li>Analytics page should not show menu if there is only one module. Issue: #2557</li> -<li>Cannot choose one of the analytics modules. Issue: #2556</li> -<li>Reporting Rates has changed name to Stock Monitoring. Issue: #2555</li> -<li>Analytics screen has no information on it. Issue: #2554</li> -<li>By default gateway users don&rsquo;t have the <code>can_access_gateway_api</code> permission. Issue: #2549</li> -<li>Cannot view Targets configuration. Issue: #2548</li> -<li>Send message uses a badly performing API. Issue: #2547</li> -<li>Document conflict on starting sentinel. Issue: #2542</li> -<li>Bad error when not authed for SMS api. Issue: #2540</li> -<li>Editing your own user settings wipes out security settings. Issue: #2539</li> -<li>Cannot send messages to unknown numbers. Issue: #2536</li> -<li>medic-api doesn&rsquo;t seem to correctly expose all pending messages. Issue: #2535</li> -<li>Outgoing messages are not well formatted in the report view. Issue: #2532</li> -<li>SMS from medic-gateway do not appear in Messages tab. Issue: #2530</li> -<li>Phantom SMS message response to invalid textform message. Issue: #2525</li> -<li>sentinel keeps processing the same backlog. Issue: #2521</li> -<li>Errors starting sentinel with DB name other than &lsquo;medic&rsquo;. Issue: #2513</li> -<li>You need to restart medic-api for new translations to make it through. Issue: #2511</li> -<li>API connection refused. Issue: #2476</li> -<li>Login page not translated. Issue: #2466</li> -<li>Language dropdown is empty when adding or editing a user. Issue: #2462</li> -<li>Language select modal is blank. Issue: #2459</li> -<li>Broken migrations should prevent API from starting. Issue: #2456</li> -<li>Geolocation information is not exposed in JSON. Issue: #2450</li> -<li>Creating user fails due to invalid <code>reported_date</code>. Issue: #2449</li> -<li>App takes minutes to load a <code>person</code> dropdown. Issue: #2445</li> -<li>User can&rsquo;t change their own password. Issue: #2440</li> -<li>When switching between reports in History, the &ldquo;No report selected&rdquo; page appears momentarily. Issue: #2433</li> -<li>\u0000 cannot be converted to text. Issue: #2426</li> -<li>The tour keeps popping up on mobile. Issue: #2423</li> -<li>In select mode, clicking on a report on the LHS to check the box also marks the report as read. Issue: #2422</li> -<li>DeleteDocs modifies the given array. Issue: #2417</li> -<li>DeleteDocs fails if the parent&rsquo;s contact is null. Issue: #2416</li> -<li>After clicking &ldquo;delete&rdquo; on the RHS of bulk delete, reports do not disappear from the LHS. Issue: #2414</li> -<li>Using &ldquo;select all&rdquo; when attempting to bulk delete, the number of records on the RHS doesn&rsquo;t match the LHS. Issue: #2411</li> -<li>Logout option no longer works on the MM Android app. Issue: #2407</li> -<li>national-admins should be able to edit contacts. Issue: #2395</li> -<li>Initial replication gets stuck on Tecno. Issue: #2394</li> -<li>Cannot have more than one repeat group that creates people within the same form. Issue: #2393</li> -<li>Targets tab doesn&rsquo;t show correct progress. Issue: #2388</li> -<li>Contacts tab takes forever to load on mobile. Issue: #2378</li> -<li>RangeError: Maximum call stack size exceeded. Issue: #2377</li> -<li>Don&rsquo;t audit docs that match <code>_local/*</code>. Issue: #2366</li> -<li>Fix changes proxy to support heartbeat. Issue: #2363</li> -<li>.4 analytics page crashes <code>develop</code> API. Issue: #2352</li> -<li>Admin can&rsquo;t submit report (permissions). Issue: #2351</li> -<li>DeleteDoc service breaks replication. Issue: #2331</li> -<li>Phones with poor internet connections get an error page when trying to update. Issue: #2328</li> -<li>Navigating straight to <code>medic/_design/medic/_rewrite/#/configuration/user</code> breaks editing. Issue: #2294</li> -<li>Cannot select contact after bad search. Issue: #2252</li> -<li>Incoming message not attributed to contact. Issue: #2230</li> -<li>Form recognized, but label in list not updated. Issue: #2215</li> -<li>Forms not showing in filter. Issue: #2214</li> -<li>Schedule not assigned to registration form. Issue: #2213</li> -<li>In date filter for Reports tab, the selected dates are being offset by 1 day. Issue: #2185</li> -<li>Exceptions when indexing (presumably) views. Issue: #2173</li> -<li>Form title disappears on page reload. Issue: #2156</li> -<li>User configuration UI doesn&rsquo;t correctly load the attached contact / locale. Issue: #2116</li> -<li>Display: block in or-appearance-h2 is overriding the disabled class. Issue: #2101</li> -<li>Verify/Unverify button falls out of sync with left pane after being clicked. Issue: #1939</li> -<li>Place contact should be a child of the place. Issue: #1710</li> -<li>Default &ldquo;New Person&rdquo; form doesn&rsquo;t allow editing the parent place. Issue: #2704</li> -</ul> -<h2 id="performance-improvements">Performance improvements</h2> -<ul> -<li>Make medic-audit&rsquo;s view generation 8-∞ times faster. Issue: #2879</li> -<li>Deregister Changes callbacks. Issue: #2870</li> -<li>Improve free text search views. Issue: #2853</li> -<li>Admin performance on lg.app has regressed. Issue: #2744</li> -<li>Improve application performance for high-utilization CHPs. Issue: #2665</li> -<li>Create migration to remove obsolete ddocs. Issue: #2597</li> -<li>Send message uses a badly performing API. Issue: #2547</li> -<li>Consider refactoring how sentinel views are compiled. Issue: #2537</li> -<li>sentinel duplicate views. Issue: #2534</li> -<li>Remove XML parsing and replace it with JSON views. Issue: #2432</li> -<li>Increase stability by looping over changes . Issue: #2430</li> -<li>Replication since gets reset when new documents added. Issue: #2404</li> -<li>Use db view pagination where possible. Issue: #2371</li> -<li>Don&rsquo;t audit docs that match <code>_local/*</code>. Issue: #2366</li> -<li>Create a client side ddoc. Issue: #2206</li> -<li>Store translations in a separate doc. Issue: #1706</li> -<li>Remove empty parents migration scalability. Issue: #2629</li> -</ul>Core: 2.9.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.9.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.9.1/ -<h2 id="bug-fixes">Bug fixes</h2> -<ul> -<li>Added a migration to fix scheduled messages so they can be sent by medic-gateway. Issue: #3015</li> -</ul>Core: 3.0.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.0.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.0.0/ -<h2 id="upgrade-notes">Upgrade notes</h2> -<ol> -<li>The <a href="https://docs.communityhealthtoolkit.org/core/releases/#dependencies">supported versions for client and server software</a> -have been changed significantly. Make sure your software meets the requirements before upgrading to 3.0.0.</li> -<li>The <code>/api/v1/messages</code> endpoint has been removed as it was no longer actively used, and contained bugs. [<a href="https://github.com/medic/medic-webapp/issues/3971">#3971</a>]</li> -<li>The ANC analytics page and the following APIs have been removed as they are no longer used. [<a href="https://github.com/medic/medic-webapp/issues/1002">#1002</a>] -<ul> -<li><code>/api/active-pregnancies</code></li> -<li><code>/api/upcoming-appointments</code></li> -<li><code>/api/missed-appointments</code></li> -<li><code>/api/upcoming-due-dates</code></li> -<li><code>/api/high-risk</code></li> -<li><code>/api/total-births</code></li> -<li><code>/api/missing-delivery-reports</code></li> -<li><code>/api/delivery-location</code></li> -<li><code>/api/visits-completed</code></li> -<li><code>/api/visits-during</code></li> -<li><code>/api/monthly-registrations</code></li> -<li><code>/api/monthly-deliveries</code></li> -</ul> -</li> -<li>The <code>/api/v1/export/messages</code>, <code>/api/v1/export/forms</code>, and <code>/api/v1/export/contacts</code> endpoints have been removed in favor of <code>/api/v2/export/messages</code>, <code>/api/v2/export/reports</code>, and <code>/api/v2/export/contacts</code> respectively. [<a href="https://github.com/medic/medic-webapp/issues/1002">#1002</a>]</li> -<li>The <code>/api/v1/fti</code> endpoint has been removed due to security concerns and lack of use. [<a href="https://github.com/medic/medic-webapp/issues/1002">#1002</a>]</li> -</ol> -<h2 id="whats-new">What&rsquo;s New</h2> -<h3 id="database-upgraded-to-the-latest-couchdb">Database upgraded to the latest CouchDB</h3> -<p>Our stack now runs on the latest and greatest version of CouchDB (v2.2) which is a major upgrade and supports clustering for better performance on large projects and more efficient replication.</p> -<h3 id="support-running-our-stack-on-docker">Support running our stack on Docker</h3> -<p>We have implemented a containerization solution (Docker) which means our stack can run safely on a range of operating systems, and multiple deployments can run on the same instance. The end result is to better support a project self-hosting the deployment and to save money on deployment by combining some or all of our AWS instances. [<a href="https://github.com/medic/medic-webapp/issues/3983">#3983</a>]</p> -<h3 id="import-and-export-of-settings-in-admin-console">Import and export of Settings in Admin Console</h3> -<p>This feature provides UI support to import and export settings. There is now third tab to the Settings page called &ldquo;Backup/Restore&rdquo; and includes instructions for downloading as well as uploading. [<a href="https://github.com/medic/medic-webapp/issues/3868">#3868</a>]</p> -<h3 id="uses-horticulturalist-for-installation-and-upgrades">Uses Horticulturalist for installation and upgrades</h3> -<p><strong>Previous Situation</strong>: To install and update the Medic webapp on a server we used an application called <em>Gardener</em>. It is a web-based tool to manage CouchDB applications. It includes a tool, called Dashboard, which lets you install Medic. You could then upgrade the Medic application using that same web-based tool. <br> -<br> -<strong>Problem:</strong> Although Dashboard made it easy to install and upgrade an application, it was problematic when managing many deployments. For instance, upgrading all instances required manually clicking the Upgrade button in a webpage. This process is tedious, and only worked if you were on a good internet. We learned the hard way that doing an upgrade from a spotty connection could leave your instance in a broken state. Also, you only had the choice to upgrade to the very latest version of the app. That means that if you are on 2.14.0, you could not update just to 2.14.5, the same version but with fixes for bugs. You&rsquo;d have to upgrade all the way to 2.18.0. <br> -<br> -<strong>Solution</strong>: Horticulturalist is a new and easy way to deploy and update Medic. Horti replaces the Market, Gardener and Dashboard as our standard way to deploy and manage our software. It can be used from the Medic Mobile admin webapp to select the specific version that you want to upgrade to. You can even select versions from a different branch to help with acceptance testing of new features. It can also be used via a computer terminal using the command line interface. This makes it easy for the Site Reliability Engineering team to manage instances, making sure that projects quickly get updates to use the most stable version of Medic tools.</p> -<p>[<a href="https://github.com/medic/medic-webapp/issues/3993">#3993</a> ….]</p> -<h2 id="improvements">Improvements</h2> -<h3 id="value-for-db-doc-attribute-is-case-sensitive">Value for db-doc attribute is case sensitive</h3> -<p>Previously the db-doc attribute in XLSforms only accepted the lower-case &ldquo;true.&rdquo; It now accepts &ldquo;TRUE&rdquo; as well, which is what we got often got with Excel doing autocorrect. [<a href="https://github.com/medic/medic-webapp/issues/3973">#3973</a>]</p> -<h3 id="targets-tab-has-no-loading-spinner-on-initial-load">Targets tab has no Loading spinner on initial load</h3> -<p>There is now a loading spinner on the Targets tab on initial load. [<a href="https://github.com/medic/medic-webapp/issues/4241">#4241</a>]</p> -<h3 id="person-with-self-as-parent">Person with self as parent</h3> -<p>It used to be possible to edit URLs to create a person with themselves as a parent. This has now been fixed. [<a href="https://github.com/medic/medic-webapp/issues/4487">#4487</a>]</p> -<h3 id="replace-medic-reporter">Replace medic-reporter</h3> -<p>Medic-reporter was a standalone couchapp that we used for sending test messages without needing an SMS or gateway device. Because it was standalone, it frequently broke and was difficult to install. <br> -<br> -We have now reproduced the main functionality of medic-reporter in the admin app, so it&rsquo;s shipped with the webapp, can be tested and maintained easily, and works. [<a href="https://github.com/medic/medic-webapp/issues/4516">#4516</a>]</p> -<h3 id="enketo-summary-label-icons-are-misaligned">Enketo summary label icons are misaligned</h3> -<p>Previously, icons on the summary screen were misaligned into a corner. We&rsquo;ve added padding to center them vertically and horizontally on the summary bar. [<a href="https://github.com/medic/medic-webapp/issues/4530">#4530</a>]</p> -<h3 id="make-sure-we-cant-infinitely-recurse-in-the-lineage-shared-library">Make sure we can&rsquo;t infinitely recurse in the lineage shared library</h3> -<p>We put guards in place to cleanly throw errors if we detect that we&rsquo;re hitting an infinite loop. We did this by putting depth guards on potentially problematic loops and throw the error if the number gets outrageously high. This will prevent app hangs and crashes. [<a href="https://github.com/medic/medic-webapp/issues/4604">#4604</a>]</p> -<h3 id="implement-access-logging-in-api">Implement access logging in API</h3> -<p>We implemented logging of requests in API for the response status, size, time, etc. [<a href="https://github.com/medic/medic-webapp/issues/4622">#4622</a>]</p> -<h2 id="performance-fixes">Performance Fixes</h2> -<h3 id="pull-sentinel-data-out-into-its-own-database">Pull sentinel data out into its own database</h3> -<p>We have taken data that is specific to the running of sentinel and doesn&rsquo;t need to be replicated down to users out into its own database. This makes our changes feed half the size (or so). [<a href="https://github.com/medic/medic-webapp/issues/3423">#3423</a>]</p> -<h3 id="split-the-admin-tab-out-as-a-new-app">Split the admin tab out as a new app</h3> -<p>Previously, non-admin users have to download and run the admin only code, which was a waste of bandwidth, memory, and disk space. We split it out as a separate webapp. The new app is a desktop-only, online-only single page. The new admin app preserves all the same functionality as before, and will be revisited with a UX/UI update in the near future. [<a href="https://github.com/medic/medic-webapp/issues/4145">#4145</a>]</p> -<h3 id="changes-requests-are-unsustainably-large">Changes requests are unsustainably large</h3> -<p>Previously, requests to changes feed got very big because we submitted all known doc IDs. Now, whenever a delete comes through in the changes feed, we manually run the view code over it to determine who should see the delete which means docs ids are not included as parameters on the request. [<a href="https://github.com/medic/medic-webapp/issues/4172">#4172</a>]</p> -<h3 id="improve-the-filtered-replication-algorithm">Improve the filtered replication algorithm</h3> -<p>We made significant performance improvements to our filtered replication algorithm. [<a href="https://github.com/medic/medic-webapp/issues/4185">#4185</a>]</p> -<h3 id="write-a-scalability-testing-framework">Write a scalability testing framework</h3> -<p>We wrote a scalability testing framework to determine how many users our app can support. This will help us to work out which aspects to focus on, prove improvements work as expected, and test for regressions. [<a href="https://github.com/medic/medic-webapp/issues/4244">#4244</a>]</p> -<h3 id="subject-summaries-are-loaded-one-at-a-time">Subject summaries are loaded one at a time</h3> -<p>Reduce the time it takes to load the Reports and Contacts lists by up to 50% by requesting subject summaries in a batch rather than individually. [<a href="https://github.com/medic/medic-webapp/issues/4669">#4669</a>]</p> -<h2 id="and-lots-more">And lots more…</h2> -<p>Over 100 individual issues have been fixed in this release - <a href="https://github.com/medic/cht-core/blob/master/Changes.md#300">read more</a>.</p> \ No newline at end of file +Core Framework Releases on Community Health Toolkithttps://docs.communityhealthtoolkit.org/core/releases/Recent content in Core Framework Releases on Community Health ToolkitHugo -- gohugo.ioen0.x release noteshttps://docs.communityhealthtoolkit.org/core/releases/0.4.15-and-earlier/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/0.4.15-and-earlier/0.4.15 March 2, 2017 +Fixed potential race condition with medic-gateway. Issue: medic-projects/issues/1243 Bumped libphonenumber to make phone number validation more up to date. Issue: medic-projects/issues/1005 0.4.14 December 16, 2016 +Bug fix for medic-gateway sending scheduled messages. Issue: #2535 0.4.13 October 21, 2016 +Option to set birthdate using days old instead of weeks. Issue: #2756 The week/month is off by 2 in the Reporting Rates analytics dashboard. Issue: #2781 Remove socket limit in medic-api.2.10.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.10.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.10.0/Features Use reference to translation keys in app_settings. Issue: #3127 Add date of birth to person created by SMS. Issue: #3100 Configure the max number of SMS in multipart SMS. Issue: #3095 Load messages script fails to use https. Issue: #3081 Cannot access all fields for contact in select2. Issue: #3069 Configurable contact summary cards. Issue: #3037 Display additional information in contact profile. Issue: #2914 Support additional context for hiding/showing actions.2.10.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.10.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.10.1/Bug fixes Sending a message from the Messages tab creates a message with uuid equal to database URL. Issue: #32422.10.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.10.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.10.2/Bug fixes Sentinel somehow infinitely loops and continually writes to its metadata file. Issue: #3275 API crashes after /medic/_bulk_docs gets called. Issue: #32682.10.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.10.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.10.3/Bug fixes Unicode support for storing enketo xml. Issue: #3308 Support negative values in xform fields better. Issue: medic/medic-projects#1624 Trigger enketo calc updates when option names are changed. Issue: #3281 New Household button missing. Issue: #3132 Change report form language without requiring refresh. Issue: #3174 Corrupted translation strings. Issue: #3305 UI/UX improvements Show report subject name on patient page. Issue: #3309 Translate task schedule group titles. Issue: #3283 Add additional supported moment locales.2.11.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.11.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.11.0/Migration notes #3230 changes patient ID generation so it automatically increases the length as needed, up to 13 digits. If you are validating incoming patient_ids in Sentinel, be sure to remove or correct any length restrictions, e.g. ^[0-9]{5}$ would become ^[0-9]{5,13}$. #3166 adds a new transition that adds patient_ids to every created person: generate_patient_id_on_people. Enable this transition if you want to send SMS about patients that may be created through the webapp.2.11.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.11.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.11.1/Bug fixes Cannot report via SMS about people who are registered in the web app. Issue: #3401 Results page CSS messed up in v2.11. Issue: #3369 The user needs an associated contact to create a contact. Issue: #3394 Error when adding Place with new person. Issue: #3420 Error after canceling and re-opening any contact creation form. Issue: #3448 namespace-form-fields migration : report bulk errors. Issue: #3371 (second part)2.11.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.11.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.11.2/Performance improvements Slow initial replication for users with lots of docs. Issue: #35082.11.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.11.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.11.3/Bug fixes The namespace-form-fields migration conflicts itself. Issue: #3534 In the create-patient-contacts migration provide a more complete list of potential patient_name locations. Issue: #33722.12.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.12.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.12.0/Features Add sync status indicator for offline users. Issue: #3357 Add gateway message delivery statuses. Issue: #3073 Add a replication_date property to records. Issue: #2180 Change patient id generation to store the length of id it&rsquo;s generating. Issues: #3505 Allow form upload through Form Configuration UI. Issue: #3433 Bug fixes On small screen, cannot re-open date filter in history tab. Issue: #3467 Debug section of the About screen has some weird extra text.2.12.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.12.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.12.1/Bug fixes Improved error messages for SMS endpoint. Issue: #3587 Allow for empty SMS message Content. Issue: #3656 Implement 500 item max for bulk delete. Issue: #3605 Security Fixed kanso packages that inadvertently cached credentials. Issue: #36482.12.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.12.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.12.2/Bug fixes Accept patient reports for patients created in app. Issue: #3740 Stop accept_patient_reports transition clearing messages for unrelated registrations. Issue: #37422.12.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.12.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.12.3/Bug fixes Accept messages with empty from or content. See: https://github.com/medic/medic-api/pull/1852.12.4 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.12.4/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.12.4/Bug fixes Fix issue with /api/v1/records. Issue: #37702.12.5 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.12.5/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.12.5/Bug fixes Fix bug where id generation wouldn&rsquo;t automatically increase id length when it ran out of ids. Issue: #37902.13.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.13.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.13.0/Migration notes #2635 changes the context available to the configured contact summary script. The contact parameter no longer has information about parents. This information is now in an array called lineage. More information is available in the configuration documentation. #3546 changes the implementation of the contact_summary so instead of declaring the output on the last line of the script, now you have to return the output. Usually this is as easy as adding a return on the last line, so output; becomes return output;.2.13.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.13.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.13.1/Bug fixes Fix bug in extract-person-contacts migration introduced in 2.13.0 #4031 Remove the now invalid erlang migrations #40332.13.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.13.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.13.2/Bug fixes Force outputs to recalc on form load #41112.13.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.13.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.13.3/Improvements Bump libphonenumber to the latest2.13.4 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.13.4/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.13.4/Improvements Bump libphonenumber for Nepal Smart Telecom.2.13.5 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.13.5/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.13.5/Bug fixes Update Notification transition crashes sentinel if the patient id is misconfigured. #41212.13.6 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.13.6/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.13.6/Performance improvements Drastically improve performance of form loading when the patient context is used, and that context is very large (e.g. you&rsquo;re including lineages). #44302.13.7 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.13.7/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.13.7/Performance improvements Various PouchDB performance improvements were backported from 2.14. This includes increasing the PouchDB version and removing our use of pouchdb-worker.2.14.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.14.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.14.0/Additional release notes are available here. +Migration notes #3449: We included a feature which makes it unnecessary to use a repeat-relevant node in Enketo forms to workaround a bug which created an empty child. This node should now be removed. #3629: We added more configurable text to the target widgets. Also, configuring an array of target titles is now deprecated in favor specifying a single translation key. Reconfigure your targets to specify values for translation_key, subtitle_translation_key, and percentage_count_translation_key properties.2.14.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.14.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.14.1/Performance improvements #4430: Drastically improve performance of form loading when the patient context is used, and that context is very large (e.g. you&rsquo;re including lineages).2.14.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.14.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.14.2/Bug fixes #3099: Uncaught exception triggers 500 response for subsequent requests.2.14.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.14.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.14.3/Bug fixes #4457: The z-score enketo widget is not usable. #4460: Uncaught Exception: write after end error.2.15.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.15.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.15.0/What&rsquo;s New View &lsquo;clinic&rsquo; Places in Places Filter You might have noticed that for SMS projects, CHW areas went missing from the places filter in the Reports tab. Good news! They are back. +When we started having CHWs log into the Medic app and register families, the places filter on the reports page became crowded with thousands of families, creating a performance issue. To get around that issue, we removed 'clinic' level places from this filter drop-down so that users would only see health centers and CHW areas, no families.2.16.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.16.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.16.0/What&rsquo;s New View date last visited for places on the people tab Because knowing where you&rsquo;ve been helps you know where you&rsquo;re going next! - Medic proverb +In order to help CHWs achieve full coverage of every family or household they care for, we&rsquo;ve added an optional feature to update the list of families or areas to display the date that family or area was last visited. You can use any patient- or family-level form or forms to update the date last visited.2.16.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.16.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.16.1/What&rsquo;s New Show an icon next to families or areas that are overdue for a visit As the ICONic Britney Spears once sang, &ldquo;Show me a siiiiiign… [visit] me, baby, one more time!&rdquo; +In order to help CHWs achieve full coverage of every family or household they care for, we added a feature in 2.16 to update the list of families or areas to display the date that family or area was last visited.2.17.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.17.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.17.0/What&rsquo;s New Show pictures in the report view (History tab) Your selfies are safe with us (and now visible in the Reports tab). +Photos uploaded by CHWs are now visible in the Reports view, or History tab. This is currently being used in the mRDT workflow by supervisors to confirm that CHWs read the mRDT test results correctly. [#4742] +Improve styling of mRDT Enketo widget &ldquo;OK, but make it pretty.&rdquo; Fine, here you go: ✨ UI fairy dust ✨2.18.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.18.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.18.0/What&rsquo;s New Make People page default sort configurable (UHC mode) Previously, the default sort for the People page list was alphabetical. We&rsquo;ve now made the default sort configurable. For UHC mode, this would likely mean sorting by last visited date. In the future, it could include sorting by number of visits this month or another value. +To enable default contact sorting to be based on last visited date, you need to configure it in app_settings.2.18.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.18.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.18.1/What&rsquo;s New Count two visits on the same day as one visit If a family is visited twice on the same day it now only counts as one visit in UHC mode. [#4897] +Inputs group not saved when its relevance is set to false Form inputs are now always saved on the reports even when they are marked as not relevant to help with analytics and editing forms. [#4875]2.6.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.6.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.6.0/This release contains breaking changes from 0.x versions. Updating from 0.x versions may result in the application no longer operating as expected. +The app can now be used offline and synced back to the server later. Added an android app for accessing the webapp from mobile. Added Tasks feature for rich event scheduling. Forms can now be provided in XForm format for rich form UIs. Added a configurable Target analytics module.2.6.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.6.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.6.1/ User&rsquo;s fullname is not showing up in /configuration/users. Issue: #2200 Deleted documents cause sentinel log spam. Issue: #1999 Disable nools for unrestricted users. Issue: medic-projects#149 Update libphonenumber and use strict validation. Issue: #2159 #2196 Contacts export response garbled. Issue: #21872.6.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.6.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.6.2/Update PouchDB to improve replication reliability and performance. Issue: #2134 #2167 When editing a CHP Area, previously set values for CHP, Branch, and Supervisor do not show up. Issue: #2223 Dropdowns in CHP Area create and edit forms have no blank option. Issue: #2227 allow-new appearance in Enketo doesn&rsquo;t make the &ldquo;New&rdquo; option appear. Issue: #2251 Improve performance of Enketo db-object-widget. Issue: #2161 Ensure roles are always available on user-settings.2.6.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.6.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.6.3/ &ldquo;console not defined&rdquo; error when loading page. Issue: #2277 Pouch doesn&rsquo;t update seq unless something has changed. Issue: #2288 Snackbar showing all the time. Issue: #2306 Support external_id property on user-settings docs. Issue: #23102.7.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.7.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.7.0/Features Bulk delete reports. Issue: #1000 Bug fixes Report list item summaries aren&rsquo;t translated. Issue: #2100 Fix form type filter. Issue: #1409 Performance improvements Replication performance. Issue: #2286 Improve search performance. Issue: #2302 Don&rsquo;t fetch form titles for each Contact report. Issue: #2300 Only fetch relevant data for the Users service. Issue: #2262 Remove clinics from the Facility filter dropdown. Issue: #2218 Optimize admin bandwidth concerns. Issue: #2211 We request facilities from the server over and over again.2.7.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.7.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.7.1/Bug fixes Creating user via fails due to invalid reported_date. Issue: #2449 Performance improvements App takes minutes to load a person dropdown. Issue: #2445 Cannot load Configuration Users page. Issue: #24442.7.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.7.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.7.2/Bug fixes Connection refused when trying to load app. Issue: #24762.7.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.7.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.7.3/Bug fixes Remove maxSockets limit to allow more concurrent connections. Issue: #24922.8.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.8.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.8.0/Features Pass user&rsquo;s info to rule to customize Tasks per user type or location. Issue: #2408 Add context to target types and goals. Issue: #2409 Update default translations Add ageInDays and ageInMonths functions to the XML forms context utilities. Issue: #2650 Users can now only access an optionally configured number of hierarchy levels below their facility. Issue: #2648 Bug fixes Android back button doesn&rsquo;t work as expected. Issue: #2600 In date filter for Reports tab, the selected dates are being offset by 1 day.2.8.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.8.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.8.1/Bug fixes If initial sync fails without syncing anything subsequent syncs get no results. Issue: #2770 Initial sync fails if server doesn&rsquo;t respond within 30 seconds. Issue: #2771 Targets tab is blank on first access. Issue: #2739 Performance improvements Adding a space to a contact search term performs poorly. Issue: #2769 Local DB grows without limit. Issue: #24342.8.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.8.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.8.2/Bug fixes Ensure PouchDB doesn&rsquo;t mis-label TECNO phones as devices running Safari. Issue: #27972.8.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.8.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.8.3/Performance improvements Remove traffic statistics collection. Issue: #28862.8.4 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.8.4/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.8.4/Bug fixes Debounce form submissions to stop duplicate submissions. Issue: #29092.8.5 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.8.5/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.8.5/ No changes, only a bump in version number to trigger a new release.2.9.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.9.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.9.0/Features Redesign of People tab to introduce patient centric workflows. Create Task and Target based on reports using short patient_id format. Issue: #2986 Calculate Z-Score within app workflow form. Issue: #2915 Transitions do not run for XForms. Issue: #2864 CHWs should not be able to edit their own area. Issue: #2844 Allow for people-centric SMS workflows. Issue: #2700 Unique &ldquo;add person&rdquo; forms to a place. Issue: #2693 Store GPS failure. Issue: #2670 Progressive Web App.2.9.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.9.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.9.1/Bug fixes Added a migration to fix scheduled messages so they can be sent by medic-gateway. Issue: #30153.0.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.0.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.0.0/Upgrade notes The supported versions for client and server software have been changed significantly. Make sure your software meets the requirements before upgrading to 3.0.0. The /api/v1/messages endpoint has been removed as it was no longer actively used, and contained bugs. [#3971] The ANC analytics page and the following APIs have been removed as they are no longer used. [#1002] /api/active-pregnancies /api/upcoming-appointments /api/missed-appointments /api/upcoming-due-dates /api/high-risk /api/total-births /api/missing-delivery-reports /api/delivery-location /api/visits-completed /api/visits-during /api/monthly-registrations /api/monthly-deliveries The /api/v1/export/messages, /api/v1/export/forms, and /api/v1/export/contacts endpoints have been removed in favor of /api/v2/export/messages, /api/v2/export/reports, and /api/v2/export/contacts respectively.3.1.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.1.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.1.0/Upgrade notes There are no breaking changes when upgrading from 3.0.x. +What&rsquo;s New SMS spam protection SMS message generation will be skipped if an identical message has been generated recently meaning our software won&rsquo;t get into an infinite loop with autoresponding robots. [#4715]. +Flexible phone number validation Phone number validation can be configured to be strict (default), tolerant, or disabled altogether. This is useful if phone numbers are being incorrectly rejected as invalid which can happen if carriers update their number ranges.3.10.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.10.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.10.0/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes Updates to meta databases replication The replication of users meta databases to the conglomerate, medic-users-meta database, is no longer configurable. This task now runs every day, at 2am UTC, and replicates feedback and telemetry documents to medic-users-meta database. +Android wrapper update required for new features Supporting remote first-time login is only fully functional while using medic-android version 0.3.10.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.10.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.10.1/Known issues Check the repository for the latest known issues. +Supported software There are no required changes to the supported software matrix from 3.0.0. +Node CouchDB Browsers SMS bridge Android medic-android medic-couch2pg 8.11+ 2.1+ Chrome 53+, Firefox latest medic-gateway 4.4+ 0.4.5+ 3.0+ Bug fixes cht-core#6700: For targets without idType, the winner emission is not deterministic3.10.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.10.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.10.2/Known issues Check the repository for the latest known issues. +Supported software There are no required changes to the supported software matrix from 3.0.0. +Node CouchDB Browsers SMS bridge Android medic-android medic-couch2pg 8.11+ 2.1+ Chrome 53+, Firefox latest medic-gateway 4.4+ 0.4.5+ 3.0+ Bug fixes cht-core#6848: Language translations not working when not supported by make-plural Features cht-core#6849: Add bootstrap-datepicker translations for Tagalog (tl) Illonggo (hil) and Bisaya (ceb) languages cht-core#6861: Add moment locales for Tagalog (tl) Illonggo (hil) and Bisaya (ceb) languages We recognize that it is atypical to have new features in a &ldquo;bugfix&rdquo; version.3.10.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.10.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.10.3/Known issues Check the repository for the latest known issues. +Supported software There are no required changes to the supported software matrix from 3.0.0. +Node CouchDB Browsers SMS bridge Android medic-android medic-couch2pg 8.11+ 2.1+ Chrome 53+, Firefox latest medic-gateway 4.4+ 0.4.5+ 3.0+ Upgrade notes This release fixes issues around using the default hierarchy and having contact documents that have a default type (person, clinic, health_center or district_hospital) and also have a contact_type property.3.10.4 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.10.4/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.10.4/Known issues Check the repository for the latest known issues. +Breaking changes None. +UI/UX changes None. +Bug fixes #7169: API can return 401 status codes for valid sessions under load, forcing users to be logged out3.10.5 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.10.5/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.10.5/This release adds a fix that overrides the default 2 minute timeout of Node HTTP requests. Client requests will no longer timeout at API level. Timeouts are still possible, but can only come from the load balancer or CouchDB. +Known issues Check the repository for the latest known issues. +Breaking changes None. +UI/UX changes None. +Bug fixes #7183: Client requests receive a 502 statuscode after 2 minutes of idle time3.11.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.11.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.11.0/Known issues CHT Android medic-android#127: Image upload forms crash the app. This has been broken for some time and is not easy to fix while supporting Android 4.4 so the resolution has been deferred until we can make this breaking change. Reach out if you require this feature in the near future. CHT Core Check the repository for the latest known issues. +Upgrade notes This upgrade can be rolled out remotely.3.11.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.11.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.11.1/Known issues Check the repository for the latest known issues. +Breaking changes None. +UI/UX changes None. +Bug fixes #7169: API can return 401 status codes for valid sessions under load, forcing users to be logged out3.11.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.11.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.11.2/This release adds a fix that overrides the default 2 minute timeout of Node HTTP requests. Client requests will no longer timeout at API level. Timeouts are still possible, but can only come from the load balancer or CouchDB. +Known issues Check the repository for the latest known issues. +Breaking changes None. +UI/UX changes None. +Bug fixes #7183: Client requests receive a 502 statuscode after 2 minutes of idle time3.11.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.11.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.11.3/This release adds a fix for the phone widget&rsquo;s validation when editing forms. +Known issues Check the repository for the latest known issues. +Breaking changes None. +UI/UX changes None. +Bug fixes #7261: Phone field invalid when editing a contact with a valid phone number.3.12.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.12.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.12.0/Known issues Check the repository for the latest known issues. +Upgrade notes This upgrade can be rolled out remotely. Users will download the new version in the background and be prompted to reload the app when it&rsquo;s ready. A small amount of data will be needed to download the new version. Upgrading to this version does not require other applications to be upgraded. This release does not drop support for any hardware or software that works with the previous version.3.12.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.12.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.12.1/This release adds a fix for the phone widget&rsquo;s validation when editing forms. +Known issues Check the repository for the latest known issues. +Breaking changes None. +UI/UX changes None. +Bug fixes #7261: Phone field invalid when editing a contact with a valid phone number.3.13.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.13.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.13.0/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes Configuration for Task Due Date Display By default, an overdue task is simply displayed as &ldquo;Due today&rdquo;. This enhancement adds configuration to allow for displaying the number of days passed since the task&rsquo;s due date. This configuration is modified by setting the task_days_overdue value as described in the documentation. +If the configuration is not set, there will be no UX changes to the way that overdue tasks are displayed.3.14.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.14.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.14.0/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes New dialog showing sync status When an offline user manually triggers a sync via the hamburger menu, a notification dialog is opened indicating the status of the sync process. +Sync in progress: Sync complete: Sync failed (with retry option): #5207: Show dialog when user clicks &ldquo;Sync Now&rdquo; in hamburger menu +Updated icon for the Create Report button The icon for the Create Report button has been updated so that its purpose is more clear.3.14.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.14.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.14.1/This release adds a fix for repeat groups in Enketo forms to pick the correct translation for fields. +Known issues Check the repository for the latest known issues. +Breaking changes None. +UI/UX changes None. +Bug fixes #7515: Translations not working when using repeat groups with dynamic repeat count3.14.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.14.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.14.2/This release adds a fix for repeat groups in Enketo forms using choice_filter. +Known issues Check the repository for the latest known issues. +Breaking changes None. +UI/UX changes None. +Bug fixes #7550: Fix blank labels in forms with repeat group using choice_filter3.15.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.15.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.15.0/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Highlights Support reporting of Bikram Sambat dates using JSON forms SMS forms can now include a date form for submitting an exact date in Bikram Sambat format. Learn how to use this feature in the documentation. +#4613: Support reporting of dates as exact date using text forms +A new API for adding users in bulk Previously there was an API for creating a single user but this feature adds an API for creating multiple users in one go.3.16.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.16.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.16.0/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes #6028: Gracefully handle overflowing form title text Highlights Support adding many users at once using data from a CSV file New CHT users (and associated places) can now be added in bulk by importing the data from a CSV file. Learn how to use this feature in the documentation. +#7706: Add support for bulk user upload from CSV3.16.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.16.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.16.1/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #7912: Calling /api/v1/settings/deprecated-transitions prevents any future transitions to run over new records in API #7933: Error 500 when processing SMS message missing year from Bikram Sambat aggregate3.17.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.17.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.17.0/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes Action button labels now show for all devices Action button labels now show on lower resolution phones, making it easier to understand what each action button does. +#7721: Icon labels on action buttons are not showing at the default dp for commonly used mobile devices. Update to search and filters on the Contacts and Reports tabs Search and filters on the Contacts and Reports tabs have been updated to more closely align with Android UX and material design patterns.3.17.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.17.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.17.1/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #7912: Calling /api/v1/settings/deprecated-transitions prevents any future transitions to run over new records in API #7933: Error 500 when processing SMS message missing year from Bikram Sambat aggregate3.17.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.17.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.17.2/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #7670: Same patient id assigned to multiple patients in a same instance #8576: Low performance in Reports tab for users with thousands of places3.2.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.2.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.2.0/Upgrade notes There are no breaking changes when upgrading from 3.1.x. +Muting You can now mute people and places which stops any scheduled messages from being sent and updates the UI for that contact. [#4767] +For more information read the feature overview and the configuration documentation. +UHC performance If you have UHC configured loading contacts is now much faster. [#4768] +Benchmarks The following benchmarks were taken on a Tecno Y4 as a CHW with a representative amount of data.3.2.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.2.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.2.1/Bug fixes More reliable replication In earlier versions if a device&rsquo;s replication connection got interrupted some documents may never be replicated to that device leaving it in an unknown state. To avoid this it is recommended that everyone upgrade to 3.2.1 or above as soon as possible. [#5235] +Refresh dialog not shown When upgrading from 2.x to 3.2.0 the dialog to update isn&rsquo;t presented to the user. The user is unaware the app updated and would need to do a manual refresh to get the latest app changes.3.3.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.3.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.3.0/Upgrade notes There are no breaking changes when upgrading from 3.2.x. +Purging Documents on phones can now be deleted once no longer useful. This allows for space on the device to be recovered and improves performance. Read the configuration documentation for how to add script to indicate that a document is no longer useful and is ready for purging. [#5048] +Outgoing messages screen Administrators can now see all outgoing messages that are either scheduled, due, or unable to be sent.3.4.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.4.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.4.0/Known issues medic#5617: Broken functionality after upgrade to 3.4 with custom locales Upgrade notes Breaking changes There are no breaking changes when upgrading from 3.3.x. +Supported software There are no required changes to the supported software matrix from 3.0.0. +Node CouchDB Browsers SMS bridge Android medic-android medic-couch2pg 8.11+ 2.1+ Chrome 53+, Firefox latest medic-gateway 4.4+ 0.4.5+ 3.0+ Scale to support more users In previous versions users who had an internet connection would maintain a continuous request to the server waiting for any relevant database changes.3.4.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.4.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.4.1/Bug fixes Unicode form codes not clearing schedules When using non-Latin characters in a form code our software failed to find the right schedules to clear so unwanted messages were being sent. This affects versions from 3.0.0 to 3.4.0. medic#56983.5.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.5.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.5.0/Known issues A small performance regression when loading the list of Contacts. medic#5755. Upgrade notes Breaking changes There are no breaking changes when upgrading from 3.4.x. +Supported software There are no required changes to the supported software matrix from 3.0.0. +Node CouchDB Browsers SMS bridge Android medic-android medic-couch2pg 8.11+ 2.1+ Chrome 53+, Firefox latest medic-gateway 4.4+ 0.4.5+ 3.0+ Performance Optimizing the history tab We found that loading the History tab was taking a long time in projects with many places.3.6.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.6.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.6.0/Known issues None. +Upgrade notes Breaking changes There are no breaking changes when upgrading from 3.5.x. +Supported software There are no required changes to the supported software matrix from 3.0.0. +Node CouchDB Browsers SMS bridge Android medic-android medic-couch2pg 8.11+ 2.1+ Chrome 53+, Firefox latest medic-gateway 4.4+ 0.4.5+ 3.0+ Africa&rsquo;s Talking integration Version 3.6.0 includes an integration with the Africa&rsquo;s Talking SMS aggregator. This allows us to offer a cloud based alternative to managing your own medic-gateway devices.3.6.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.6.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.6.1/Bug fixes medic#5844: Source validation failing for Africa&rsquo;s Talking aggregator integration3.6.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.6.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.6.2/Performance fixes medic#5942: Replace underscore with lodash in replication3.7.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.7.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.7.0/Known issues None. +Upgrade notes Breaking changes A new OS level dependency (xsltproc) is required for generating XFORM html on the server. Not fully supported by medic-conf@2.x. For full functionality, users must upgrade to medic-conf@3.x. With the introduction of configurable hierarchies, additional configuration must be added. More information about how to set up configurable hierarchies. Supported software There are no required changes to the supported software matrix from 3.0.0. +Node CouchDB Browsers SMS bridge Android medic-android medic-couch2pg 8.3.7.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.7.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.7.1/Bug fixes cht-core#6025: Outbound can halt scheduler execution permanently cht-core#6029: Inconsistent field label in CHT Reference App - Blue skin color cht-core#6052: Missing labels for report form fields cht-core#6065: Confirmation notification displayed while navigating away from tasks list cht-core#6071: Configurable contacts not correctly mapped as SMS recipients medic-conf#260: Fields are not showing when custom type is defined for appliesToType3.8.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.8.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.8.0/Known issues No known issues. +Upgrade notes Breaking changes The changes to tasks and targets require a &ldquo;3.8 compatible&rdquo; partner configurations to be deployed for the tasks and target tabs to continue to function. You&rsquo;ll see errors on these tabs and the console error Rules Engine: Updates to the nools schema are required if the configuration is not compatible with 3.8. All &ldquo;3.8 compatible&rdquo; configurations are backward compatible and can be deployed safely to any Core Framework version.3.8.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.8.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.8.1/Upgrade notes CouchDB view code has been modified which will require rebuilding which may take some time depending on how many docs you have. We recommend you use the Stage feature to rebuild the views before upgrading to reduce server downtime. Webapp code has been modified and therefore users will download the application again. Supported software There are no required changes to the supported software matrix from 3.0.0. +Node CouchDB Browsers SMS bridge Android medic-android medic-couch2pg 8.3.8.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.8.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.8.2/Known issues When logging out (or after being logged out), users could end up in a redirect loop (cht-core#6337). Supported software There are no required changes to the supported software matrix from 3.0.0. +Node CouchDB Browsers SMS bridge Android medic-android medic-couch2pg 8.11+ 2.1+ Chrome 53+, Firefox latest medic-gateway 4.4+ 0.4.5+ 3.0+ Bug fixes cht-core#6583: Users are forced to login one year after they last logged in3.9.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.9.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.9.0/Known issues None. +Upgrade notes Breaking changes Docker image update required Prior to initiating an upgrade to 3.9, you will need to update the CHT Docker Image and a few packages inside the medic-os container. Please closely follow our 3.9 CHT Docker Image Upgrade Process +This image updates the horticulturalist package to stage ddocs properly. +Outbound Push only sends each report once The implementation of Outbound Push has changed as part of cht-core#6306.3.9.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.9.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.9.1/Supported software There are no required changes to the supported software matrix from 3.0.0. +Node CouchDB Browsers SMS bridge Android medic-android medic-couch2pg 8.11+ 2.1+ Chrome 53+, Firefox latest medic-gateway 4.4+ 0.4.5+ 3.0+ Bug fixes cht-core#6562: Switching between tabs while tasks are being calculated can result in having multiple task documents with the same emission id cht-core#6583: Users are forced to login one year after they last logged in3.9.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.9.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.9.2/Supported software There are no required changes to the supported software matrix from 3.0.0. +Node CouchDB Browsers SMS bridge Android medic-android medic-couch2pg 8.11+ 2.1+ Chrome 53+, Firefox latest medic-gateway 4.4+ 0.4.5+ 3.0+ Bug fixes cht-core#6700: For targets without idType, the winner emission is not deterministic cht-core#6756: Backwards compatibility for location gathering4.0.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.0.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.0.0/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes To prepare for this release, please read through our Preparing to Upgrade documentation. +Upgrade process Be aware that as this is a major upgrade some manual steps are required - following the usual upgrade process will not work. Data migration documentation is coming soon, but in the meantime please reach out on the forum for direct support in upgrading.4.0.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.0.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.0.1/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #7912: Calling /api/v1/settings/deprecated-transitions prevents any future transitions to run over new records in API #7918: Error loading forms containing countdown-widget #7933: Error 500 when processing SMS message missing year from Bikram Sambat aggregate Contributors Thanks to all who committed changes for this release! +Gareth Bowen Diana Barsan4.1.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.1.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.1.0/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes Bulk delete of reports redesign The bulk delete of Reports tab has been redesigned to allow the user to select multiple reports and delete them. You can read more about it in the Reports tab docs. +#7778: Bulk delete of reports redesign Highlights Support replacing a CHW user without any connectivity Offline users can now be replaced on a device so that a new user can use that device without needing to immediately sync with the server.4.1.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.1.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.1.1/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Features #7712: Add moment and datepicker translations for Luganda Technical improvements #8110: E2E test failing when next year is leap year #8112: Integration E2E test failing when next year is leap year Contributors Thanks to all who committed changes for this release! +Gareth Bowen Andra Blaj Diana Barsan4.1.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.1.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.1.2/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #8173: API Changes watcher skips changes - or becomes blocked #8205: Nginx can&rsquo;t connect to API after container restarts because of dynamic IP allocation Contributors Thanks to all who committed changes for this release! +Diana Barsan Gareth Bowen4.2.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.2.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.2.0/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes Floating Action Button The additive actions (creating reports, places, people, etc&hellip;) have moved from the bottom action bar to a Floating Action Button that opens a menu with all actions. This change aligns the CHT more closely with Android UX and material design patterns, and applies to the Messages, Reports, and Contacts tab.4.2.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.2.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.2.1/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Improvements #8214: Align nginx and ALB timeout values Contributors Thanks to all who committed changes for this release! +Diana Barsan Jennifer Q Gareth Bowen4.2.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.2.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.2.2/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #8161: Telemetry doc metadata.versions.app is unknown for all telemetry docs #8359: Infinite scrolling not working on Contacts tab Contributors Thanks to all who committed changes for this release! +Diana Barsan Jennifer Q Gareth Bowen4.2.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.2.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.2.3/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #8539: Sentinel stuck in infinite loop when 100 sequential deletions fail to generate tombstones Technical improvements #8558: Skip Protractor e2e tests suites on old supported branches Contributors Thanks to all who committed changes for this release! +Diana Barsan Jennifer Q Gareth Bowen4.2.4 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.2.4/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.2.4/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #7670: Same patient id assigned to multiple patients in a same instance #8576: Low performance in Reports tab for users with thousands of places4.3.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.3.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.3.0/Known issues Check the repository for the latest known issues. +Upgrade notes Continuous downwards replication (the algorithm through which offline users download docs from the server) has been completely rewritten. This change required a high number of view updates, which implies that staging this upgrade and indexing views before upgrading will be a lengthy process - depending on the size of the database. Additionally, the server might need additional storage while this process is ongoing.4.3.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.3.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.3.1/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #8478: Invalid phone for patient still running triggers Contributors Thanks to all who committed changes for this release! +Prajwol Shrestha Gareth Bowen Jennifer Q Diana Barsan4.3.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.3.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.3.2/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #7670: Same patient id assigned to multiple patients in a same instance #8576: Low performance in Reports tab for users with thousands of places #8589: Users unable to edit the report they created4.4.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.4.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.4.0/Known issues Check the repository for the latest known issues. +Upgrade notes The deprecated hardcoded national_admin role no longer behaves as a CouchDb admin. The role now behaves as an ordinary online user role, with no additional implicit permissions. Configurations using national_admin role can either switch to using a CouchDb admin for admin-only tasks and/or granting more permissions to national_admin. +Breaking changes None. +UI/UX changes CHT dialogs. CHT dialogs were updated to align with Material guidelines.4.4.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.4.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.4.1/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #7670: Same patient id assigned to multiple patients in a same instance #8576: Low performance in Reports tab for users with thousands of places #8589: Users unable to edit the report they created Contributors Thanks to all who committed changes for this release! +Jennifer Q Gareth Bowen Diana Barsan4.4.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.4.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.4.2/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #8745: error.loading.form.no_authorized when opening a form from tasks tab because context evaluates to false Contributors Thanks to all who committed changes for this release! +Diana Barsan4.5.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.5.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.5.0/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes #8513: Align &ldquo;Target&rdquo; components with Material design guidelines #8541: Options with long names look wrong in enketo selects Highlights Schedules can be created by passing array on start_from It is now possible to configure the start_from property to accept array of fields while configuring SMS schedules. This allows users to pass multiple fields and the first existing field will be used to create schedules.4.5.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.5.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.5.1/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #8745: error.loading.form.no_authorized when opening a form from tasks tab because context evaluates to false Contributors Thanks to all who committed changes for this release! +Diana Barsan Jennifer Q4.5.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.5.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.5.2/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #8837: Telemetry date not logged on telemetry documents Contributors Thanks to all who committed changes for this release! +Jennifer Q4.6.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.6.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.6.0/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes #6177: Improve look and utility of the &ldquo;About&rdquo; page #7770: Browser compatibility modal notice for Chrome version 75-90 #8075: Update default branding to CHT logo #8660: Link to Contact&rsquo;s Profile from Messages tab Highlights Allow contact searches in forms to be filtered by descendants of the current contact A contact selector can be used in forms to allow users to select a contact by searching.4.7.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.7.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.7.0/Known issues Due to a critical bug that prevented further upgrades from 4.7.0, we have have removed 4.7.0 from our releases list, so that it will not be possible to upgrade to 4.7.0. The bug has been fixed in 4.7.1. If you have already installed or upgraded to 4.7.0, please connect with us on the forum to get unblocked. We apologise for the inconvenience. Upgrade notes Breaking changes None. +UI/UX changes #5807: Fix widget wrapping when displaying multiple columns.4.7.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.7.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.7.1/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #9117: Impossible to upgrade from 4.7.0 Technical improvements None. +Contributors Thanks to all who committed changes for this release! +Diana Barsan4.7.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.7.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.7.2/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #9166: &ldquo;users-meta failed with compilation_error&rdquo; when upgrading from 4.2.4 #9187: Haproxy unit tests are failing due to haproxy patch release Technical improvements None.4.8.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.8.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.8.0/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Highlights Performance The Contacts page loads much faster now. Using apdex scores and testing on low spec devices, we were able to identify slow areas and make improvements to both the list view and the detail view. +Apdex Improvements #9006: Improves performance of the list view by reducing the number of rows fetched each time #9019: Improves performance of the detail view by reducing loops for tasks and reports Security Two security issues with severity level of &ldquo;high&rdquo; and one of &ldquo;low&rdquo; were fixed in this release.4.8.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.8.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.8.1/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #9166: &ldquo;users-meta failed with compilation_error&rdquo; when upgrading from 4.2.4 #9187: Haproxy unit tests are failing due to haproxy patch release Technical improvements None.4.9.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.9.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.9.0/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes This release adds support for assigning multiple places to users. Users who are configured to have multiple places will see some subtle UI changes as described in the Highlights section. For a video walkthrough of the changes, check out the June 2024 CHT Round-up call and this forum post. +Highlights Allow multiple places to be assigned to users To better support Supervisors who manage CHWs across multiple areas, it is now possible to assign more than one “Place” (ie Community Health Unit, Health Center, etc…) to a user. \ No newline at end of file diff --git a/css/swagger-ui.css b/css/swagger-ui.css deleted file mode 100644 index c61e5a85f7..0000000000 --- a/css/swagger-ui.css +++ /dev/null @@ -1,4 +0,0 @@ -.swagger-ui{ - /*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */font-family:sans-serif;color:#3b4151}.swagger-ui html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}.swagger-ui body{margin:0}.swagger-ui article,.swagger-ui aside,.swagger-ui footer,.swagger-ui header,.swagger-ui nav,.swagger-ui section{display:block}.swagger-ui h1{font-size:2em;margin:.67em 0}.swagger-ui figcaption,.swagger-ui figure,.swagger-ui main{display:block}.swagger-ui figure{margin:1em 40px}.swagger-ui hr{box-sizing:content-box;height:0;overflow:visible}.swagger-ui pre{font-family:monospace,monospace;font-size:1em}.swagger-ui a{background-color:transparent;-webkit-text-decoration-skip:objects}.swagger-ui abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.swagger-ui b,.swagger-ui strong{font-weight:inherit;font-weight:bolder}.swagger-ui code,.swagger-ui kbd,.swagger-ui samp{font-family:monospace,monospace;font-size:1em}.swagger-ui dfn{font-style:italic}.swagger-ui mark{background-color:#ff0;color:#000}.swagger-ui small{font-size:80%}.swagger-ui sub,.swagger-ui sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}.swagger-ui sub{bottom:-.25em}.swagger-ui sup{top:-.5em}.swagger-ui audio,.swagger-ui video{display:inline-block}.swagger-ui audio:not([controls]){display:none;height:0}.swagger-ui img{border-style:none}.swagger-ui svg:not(:root){overflow:hidden}.swagger-ui button,.swagger-ui input,.swagger-ui optgroup,.swagger-ui select,.swagger-ui textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}.swagger-ui button,.swagger-ui input{overflow:visible}.swagger-ui button,.swagger-ui select{text-transform:none}.swagger-ui [type=reset],.swagger-ui [type=submit],.swagger-ui button,.swagger-ui html [type=button]{-webkit-appearance:button}.swagger-ui [type=button]::-moz-focus-inner,.swagger-ui [type=reset]::-moz-focus-inner,.swagger-ui [type=submit]::-moz-focus-inner,.swagger-ui button::-moz-focus-inner{border-style:none;padding:0}.swagger-ui [type=button]:-moz-focusring,.swagger-ui [type=reset]:-moz-focusring,.swagger-ui [type=submit]:-moz-focusring,.swagger-ui button:-moz-focusring{outline:1px dotted ButtonText}.swagger-ui fieldset{padding:.35em .75em .625em}.swagger-ui legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}.swagger-ui progress{display:inline-block;vertical-align:baseline}.swagger-ui textarea{overflow:auto}.swagger-ui [type=checkbox],.swagger-ui [type=radio]{box-sizing:border-box;padding:0}.swagger-ui [type=number]::-webkit-inner-spin-button,.swagger-ui [type=number]::-webkit-outer-spin-button{height:auto}.swagger-ui [type=search]{-webkit-appearance:textfield;outline-offset:-2px}.swagger-ui [type=search]::-webkit-search-cancel-button,.swagger-ui [type=search]::-webkit-search-decoration{-webkit-appearance:none}.swagger-ui ::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}.swagger-ui details,.swagger-ui menu{display:block}.swagger-ui summary{display:list-item}.swagger-ui canvas{display:inline-block}.swagger-ui template{display:none}.swagger-ui [hidden]{display:none}.swagger-ui .debug *{outline:1px solid gold}.swagger-ui .debug-white *{outline:1px solid #fff}.swagger-ui .debug-black *{outline:1px solid #000}.swagger-ui .debug-grid{background:transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MTRDOTY4N0U2N0VFMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MTRDOTY4N0Q2N0VFMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3NjcyQkQ3NjY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3NjcyQkQ3NzY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PsBS+GMAAAAjSURBVHjaYvz//z8DLsD4gcGXiYEAGBIKGBne//fFpwAgwAB98AaF2pjlUQAAAABJRU5ErkJggg==) repeat 0 0}.swagger-ui .debug-grid-16{background:transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6ODYyRjhERDU2N0YyMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ODYyRjhERDQ2N0YyMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3NjcyQkQ3QTY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3NjcyQkQ3QjY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PvCS01IAAABMSURBVHjaYmR4/5+BFPBfAMFm/MBgx8RAGWCn1AAmSg34Q6kBDKMGMDCwICeMIemF/5QawEipAWwUhwEjMDvbAWlWkvVBwu8vQIABAEwBCph8U6c0AAAAAElFTkSuQmCC) repeat 0 0}.swagger-ui .debug-grid-8-solid{background:#fff url(data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAAAAAD/4QMxaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzExMSA3OS4xNTgzMjUsIDIwMTUvMDkvMTAtMDE6MTA6MjAgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE1IChNYWNpbnRvc2gpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkIxMjI0OTczNjdCMzExRTZCMkJDRTI0MDgxMDAyMTcxIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkIxMjI0OTc0NjdCMzExRTZCMkJDRTI0MDgxMDAyMTcxIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QjEyMjQ5NzE2N0IzMTFFNkIyQkNFMjQwODEwMDIxNzEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QjEyMjQ5NzI2N0IzMTFFNkIyQkNFMjQwODEwMDIxNzEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7/7gAOQWRvYmUAZMAAAAAB/9sAhAAbGhopHSlBJiZBQi8vL0JHPz4+P0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHAR0pKTQmND8oKD9HPzU/R0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0f/wAARCAAIAAgDASIAAhEBAxEB/8QAWQABAQAAAAAAAAAAAAAAAAAAAAYBAQEAAAAAAAAAAAAAAAAAAAIEEAEBAAMBAAAAAAAAAAAAAAABADECA0ERAAEDBQAAAAAAAAAAAAAAAAARITFBUWESIv/aAAwDAQACEQMRAD8AoOnTV1QTD7JJshP3vSM3P//Z) repeat 0 0}.swagger-ui .debug-grid-16-solid{background:#fff url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NzY3MkJEN0U2N0M1MTFFNkIyQkNFMjQwODEwMDIxNzEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NzY3MkJEN0Y2N0M1MTFFNkIyQkNFMjQwODEwMDIxNzEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3NjcyQkQ3QzY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3NjcyQkQ3RDY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pve6J3kAAAAzSURBVHjaYvz//z8D0UDsMwMjSRoYP5Gq4SPNbRjVMEQ1fCRDg+in/6+J1AJUxsgAEGAA31BAJMS0GYEAAAAASUVORK5CYII=) repeat 0 0}.swagger-ui .border-box,.swagger-ui a,.swagger-ui article,.swagger-ui body,.swagger-ui code,.swagger-ui dd,.swagger-ui div,.swagger-ui dl,.swagger-ui dt,.swagger-ui fieldset,.swagger-ui footer,.swagger-ui form,.swagger-ui h1,.swagger-ui h2,.swagger-ui h3,.swagger-ui h4,.swagger-ui h5,.swagger-ui h6,.swagger-ui header,.swagger-ui html,.swagger-ui input[type=email],.swagger-ui input[type=number],.swagger-ui input[type=password],.swagger-ui input[type=tel],.swagger-ui input[type=text],.swagger-ui input[type=url],.swagger-ui legend,.swagger-ui li,.swagger-ui main,.swagger-ui ol,.swagger-ui p,.swagger-ui pre,.swagger-ui section,.swagger-ui table,.swagger-ui td,.swagger-ui textarea,.swagger-ui th,.swagger-ui tr,.swagger-ui ul{box-sizing:border-box}.swagger-ui .aspect-ratio{height:0;position:relative}.swagger-ui .aspect-ratio--16x9{padding-bottom:56.25%}.swagger-ui .aspect-ratio--9x16{padding-bottom:177.77%}.swagger-ui .aspect-ratio--4x3{padding-bottom:75%}.swagger-ui .aspect-ratio--3x4{padding-bottom:133.33%}.swagger-ui .aspect-ratio--6x4{padding-bottom:66.6%}.swagger-ui .aspect-ratio--4x6{padding-bottom:150%}.swagger-ui .aspect-ratio--8x5{padding-bottom:62.5%}.swagger-ui .aspect-ratio--5x8{padding-bottom:160%}.swagger-ui .aspect-ratio--7x5{padding-bottom:71.42%}.swagger-ui .aspect-ratio--5x7{padding-bottom:140%}.swagger-ui .aspect-ratio--1x1{padding-bottom:100%}.swagger-ui .aspect-ratio--object{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;z-index:100}@media screen and (min-width:30em){.swagger-ui .aspect-ratio-ns{height:0;position:relative}.swagger-ui .aspect-ratio--16x9-ns{padding-bottom:56.25%}.swagger-ui .aspect-ratio--9x16-ns{padding-bottom:177.77%}.swagger-ui .aspect-ratio--4x3-ns{padding-bottom:75%}.swagger-ui .aspect-ratio--3x4-ns{padding-bottom:133.33%}.swagger-ui .aspect-ratio--6x4-ns{padding-bottom:66.6%}.swagger-ui .aspect-ratio--4x6-ns{padding-bottom:150%}.swagger-ui .aspect-ratio--8x5-ns{padding-bottom:62.5%}.swagger-ui .aspect-ratio--5x8-ns{padding-bottom:160%}.swagger-ui .aspect-ratio--7x5-ns{padding-bottom:71.42%}.swagger-ui .aspect-ratio--5x7-ns{padding-bottom:140%}.swagger-ui .aspect-ratio--1x1-ns{padding-bottom:100%}.swagger-ui .aspect-ratio--object-ns{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;z-index:100}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .aspect-ratio-m{height:0;position:relative}.swagger-ui .aspect-ratio--16x9-m{padding-bottom:56.25%}.swagger-ui .aspect-ratio--9x16-m{padding-bottom:177.77%}.swagger-ui .aspect-ratio--4x3-m{padding-bottom:75%}.swagger-ui .aspect-ratio--3x4-m{padding-bottom:133.33%}.swagger-ui .aspect-ratio--6x4-m{padding-bottom:66.6%}.swagger-ui .aspect-ratio--4x6-m{padding-bottom:150%}.swagger-ui .aspect-ratio--8x5-m{padding-bottom:62.5%}.swagger-ui .aspect-ratio--5x8-m{padding-bottom:160%}.swagger-ui .aspect-ratio--7x5-m{padding-bottom:71.42%}.swagger-ui .aspect-ratio--5x7-m{padding-bottom:140%}.swagger-ui .aspect-ratio--1x1-m{padding-bottom:100%}.swagger-ui .aspect-ratio--object-m{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;z-index:100}}@media screen and (min-width:60em){.swagger-ui .aspect-ratio-l{height:0;position:relative}.swagger-ui .aspect-ratio--16x9-l{padding-bottom:56.25%}.swagger-ui .aspect-ratio--9x16-l{padding-bottom:177.77%}.swagger-ui .aspect-ratio--4x3-l{padding-bottom:75%}.swagger-ui .aspect-ratio--3x4-l{padding-bottom:133.33%}.swagger-ui .aspect-ratio--6x4-l{padding-bottom:66.6%}.swagger-ui .aspect-ratio--4x6-l{padding-bottom:150%}.swagger-ui .aspect-ratio--8x5-l{padding-bottom:62.5%}.swagger-ui .aspect-ratio--5x8-l{padding-bottom:160%}.swagger-ui .aspect-ratio--7x5-l{padding-bottom:71.42%}.swagger-ui .aspect-ratio--5x7-l{padding-bottom:140%}.swagger-ui .aspect-ratio--1x1-l{padding-bottom:100%}.swagger-ui .aspect-ratio--object-l{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;z-index:100}}.swagger-ui img{max-width:100%}.swagger-ui .cover{background-size:cover!important}.swagger-ui .contain{background-size:contain!important}@media screen and (min-width:30em){.swagger-ui .cover-ns{background-size:cover!important}.swagger-ui .contain-ns{background-size:contain!important}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .cover-m{background-size:cover!important}.swagger-ui .contain-m{background-size:contain!important}}@media screen and (min-width:60em){.swagger-ui .cover-l{background-size:cover!important}.swagger-ui .contain-l{background-size:contain!important}}.swagger-ui .bg-center{background-repeat:no-repeat;background-position:50%}.swagger-ui .bg-top{background-repeat:no-repeat;background-position:top}.swagger-ui .bg-right{background-repeat:no-repeat;background-position:100%}.swagger-ui .bg-bottom{background-repeat:no-repeat;background-position:bottom}.swagger-ui .bg-left{background-repeat:no-repeat;background-position:0}@media screen and (min-width:30em){.swagger-ui .bg-center-ns{background-repeat:no-repeat;background-position:50%}.swagger-ui .bg-top-ns{background-repeat:no-repeat;background-position:top}.swagger-ui .bg-right-ns{background-repeat:no-repeat;background-position:100%}.swagger-ui .bg-bottom-ns{background-repeat:no-repeat;background-position:bottom}.swagger-ui .bg-left-ns{background-repeat:no-repeat;background-position:0}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .bg-center-m{background-repeat:no-repeat;background-position:50%}.swagger-ui .bg-top-m{background-repeat:no-repeat;background-position:top}.swagger-ui .bg-right-m{background-repeat:no-repeat;background-position:100%}.swagger-ui .bg-bottom-m{background-repeat:no-repeat;background-position:bottom}.swagger-ui .bg-left-m{background-repeat:no-repeat;background-position:0}}@media screen and (min-width:60em){.swagger-ui .bg-center-l{background-repeat:no-repeat;background-position:50%}.swagger-ui .bg-top-l{background-repeat:no-repeat;background-position:top}.swagger-ui .bg-right-l{background-repeat:no-repeat;background-position:100%}.swagger-ui .bg-bottom-l{background-repeat:no-repeat;background-position:bottom}.swagger-ui .bg-left-l{background-repeat:no-repeat;background-position:0}}.swagger-ui .outline{outline:1px solid}.swagger-ui .outline-transparent{outline:1px solid transparent}.swagger-ui .outline-0{outline:0}@media screen and (min-width:30em){.swagger-ui .outline-ns{outline:1px solid}.swagger-ui .outline-transparent-ns{outline:1px solid transparent}.swagger-ui .outline-0-ns{outline:0}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .outline-m{outline:1px solid}.swagger-ui .outline-transparent-m{outline:1px solid transparent}.swagger-ui .outline-0-m{outline:0}}@media screen and (min-width:60em){.swagger-ui .outline-l{outline:1px solid}.swagger-ui .outline-transparent-l{outline:1px solid transparent}.swagger-ui .outline-0-l{outline:0}}.swagger-ui .ba{border-style:solid;border-width:1px}.swagger-ui .bt{border-top-style:solid;border-top-width:1px}.swagger-ui .br{border-right-style:solid;border-right-width:1px}.swagger-ui .bb{border-bottom-style:solid;border-bottom-width:1px}.swagger-ui .bl{border-left-style:solid;border-left-width:1px}.swagger-ui .bn{border-style:none;border-width:0}@media screen and (min-width:30em){.swagger-ui .ba-ns{border-style:solid;border-width:1px}.swagger-ui .bt-ns{border-top-style:solid;border-top-width:1px}.swagger-ui .br-ns{border-right-style:solid;border-right-width:1px}.swagger-ui .bb-ns{border-bottom-style:solid;border-bottom-width:1px}.swagger-ui .bl-ns{border-left-style:solid;border-left-width:1px}.swagger-ui .bn-ns{border-style:none;border-width:0}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .ba-m{border-style:solid;border-width:1px}.swagger-ui .bt-m{border-top-style:solid;border-top-width:1px}.swagger-ui .br-m{border-right-style:solid;border-right-width:1px}.swagger-ui .bb-m{border-bottom-style:solid;border-bottom-width:1px}.swagger-ui .bl-m{border-left-style:solid;border-left-width:1px}.swagger-ui .bn-m{border-style:none;border-width:0}}@media screen and (min-width:60em){.swagger-ui .ba-l{border-style:solid;border-width:1px}.swagger-ui .bt-l{border-top-style:solid;border-top-width:1px}.swagger-ui .br-l{border-right-style:solid;border-right-width:1px}.swagger-ui .bb-l{border-bottom-style:solid;border-bottom-width:1px}.swagger-ui .bl-l{border-left-style:solid;border-left-width:1px}.swagger-ui .bn-l{border-style:none;border-width:0}}.swagger-ui .b--black{border-color:#000}.swagger-ui .b--near-black{border-color:#111}.swagger-ui .b--dark-gray{border-color:#333}.swagger-ui .b--mid-gray{border-color:#555}.swagger-ui .b--gray{border-color:#777}.swagger-ui .b--silver{border-color:#999}.swagger-ui .b--light-silver{border-color:#aaa}.swagger-ui .b--moon-gray{border-color:#ccc}.swagger-ui .b--light-gray{border-color:#eee}.swagger-ui .b--near-white{border-color:#f4f4f4}.swagger-ui .b--white{border-color:#fff}.swagger-ui .b--white-90{border-color:hsla(0,0%,100%,.9)}.swagger-ui .b--white-80{border-color:hsla(0,0%,100%,.8)}.swagger-ui .b--white-70{border-color:hsla(0,0%,100%,.7)}.swagger-ui .b--white-60{border-color:hsla(0,0%,100%,.6)}.swagger-ui .b--white-50{border-color:hsla(0,0%,100%,.5)}.swagger-ui .b--white-40{border-color:hsla(0,0%,100%,.4)}.swagger-ui .b--white-30{border-color:hsla(0,0%,100%,.3)}.swagger-ui .b--white-20{border-color:hsla(0,0%,100%,.2)}.swagger-ui .b--white-10{border-color:hsla(0,0%,100%,.1)}.swagger-ui .b--white-05{border-color:hsla(0,0%,100%,.05)}.swagger-ui .b--white-025{border-color:hsla(0,0%,100%,.025)}.swagger-ui .b--white-0125{border-color:hsla(0,0%,100%,.0125)}.swagger-ui .b--black-90{border-color:rgba(0,0,0,.9)}.swagger-ui .b--black-80{border-color:rgba(0,0,0,.8)}.swagger-ui .b--black-70{border-color:rgba(0,0,0,.7)}.swagger-ui .b--black-60{border-color:rgba(0,0,0,.6)}.swagger-ui .b--black-50{border-color:rgba(0,0,0,.5)}.swagger-ui .b--black-40{border-color:rgba(0,0,0,.4)}.swagger-ui .b--black-30{border-color:rgba(0,0,0,.3)}.swagger-ui .b--black-20{border-color:rgba(0,0,0,.2)}.swagger-ui .b--black-10{border-color:rgba(0,0,0,.1)}.swagger-ui .b--black-05{border-color:rgba(0,0,0,.05)}.swagger-ui .b--black-025{border-color:rgba(0,0,0,.025)}.swagger-ui .b--black-0125{border-color:rgba(0,0,0,.0125)}.swagger-ui .b--dark-red{border-color:#e7040f}.swagger-ui .b--red{border-color:#ff4136}.swagger-ui .b--light-red{border-color:#ff725c}.swagger-ui .b--orange{border-color:#ff6300}.swagger-ui .b--gold{border-color:#ffb700}.swagger-ui .b--yellow{border-color:gold}.swagger-ui .b--light-yellow{border-color:#fbf1a9}.swagger-ui .b--purple{border-color:#5e2ca5}.swagger-ui .b--light-purple{border-color:#a463f2}.swagger-ui .b--dark-pink{border-color:#d5008f}.swagger-ui .b--hot-pink{border-color:#ff41b4}.swagger-ui .b--pink{border-color:#ff80cc}.swagger-ui .b--light-pink{border-color:#ffa3d7}.swagger-ui .b--dark-green{border-color:#137752}.swagger-ui .b--green{border-color:#19a974}.swagger-ui .b--light-green{border-color:#9eebcf}.swagger-ui .b--navy{border-color:#001b44}.swagger-ui .b--dark-blue{border-color:#00449e}.swagger-ui .b--blue{border-color:#357edd}.swagger-ui .b--light-blue{border-color:#96ccff}.swagger-ui .b--lightest-blue{border-color:#cdecff}.swagger-ui .b--washed-blue{border-color:#f6fffe}.swagger-ui .b--washed-green{border-color:#e8fdf5}.swagger-ui .b--washed-yellow{border-color:#fffceb}.swagger-ui .b--washed-red{border-color:#ffdfdf}.swagger-ui .b--transparent{border-color:transparent}.swagger-ui .b--inherit{border-color:inherit}.swagger-ui .br0{border-radius:0}.swagger-ui .br1{border-radius:.125rem}.swagger-ui .br2{border-radius:.25rem}.swagger-ui .br3{border-radius:.5rem}.swagger-ui .br4{border-radius:1rem}.swagger-ui .br-100{border-radius:100%}.swagger-ui .br-pill{border-radius:9999px}.swagger-ui .br--bottom{border-top-left-radius:0;border-top-right-radius:0}.swagger-ui .br--top{border-bottom-left-radius:0;border-bottom-right-radius:0}.swagger-ui .br--right{border-top-left-radius:0;border-bottom-left-radius:0}.swagger-ui .br--left{border-top-right-radius:0;border-bottom-right-radius:0}@media screen and (min-width:30em){.swagger-ui .br0-ns{border-radius:0}.swagger-ui .br1-ns{border-radius:.125rem}.swagger-ui .br2-ns{border-radius:.25rem}.swagger-ui .br3-ns{border-radius:.5rem}.swagger-ui .br4-ns{border-radius:1rem}.swagger-ui .br-100-ns{border-radius:100%}.swagger-ui .br-pill-ns{border-radius:9999px}.swagger-ui .br--bottom-ns{border-top-left-radius:0;border-top-right-radius:0}.swagger-ui .br--top-ns{border-bottom-left-radius:0;border-bottom-right-radius:0}.swagger-ui .br--right-ns{border-top-left-radius:0;border-bottom-left-radius:0}.swagger-ui .br--left-ns{border-top-right-radius:0;border-bottom-right-radius:0}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .br0-m{border-radius:0}.swagger-ui .br1-m{border-radius:.125rem}.swagger-ui .br2-m{border-radius:.25rem}.swagger-ui .br3-m{border-radius:.5rem}.swagger-ui .br4-m{border-radius:1rem}.swagger-ui .br-100-m{border-radius:100%}.swagger-ui .br-pill-m{border-radius:9999px}.swagger-ui .br--bottom-m{border-top-left-radius:0;border-top-right-radius:0}.swagger-ui .br--top-m{border-bottom-left-radius:0;border-bottom-right-radius:0}.swagger-ui .br--right-m{border-top-left-radius:0;border-bottom-left-radius:0}.swagger-ui .br--left-m{border-top-right-radius:0;border-bottom-right-radius:0}}@media screen and (min-width:60em){.swagger-ui .br0-l{border-radius:0}.swagger-ui .br1-l{border-radius:.125rem}.swagger-ui .br2-l{border-radius:.25rem}.swagger-ui .br3-l{border-radius:.5rem}.swagger-ui .br4-l{border-radius:1rem}.swagger-ui .br-100-l{border-radius:100%}.swagger-ui .br-pill-l{border-radius:9999px}.swagger-ui .br--bottom-l{border-top-left-radius:0;border-top-right-radius:0}.swagger-ui .br--top-l{border-bottom-left-radius:0;border-bottom-right-radius:0}.swagger-ui .br--right-l{border-top-left-radius:0;border-bottom-left-radius:0}.swagger-ui .br--left-l{border-top-right-radius:0;border-bottom-right-radius:0}}.swagger-ui .b--dotted{border-style:dotted}.swagger-ui .b--dashed{border-style:dashed}.swagger-ui .b--solid{border-style:solid}.swagger-ui .b--none{border-style:none}@media screen and (min-width:30em){.swagger-ui .b--dotted-ns{border-style:dotted}.swagger-ui .b--dashed-ns{border-style:dashed}.swagger-ui .b--solid-ns{border-style:solid}.swagger-ui .b--none-ns{border-style:none}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .b--dotted-m{border-style:dotted}.swagger-ui .b--dashed-m{border-style:dashed}.swagger-ui .b--solid-m{border-style:solid}.swagger-ui .b--none-m{border-style:none}}@media screen and (min-width:60em){.swagger-ui .b--dotted-l{border-style:dotted}.swagger-ui .b--dashed-l{border-style:dashed}.swagger-ui .b--solid-l{border-style:solid}.swagger-ui .b--none-l{border-style:none}}.swagger-ui .bw0{border-width:0}.swagger-ui .bw1{border-width:.125rem}.swagger-ui .bw2{border-width:.25rem}.swagger-ui .bw3{border-width:.5rem}.swagger-ui .bw4{border-width:1rem}.swagger-ui .bw5{border-width:2rem}.swagger-ui .bt-0{border-top-width:0}.swagger-ui .br-0{border-right-width:0}.swagger-ui .bb-0{border-bottom-width:0}.swagger-ui .bl-0{border-left-width:0}@media screen and (min-width:30em){.swagger-ui .bw0-ns{border-width:0}.swagger-ui .bw1-ns{border-width:.125rem}.swagger-ui .bw2-ns{border-width:.25rem}.swagger-ui .bw3-ns{border-width:.5rem}.swagger-ui .bw4-ns{border-width:1rem}.swagger-ui .bw5-ns{border-width:2rem}.swagger-ui .bt-0-ns{border-top-width:0}.swagger-ui .br-0-ns{border-right-width:0}.swagger-ui .bb-0-ns{border-bottom-width:0}.swagger-ui .bl-0-ns{border-left-width:0}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .bw0-m{border-width:0}.swagger-ui .bw1-m{border-width:.125rem}.swagger-ui .bw2-m{border-width:.25rem}.swagger-ui .bw3-m{border-width:.5rem}.swagger-ui .bw4-m{border-width:1rem}.swagger-ui .bw5-m{border-width:2rem}.swagger-ui .bt-0-m{border-top-width:0}.swagger-ui .br-0-m{border-right-width:0}.swagger-ui .bb-0-m{border-bottom-width:0}.swagger-ui .bl-0-m{border-left-width:0}}@media screen and (min-width:60em){.swagger-ui .bw0-l{border-width:0}.swagger-ui .bw1-l{border-width:.125rem}.swagger-ui .bw2-l{border-width:.25rem}.swagger-ui .bw3-l{border-width:.5rem}.swagger-ui .bw4-l{border-width:1rem}.swagger-ui .bw5-l{border-width:2rem}.swagger-ui .bt-0-l{border-top-width:0}.swagger-ui .br-0-l{border-right-width:0}.swagger-ui .bb-0-l{border-bottom-width:0}.swagger-ui .bl-0-l{border-left-width:0}}.swagger-ui .shadow-1{box-shadow:0 0 4px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-2{box-shadow:0 0 8px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-3{box-shadow:2px 2px 4px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-4{box-shadow:2px 2px 8px 0 rgba(0,0,0,.2)}.swagger-ui .shadow-5{box-shadow:4px 4px 8px 0 rgba(0,0,0,.2)}@media screen and (min-width:30em){.swagger-ui .shadow-1-ns{box-shadow:0 0 4px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-2-ns{box-shadow:0 0 8px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-3-ns{box-shadow:2px 2px 4px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-4-ns{box-shadow:2px 2px 8px 0 rgba(0,0,0,.2)}.swagger-ui .shadow-5-ns{box-shadow:4px 4px 8px 0 rgba(0,0,0,.2)}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .shadow-1-m{box-shadow:0 0 4px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-2-m{box-shadow:0 0 8px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-3-m{box-shadow:2px 2px 4px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-4-m{box-shadow:2px 2px 8px 0 rgba(0,0,0,.2)}.swagger-ui .shadow-5-m{box-shadow:4px 4px 8px 0 rgba(0,0,0,.2)}}@media screen and (min-width:60em){.swagger-ui .shadow-1-l{box-shadow:0 0 4px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-2-l{box-shadow:0 0 8px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-3-l{box-shadow:2px 2px 4px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-4-l{box-shadow:2px 2px 8px 0 rgba(0,0,0,.2)}.swagger-ui .shadow-5-l{box-shadow:4px 4px 8px 0 rgba(0,0,0,.2)}}.swagger-ui .pre{overflow-x:auto;overflow-y:hidden;overflow:scroll}.swagger-ui .top-0{top:0}.swagger-ui .right-0{right:0}.swagger-ui .bottom-0{bottom:0}.swagger-ui .left-0{left:0}.swagger-ui .top-1{top:1rem}.swagger-ui .right-1{right:1rem}.swagger-ui .bottom-1{bottom:1rem}.swagger-ui .left-1{left:1rem}.swagger-ui .top-2{top:2rem}.swagger-ui .right-2{right:2rem}.swagger-ui .bottom-2{bottom:2rem}.swagger-ui .left-2{left:2rem}.swagger-ui .top--1{top:-1rem}.swagger-ui .right--1{right:-1rem}.swagger-ui .bottom--1{bottom:-1rem}.swagger-ui .left--1{left:-1rem}.swagger-ui .top--2{top:-2rem}.swagger-ui .right--2{right:-2rem}.swagger-ui .bottom--2{bottom:-2rem}.swagger-ui .left--2{left:-2rem}.swagger-ui .absolute--fill{top:0;right:0;bottom:0;left:0}@media screen and (min-width:30em){.swagger-ui .top-0-ns{top:0}.swagger-ui .left-0-ns{left:0}.swagger-ui .right-0-ns{right:0}.swagger-ui .bottom-0-ns{bottom:0}.swagger-ui .top-1-ns{top:1rem}.swagger-ui .left-1-ns{left:1rem}.swagger-ui .right-1-ns{right:1rem}.swagger-ui .bottom-1-ns{bottom:1rem}.swagger-ui .top-2-ns{top:2rem}.swagger-ui .left-2-ns{left:2rem}.swagger-ui .right-2-ns{right:2rem}.swagger-ui .bottom-2-ns{bottom:2rem}.swagger-ui .top--1-ns{top:-1rem}.swagger-ui .right--1-ns{right:-1rem}.swagger-ui .bottom--1-ns{bottom:-1rem}.swagger-ui .left--1-ns{left:-1rem}.swagger-ui .top--2-ns{top:-2rem}.swagger-ui .right--2-ns{right:-2rem}.swagger-ui .bottom--2-ns{bottom:-2rem}.swagger-ui .left--2-ns{left:-2rem}.swagger-ui .absolute--fill-ns{top:0;right:0;bottom:0;left:0}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .top-0-m{top:0}.swagger-ui .left-0-m{left:0}.swagger-ui .right-0-m{right:0}.swagger-ui .bottom-0-m{bottom:0}.swagger-ui .top-1-m{top:1rem}.swagger-ui .left-1-m{left:1rem}.swagger-ui .right-1-m{right:1rem}.swagger-ui .bottom-1-m{bottom:1rem}.swagger-ui .top-2-m{top:2rem}.swagger-ui .left-2-m{left:2rem}.swagger-ui .right-2-m{right:2rem}.swagger-ui .bottom-2-m{bottom:2rem}.swagger-ui .top--1-m{top:-1rem}.swagger-ui .right--1-m{right:-1rem}.swagger-ui .bottom--1-m{bottom:-1rem}.swagger-ui .left--1-m{left:-1rem}.swagger-ui .top--2-m{top:-2rem}.swagger-ui .right--2-m{right:-2rem}.swagger-ui .bottom--2-m{bottom:-2rem}.swagger-ui .left--2-m{left:-2rem}.swagger-ui .absolute--fill-m{top:0;right:0;bottom:0;left:0}}@media screen and (min-width:60em){.swagger-ui .top-0-l{top:0}.swagger-ui .left-0-l{left:0}.swagger-ui .right-0-l{right:0}.swagger-ui .bottom-0-l{bottom:0}.swagger-ui .top-1-l{top:1rem}.swagger-ui .left-1-l{left:1rem}.swagger-ui .right-1-l{right:1rem}.swagger-ui .bottom-1-l{bottom:1rem}.swagger-ui .top-2-l{top:2rem}.swagger-ui .left-2-l{left:2rem}.swagger-ui .right-2-l{right:2rem}.swagger-ui .bottom-2-l{bottom:2rem}.swagger-ui .top--1-l{top:-1rem}.swagger-ui .right--1-l{right:-1rem}.swagger-ui .bottom--1-l{bottom:-1rem}.swagger-ui .left--1-l{left:-1rem}.swagger-ui .top--2-l{top:-2rem}.swagger-ui .right--2-l{right:-2rem}.swagger-ui .bottom--2-l{bottom:-2rem}.swagger-ui .left--2-l{left:-2rem}.swagger-ui .absolute--fill-l{top:0;right:0;bottom:0;left:0}}.swagger-ui .cf:after,.swagger-ui .cf:before{content:" ";display:table}.swagger-ui .cf:after{clear:both}.swagger-ui .cf{*zoom:1}.swagger-ui .cl{clear:left}.swagger-ui .cr{clear:right}.swagger-ui .cb{clear:both}.swagger-ui .cn{clear:none}@media screen and (min-width:30em){.swagger-ui .cl-ns{clear:left}.swagger-ui .cr-ns{clear:right}.swagger-ui .cb-ns{clear:both}.swagger-ui .cn-ns{clear:none}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .cl-m{clear:left}.swagger-ui .cr-m{clear:right}.swagger-ui .cb-m{clear:both}.swagger-ui .cn-m{clear:none}}@media screen and (min-width:60em){.swagger-ui .cl-l{clear:left}.swagger-ui .cr-l{clear:right}.swagger-ui .cb-l{clear:both}.swagger-ui .cn-l{clear:none}}.swagger-ui .flex{display:flex}.swagger-ui .inline-flex{display:inline-flex}.swagger-ui .flex-auto{flex:1 1 auto;min-width:0;min-height:0}.swagger-ui .flex-none{flex:none}.swagger-ui .flex-column{flex-direction:column}.swagger-ui .flex-row{flex-direction:row}.swagger-ui .flex-wrap{flex-wrap:wrap}.swagger-ui .flex-nowrap{flex-wrap:nowrap}.swagger-ui .flex-wrap-reverse{flex-wrap:wrap-reverse}.swagger-ui .flex-column-reverse{flex-direction:column-reverse}.swagger-ui .flex-row-reverse{flex-direction:row-reverse}.swagger-ui .items-start{align-items:flex-start}.swagger-ui .items-end{align-items:flex-end}.swagger-ui .items-center{align-items:center}.swagger-ui .items-baseline{align-items:baseline}.swagger-ui .items-stretch{align-items:stretch}.swagger-ui .self-start{align-self:flex-start}.swagger-ui .self-end{align-self:flex-end}.swagger-ui .self-center{align-self:center}.swagger-ui .self-baseline{align-self:baseline}.swagger-ui .self-stretch{align-self:stretch}.swagger-ui .justify-start{justify-content:flex-start}.swagger-ui .justify-end{justify-content:flex-end}.swagger-ui .justify-center{justify-content:center}.swagger-ui .justify-between{justify-content:space-between}.swagger-ui .justify-around{justify-content:space-around}.swagger-ui .content-start{align-content:flex-start}.swagger-ui .content-end{align-content:flex-end}.swagger-ui .content-center{align-content:center}.swagger-ui .content-between{align-content:space-between}.swagger-ui .content-around{align-content:space-around}.swagger-ui .content-stretch{align-content:stretch}.swagger-ui .order-0{order:0}.swagger-ui .order-1{order:1}.swagger-ui .order-2{order:2}.swagger-ui .order-3{order:3}.swagger-ui .order-4{order:4}.swagger-ui .order-5{order:5}.swagger-ui .order-6{order:6}.swagger-ui .order-7{order:7}.swagger-ui .order-8{order:8}.swagger-ui .order-last{order:99999}.swagger-ui .flex-grow-0{flex-grow:0}.swagger-ui .flex-grow-1{flex-grow:1}.swagger-ui .flex-shrink-0{flex-shrink:0}.swagger-ui .flex-shrink-1{flex-shrink:1}@media screen and (min-width:30em){.swagger-ui .flex-ns{display:flex}.swagger-ui .inline-flex-ns{display:inline-flex}.swagger-ui .flex-auto-ns{flex:1 1 auto;min-width:0;min-height:0}.swagger-ui .flex-none-ns{flex:none}.swagger-ui .flex-column-ns{flex-direction:column}.swagger-ui .flex-row-ns{flex-direction:row}.swagger-ui .flex-wrap-ns{flex-wrap:wrap}.swagger-ui .flex-nowrap-ns{flex-wrap:nowrap}.swagger-ui .flex-wrap-reverse-ns{flex-wrap:wrap-reverse}.swagger-ui .flex-column-reverse-ns{flex-direction:column-reverse}.swagger-ui .flex-row-reverse-ns{flex-direction:row-reverse}.swagger-ui .items-start-ns{align-items:flex-start}.swagger-ui .items-end-ns{align-items:flex-end}.swagger-ui .items-center-ns{align-items:center}.swagger-ui .items-baseline-ns{align-items:baseline}.swagger-ui .items-stretch-ns{align-items:stretch}.swagger-ui .self-start-ns{align-self:flex-start}.swagger-ui .self-end-ns{align-self:flex-end}.swagger-ui .self-center-ns{align-self:center}.swagger-ui .self-baseline-ns{align-self:baseline}.swagger-ui .self-stretch-ns{align-self:stretch}.swagger-ui .justify-start-ns{justify-content:flex-start}.swagger-ui .justify-end-ns{justify-content:flex-end}.swagger-ui .justify-center-ns{justify-content:center}.swagger-ui .justify-between-ns{justify-content:space-between}.swagger-ui .justify-around-ns{justify-content:space-around}.swagger-ui .content-start-ns{align-content:flex-start}.swagger-ui .content-end-ns{align-content:flex-end}.swagger-ui .content-center-ns{align-content:center}.swagger-ui .content-between-ns{align-content:space-between}.swagger-ui .content-around-ns{align-content:space-around}.swagger-ui .content-stretch-ns{align-content:stretch}.swagger-ui .order-0-ns{order:0}.swagger-ui .order-1-ns{order:1}.swagger-ui .order-2-ns{order:2}.swagger-ui .order-3-ns{order:3}.swagger-ui .order-4-ns{order:4}.swagger-ui .order-5-ns{order:5}.swagger-ui .order-6-ns{order:6}.swagger-ui .order-7-ns{order:7}.swagger-ui .order-8-ns{order:8}.swagger-ui .order-last-ns{order:99999}.swagger-ui .flex-grow-0-ns{flex-grow:0}.swagger-ui .flex-grow-1-ns{flex-grow:1}.swagger-ui .flex-shrink-0-ns{flex-shrink:0}.swagger-ui .flex-shrink-1-ns{flex-shrink:1}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .flex-m{display:flex}.swagger-ui .inline-flex-m{display:inline-flex}.swagger-ui .flex-auto-m{flex:1 1 auto;min-width:0;min-height:0}.swagger-ui .flex-none-m{flex:none}.swagger-ui .flex-column-m{flex-direction:column}.swagger-ui .flex-row-m{flex-direction:row}.swagger-ui .flex-wrap-m{flex-wrap:wrap}.swagger-ui .flex-nowrap-m{flex-wrap:nowrap}.swagger-ui .flex-wrap-reverse-m{flex-wrap:wrap-reverse}.swagger-ui .flex-column-reverse-m{flex-direction:column-reverse}.swagger-ui .flex-row-reverse-m{flex-direction:row-reverse}.swagger-ui .items-start-m{align-items:flex-start}.swagger-ui .items-end-m{align-items:flex-end}.swagger-ui .items-center-m{align-items:center}.swagger-ui .items-baseline-m{align-items:baseline}.swagger-ui .items-stretch-m{align-items:stretch}.swagger-ui .self-start-m{align-self:flex-start}.swagger-ui .self-end-m{align-self:flex-end}.swagger-ui .self-center-m{align-self:center}.swagger-ui .self-baseline-m{align-self:baseline}.swagger-ui .self-stretch-m{align-self:stretch}.swagger-ui .justify-start-m{justify-content:flex-start}.swagger-ui .justify-end-m{justify-content:flex-end}.swagger-ui .justify-center-m{justify-content:center}.swagger-ui .justify-between-m{justify-content:space-between}.swagger-ui .justify-around-m{justify-content:space-around}.swagger-ui .content-start-m{align-content:flex-start}.swagger-ui .content-end-m{align-content:flex-end}.swagger-ui .content-center-m{align-content:center}.swagger-ui .content-between-m{align-content:space-between}.swagger-ui .content-around-m{align-content:space-around}.swagger-ui .content-stretch-m{align-content:stretch}.swagger-ui .order-0-m{order:0}.swagger-ui .order-1-m{order:1}.swagger-ui .order-2-m{order:2}.swagger-ui .order-3-m{order:3}.swagger-ui .order-4-m{order:4}.swagger-ui .order-5-m{order:5}.swagger-ui .order-6-m{order:6}.swagger-ui .order-7-m{order:7}.swagger-ui .order-8-m{order:8}.swagger-ui .order-last-m{order:99999}.swagger-ui .flex-grow-0-m{flex-grow:0}.swagger-ui .flex-grow-1-m{flex-grow:1}.swagger-ui .flex-shrink-0-m{flex-shrink:0}.swagger-ui .flex-shrink-1-m{flex-shrink:1}}@media screen and (min-width:60em){.swagger-ui .flex-l{display:flex}.swagger-ui .inline-flex-l{display:inline-flex}.swagger-ui .flex-auto-l{flex:1 1 auto;min-width:0;min-height:0}.swagger-ui .flex-none-l{flex:none}.swagger-ui .flex-column-l{flex-direction:column}.swagger-ui .flex-row-l{flex-direction:row}.swagger-ui .flex-wrap-l{flex-wrap:wrap}.swagger-ui .flex-nowrap-l{flex-wrap:nowrap}.swagger-ui .flex-wrap-reverse-l{flex-wrap:wrap-reverse}.swagger-ui .flex-column-reverse-l{flex-direction:column-reverse}.swagger-ui .flex-row-reverse-l{flex-direction:row-reverse}.swagger-ui .items-start-l{align-items:flex-start}.swagger-ui .items-end-l{align-items:flex-end}.swagger-ui .items-center-l{align-items:center}.swagger-ui .items-baseline-l{align-items:baseline}.swagger-ui .items-stretch-l{align-items:stretch}.swagger-ui .self-start-l{align-self:flex-start}.swagger-ui .self-end-l{align-self:flex-end}.swagger-ui .self-center-l{align-self:center}.swagger-ui .self-baseline-l{align-self:baseline}.swagger-ui .self-stretch-l{align-self:stretch}.swagger-ui .justify-start-l{justify-content:flex-start}.swagger-ui .justify-end-l{justify-content:flex-end}.swagger-ui .justify-center-l{justify-content:center}.swagger-ui .justify-between-l{justify-content:space-between}.swagger-ui .justify-around-l{justify-content:space-around}.swagger-ui .content-start-l{align-content:flex-start}.swagger-ui .content-end-l{align-content:flex-end}.swagger-ui .content-center-l{align-content:center}.swagger-ui .content-between-l{align-content:space-between}.swagger-ui .content-around-l{align-content:space-around}.swagger-ui .content-stretch-l{align-content:stretch}.swagger-ui .order-0-l{order:0}.swagger-ui .order-1-l{order:1}.swagger-ui .order-2-l{order:2}.swagger-ui .order-3-l{order:3}.swagger-ui .order-4-l{order:4}.swagger-ui .order-5-l{order:5}.swagger-ui .order-6-l{order:6}.swagger-ui .order-7-l{order:7}.swagger-ui .order-8-l{order:8}.swagger-ui .order-last-l{order:99999}.swagger-ui .flex-grow-0-l{flex-grow:0}.swagger-ui .flex-grow-1-l{flex-grow:1}.swagger-ui .flex-shrink-0-l{flex-shrink:0}.swagger-ui .flex-shrink-1-l{flex-shrink:1}}.swagger-ui .dn{display:none}.swagger-ui .di{display:inline}.swagger-ui .db{display:block}.swagger-ui .dib{display:inline-block}.swagger-ui .dit{display:inline-table}.swagger-ui .dt{display:table}.swagger-ui .dtc{display:table-cell}.swagger-ui .dt-row{display:table-row}.swagger-ui .dt-row-group{display:table-row-group}.swagger-ui .dt-column{display:table-column}.swagger-ui .dt-column-group{display:table-column-group}.swagger-ui .dt--fixed{table-layout:fixed;width:100%}@media screen and (min-width:30em){.swagger-ui .dn-ns{display:none}.swagger-ui .di-ns{display:inline}.swagger-ui .db-ns{display:block}.swagger-ui .dib-ns{display:inline-block}.swagger-ui .dit-ns{display:inline-table}.swagger-ui .dt-ns{display:table}.swagger-ui .dtc-ns{display:table-cell}.swagger-ui .dt-row-ns{display:table-row}.swagger-ui .dt-row-group-ns{display:table-row-group}.swagger-ui .dt-column-ns{display:table-column}.swagger-ui .dt-column-group-ns{display:table-column-group}.swagger-ui .dt--fixed-ns{table-layout:fixed;width:100%}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .dn-m{display:none}.swagger-ui .di-m{display:inline}.swagger-ui .db-m{display:block}.swagger-ui .dib-m{display:inline-block}.swagger-ui .dit-m{display:inline-table}.swagger-ui .dt-m{display:table}.swagger-ui .dtc-m{display:table-cell}.swagger-ui .dt-row-m{display:table-row}.swagger-ui .dt-row-group-m{display:table-row-group}.swagger-ui .dt-column-m{display:table-column}.swagger-ui .dt-column-group-m{display:table-column-group}.swagger-ui .dt--fixed-m{table-layout:fixed;width:100%}}@media screen and (min-width:60em){.swagger-ui .dn-l{display:none}.swagger-ui .di-l{display:inline}.swagger-ui .db-l{display:block}.swagger-ui .dib-l{display:inline-block}.swagger-ui .dit-l{display:inline-table}.swagger-ui .dt-l{display:table}.swagger-ui .dtc-l{display:table-cell}.swagger-ui .dt-row-l{display:table-row}.swagger-ui .dt-row-group-l{display:table-row-group}.swagger-ui .dt-column-l{display:table-column}.swagger-ui .dt-column-group-l{display:table-column-group}.swagger-ui .dt--fixed-l{table-layout:fixed;width:100%}}.swagger-ui .fl{float:left;_display:inline}.swagger-ui .fr{float:right;_display:inline}.swagger-ui .fn{float:none}@media screen and (min-width:30em){.swagger-ui .fl-ns{float:left;_display:inline}.swagger-ui .fr-ns{float:right;_display:inline}.swagger-ui .fn-ns{float:none}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .fl-m{float:left;_display:inline}.swagger-ui .fr-m{float:right;_display:inline}.swagger-ui .fn-m{float:none}}@media screen and (min-width:60em){.swagger-ui .fl-l{float:left;_display:inline}.swagger-ui .fr-l{float:right;_display:inline}.swagger-ui .fn-l{float:none}}.swagger-ui .sans-serif{font-family:-apple-system,BlinkMacSystemFont,avenir next,avenir,helvetica,helvetica neue,ubuntu,roboto,noto,segoe ui,arial,sans-serif}.swagger-ui .serif{font-family:georgia,serif}.swagger-ui .system-sans-serif{font-family:sans-serif}.swagger-ui .system-serif{font-family:serif}.swagger-ui .code,.swagger-ui code{font-family:Consolas,monaco,monospace}.swagger-ui .courier{font-family:Courier Next,courier,monospace}.swagger-ui .helvetica{font-family:helvetica neue,helvetica,sans-serif}.swagger-ui .avenir{font-family:avenir next,avenir,sans-serif}.swagger-ui .athelas{font-family:athelas,georgia,serif}.swagger-ui .georgia{font-family:georgia,serif}.swagger-ui .times{font-family:times,serif}.swagger-ui .bodoni{font-family:Bodoni MT,serif}.swagger-ui .calisto{font-family:Calisto MT,serif}.swagger-ui .garamond{font-family:garamond,serif}.swagger-ui .baskerville{font-family:baskerville,serif}.swagger-ui .i{font-style:italic}.swagger-ui .fs-normal{font-style:normal}@media screen and (min-width:30em){.swagger-ui .i-ns{font-style:italic}.swagger-ui .fs-normal-ns{font-style:normal}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .i-m{font-style:italic}.swagger-ui .fs-normal-m{font-style:normal}}@media screen and (min-width:60em){.swagger-ui .i-l{font-style:italic}.swagger-ui .fs-normal-l{font-style:normal}}.swagger-ui .normal{font-weight:400}.swagger-ui .b{font-weight:700}.swagger-ui .fw1{font-weight:100}.swagger-ui .fw2{font-weight:200}.swagger-ui .fw3{font-weight:300}.swagger-ui .fw4{font-weight:400}.swagger-ui .fw5{font-weight:500}.swagger-ui .fw6{font-weight:600}.swagger-ui .fw7{font-weight:700}.swagger-ui .fw8{font-weight:800}.swagger-ui .fw9{font-weight:900}@media screen and (min-width:30em){.swagger-ui .normal-ns{font-weight:400}.swagger-ui .b-ns{font-weight:700}.swagger-ui .fw1-ns{font-weight:100}.swagger-ui .fw2-ns{font-weight:200}.swagger-ui .fw3-ns{font-weight:300}.swagger-ui .fw4-ns{font-weight:400}.swagger-ui .fw5-ns{font-weight:500}.swagger-ui .fw6-ns{font-weight:600}.swagger-ui .fw7-ns{font-weight:700}.swagger-ui .fw8-ns{font-weight:800}.swagger-ui .fw9-ns{font-weight:900}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .normal-m{font-weight:400}.swagger-ui .b-m{font-weight:700}.swagger-ui .fw1-m{font-weight:100}.swagger-ui .fw2-m{font-weight:200}.swagger-ui .fw3-m{font-weight:300}.swagger-ui .fw4-m{font-weight:400}.swagger-ui .fw5-m{font-weight:500}.swagger-ui .fw6-m{font-weight:600}.swagger-ui .fw7-m{font-weight:700}.swagger-ui .fw8-m{font-weight:800}.swagger-ui .fw9-m{font-weight:900}}@media screen and (min-width:60em){.swagger-ui .normal-l{font-weight:400}.swagger-ui .b-l{font-weight:700}.swagger-ui .fw1-l{font-weight:100}.swagger-ui .fw2-l{font-weight:200}.swagger-ui .fw3-l{font-weight:300}.swagger-ui .fw4-l{font-weight:400}.swagger-ui .fw5-l{font-weight:500}.swagger-ui .fw6-l{font-weight:600}.swagger-ui .fw7-l{font-weight:700}.swagger-ui .fw8-l{font-weight:800}.swagger-ui .fw9-l{font-weight:900}}.swagger-ui .input-reset{-webkit-appearance:none;-moz-appearance:none}.swagger-ui .button-reset::-moz-focus-inner,.swagger-ui .input-reset::-moz-focus-inner{border:0;padding:0}.swagger-ui .h1{height:1rem}.swagger-ui .h2{height:2rem}.swagger-ui .h3{height:4rem}.swagger-ui .h4{height:8rem}.swagger-ui .h5{height:16rem}.swagger-ui .h-25{height:25%}.swagger-ui .h-50{height:50%}.swagger-ui .h-75{height:75%}.swagger-ui .h-100{height:100%}.swagger-ui .min-h-100{min-height:100%}.swagger-ui .vh-25{height:25vh}.swagger-ui .vh-50{height:50vh}.swagger-ui .vh-75{height:75vh}.swagger-ui .vh-100{height:100vh}.swagger-ui .min-vh-100{min-height:100vh}.swagger-ui .h-auto{height:auto}.swagger-ui .h-inherit{height:inherit}@media screen and (min-width:30em){.swagger-ui .h1-ns{height:1rem}.swagger-ui .h2-ns{height:2rem}.swagger-ui .h3-ns{height:4rem}.swagger-ui .h4-ns{height:8rem}.swagger-ui .h5-ns{height:16rem}.swagger-ui .h-25-ns{height:25%}.swagger-ui .h-50-ns{height:50%}.swagger-ui .h-75-ns{height:75%}.swagger-ui .h-100-ns{height:100%}.swagger-ui .min-h-100-ns{min-height:100%}.swagger-ui .vh-25-ns{height:25vh}.swagger-ui .vh-50-ns{height:50vh}.swagger-ui .vh-75-ns{height:75vh}.swagger-ui .vh-100-ns{height:100vh}.swagger-ui .min-vh-100-ns{min-height:100vh}.swagger-ui .h-auto-ns{height:auto}.swagger-ui .h-inherit-ns{height:inherit}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .h1-m{height:1rem}.swagger-ui .h2-m{height:2rem}.swagger-ui .h3-m{height:4rem}.swagger-ui .h4-m{height:8rem}.swagger-ui .h5-m{height:16rem}.swagger-ui .h-25-m{height:25%}.swagger-ui .h-50-m{height:50%}.swagger-ui .h-75-m{height:75%}.swagger-ui .h-100-m{height:100%}.swagger-ui .min-h-100-m{min-height:100%}.swagger-ui .vh-25-m{height:25vh}.swagger-ui .vh-50-m{height:50vh}.swagger-ui .vh-75-m{height:75vh}.swagger-ui .vh-100-m{height:100vh}.swagger-ui .min-vh-100-m{min-height:100vh}.swagger-ui .h-auto-m{height:auto}.swagger-ui .h-inherit-m{height:inherit}}@media screen and (min-width:60em){.swagger-ui .h1-l{height:1rem}.swagger-ui .h2-l{height:2rem}.swagger-ui .h3-l{height:4rem}.swagger-ui .h4-l{height:8rem}.swagger-ui .h5-l{height:16rem}.swagger-ui .h-25-l{height:25%}.swagger-ui .h-50-l{height:50%}.swagger-ui .h-75-l{height:75%}.swagger-ui .h-100-l{height:100%}.swagger-ui .min-h-100-l{min-height:100%}.swagger-ui .vh-25-l{height:25vh}.swagger-ui .vh-50-l{height:50vh}.swagger-ui .vh-75-l{height:75vh}.swagger-ui .vh-100-l{height:100vh}.swagger-ui .min-vh-100-l{min-height:100vh}.swagger-ui .h-auto-l{height:auto}.swagger-ui .h-inherit-l{height:inherit}}.swagger-ui .tracked{letter-spacing:.1em}.swagger-ui .tracked-tight{letter-spacing:-.05em}.swagger-ui .tracked-mega{letter-spacing:.25em}@media screen and (min-width:30em){.swagger-ui .tracked-ns{letter-spacing:.1em}.swagger-ui .tracked-tight-ns{letter-spacing:-.05em}.swagger-ui .tracked-mega-ns{letter-spacing:.25em}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .tracked-m{letter-spacing:.1em}.swagger-ui .tracked-tight-m{letter-spacing:-.05em}.swagger-ui .tracked-mega-m{letter-spacing:.25em}}@media screen and (min-width:60em){.swagger-ui .tracked-l{letter-spacing:.1em}.swagger-ui .tracked-tight-l{letter-spacing:-.05em}.swagger-ui .tracked-mega-l{letter-spacing:.25em}}.swagger-ui .lh-solid{line-height:1}.swagger-ui .lh-title{line-height:1.25}.swagger-ui .lh-copy{line-height:1.5}@media screen and (min-width:30em){.swagger-ui .lh-solid-ns{line-height:1}.swagger-ui .lh-title-ns{line-height:1.25}.swagger-ui .lh-copy-ns{line-height:1.5}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .lh-solid-m{line-height:1}.swagger-ui .lh-title-m{line-height:1.25}.swagger-ui .lh-copy-m{line-height:1.5}}@media screen and (min-width:60em){.swagger-ui .lh-solid-l{line-height:1}.swagger-ui .lh-title-l{line-height:1.25}.swagger-ui .lh-copy-l{line-height:1.5}}.swagger-ui .link{text-decoration:none}.swagger-ui .link,.swagger-ui .link:link,.swagger-ui .link:visited{transition:color .15s ease-in}.swagger-ui .link:hover{transition:color .15s ease-in}.swagger-ui .link:active{transition:color .15s ease-in}.swagger-ui .link:focus{transition:color .15s ease-in;outline:1px dotted currentColor}.swagger-ui .list{list-style-type:none}.swagger-ui .mw-100{max-width:100%}.swagger-ui .mw1{max-width:1rem}.swagger-ui .mw2{max-width:2rem}.swagger-ui .mw3{max-width:4rem}.swagger-ui .mw4{max-width:8rem}.swagger-ui .mw5{max-width:16rem}.swagger-ui .mw6{max-width:32rem}.swagger-ui .mw7{max-width:48rem}.swagger-ui .mw8{max-width:64rem}.swagger-ui .mw9{max-width:96rem}.swagger-ui .mw-none{max-width:none}@media screen and (min-width:30em){.swagger-ui .mw-100-ns{max-width:100%}.swagger-ui .mw1-ns{max-width:1rem}.swagger-ui .mw2-ns{max-width:2rem}.swagger-ui .mw3-ns{max-width:4rem}.swagger-ui .mw4-ns{max-width:8rem}.swagger-ui .mw5-ns{max-width:16rem}.swagger-ui .mw6-ns{max-width:32rem}.swagger-ui .mw7-ns{max-width:48rem}.swagger-ui .mw8-ns{max-width:64rem}.swagger-ui .mw9-ns{max-width:96rem}.swagger-ui .mw-none-ns{max-width:none}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .mw-100-m{max-width:100%}.swagger-ui .mw1-m{max-width:1rem}.swagger-ui .mw2-m{max-width:2rem}.swagger-ui .mw3-m{max-width:4rem}.swagger-ui .mw4-m{max-width:8rem}.swagger-ui .mw5-m{max-width:16rem}.swagger-ui .mw6-m{max-width:32rem}.swagger-ui .mw7-m{max-width:48rem}.swagger-ui .mw8-m{max-width:64rem}.swagger-ui .mw9-m{max-width:96rem}.swagger-ui .mw-none-m{max-width:none}}@media screen and (min-width:60em){.swagger-ui .mw-100-l{max-width:100%}.swagger-ui .mw1-l{max-width:1rem}.swagger-ui .mw2-l{max-width:2rem}.swagger-ui .mw3-l{max-width:4rem}.swagger-ui .mw4-l{max-width:8rem}.swagger-ui .mw5-l{max-width:16rem}.swagger-ui .mw6-l{max-width:32rem}.swagger-ui .mw7-l{max-width:48rem}.swagger-ui .mw8-l{max-width:64rem}.swagger-ui .mw9-l{max-width:96rem}.swagger-ui .mw-none-l{max-width:none}}.swagger-ui .w1{width:1rem}.swagger-ui .w2{width:2rem}.swagger-ui .w3{width:4rem}.swagger-ui .w4{width:8rem}.swagger-ui .w5{width:16rem}.swagger-ui .w-10{width:10%}.swagger-ui .w-20{width:20%}.swagger-ui .w-25{width:25%}.swagger-ui .w-30{width:30%}.swagger-ui .w-33{width:33%}.swagger-ui .w-34{width:34%}.swagger-ui .w-40{width:40%}.swagger-ui .w-50{width:50%}.swagger-ui .w-60{width:60%}.swagger-ui .w-70{width:70%}.swagger-ui .w-75{width:75%}.swagger-ui .w-80{width:80%}.swagger-ui .w-90{width:90%}.swagger-ui .w-100{width:100%}.swagger-ui .w-third{width:33.33333%}.swagger-ui .w-two-thirds{width:66.66667%}.swagger-ui .w-auto{width:auto}@media screen and (min-width:30em){.swagger-ui .w1-ns{width:1rem}.swagger-ui .w2-ns{width:2rem}.swagger-ui .w3-ns{width:4rem}.swagger-ui .w4-ns{width:8rem}.swagger-ui .w5-ns{width:16rem}.swagger-ui .w-10-ns{width:10%}.swagger-ui .w-20-ns{width:20%}.swagger-ui .w-25-ns{width:25%}.swagger-ui .w-30-ns{width:30%}.swagger-ui .w-33-ns{width:33%}.swagger-ui .w-34-ns{width:34%}.swagger-ui .w-40-ns{width:40%}.swagger-ui .w-50-ns{width:50%}.swagger-ui .w-60-ns{width:60%}.swagger-ui .w-70-ns{width:70%}.swagger-ui .w-75-ns{width:75%}.swagger-ui .w-80-ns{width:80%}.swagger-ui .w-90-ns{width:90%}.swagger-ui .w-100-ns{width:100%}.swagger-ui .w-third-ns{width:33.33333%}.swagger-ui .w-two-thirds-ns{width:66.66667%}.swagger-ui .w-auto-ns{width:auto}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .w1-m{width:1rem}.swagger-ui .w2-m{width:2rem}.swagger-ui .w3-m{width:4rem}.swagger-ui .w4-m{width:8rem}.swagger-ui .w5-m{width:16rem}.swagger-ui .w-10-m{width:10%}.swagger-ui .w-20-m{width:20%}.swagger-ui .w-25-m{width:25%}.swagger-ui .w-30-m{width:30%}.swagger-ui .w-33-m{width:33%}.swagger-ui .w-34-m{width:34%}.swagger-ui .w-40-m{width:40%}.swagger-ui .w-50-m{width:50%}.swagger-ui .w-60-m{width:60%}.swagger-ui .w-70-m{width:70%}.swagger-ui .w-75-m{width:75%}.swagger-ui .w-80-m{width:80%}.swagger-ui .w-90-m{width:90%}.swagger-ui .w-100-m{width:100%}.swagger-ui .w-third-m{width:33.33333%}.swagger-ui .w-two-thirds-m{width:66.66667%}.swagger-ui .w-auto-m{width:auto}}@media screen and (min-width:60em){.swagger-ui .w1-l{width:1rem}.swagger-ui .w2-l{width:2rem}.swagger-ui .w3-l{width:4rem}.swagger-ui .w4-l{width:8rem}.swagger-ui .w5-l{width:16rem}.swagger-ui .w-10-l{width:10%}.swagger-ui .w-20-l{width:20%}.swagger-ui .w-25-l{width:25%}.swagger-ui .w-30-l{width:30%}.swagger-ui .w-33-l{width:33%}.swagger-ui .w-34-l{width:34%}.swagger-ui .w-40-l{width:40%}.swagger-ui .w-50-l{width:50%}.swagger-ui .w-60-l{width:60%}.swagger-ui .w-70-l{width:70%}.swagger-ui .w-75-l{width:75%}.swagger-ui .w-80-l{width:80%}.swagger-ui .w-90-l{width:90%}.swagger-ui .w-100-l{width:100%}.swagger-ui .w-third-l{width:33.33333%}.swagger-ui .w-two-thirds-l{width:66.66667%}.swagger-ui .w-auto-l{width:auto}}.swagger-ui .overflow-visible{overflow:visible}.swagger-ui .overflow-hidden{overflow:hidden}.swagger-ui .overflow-scroll{overflow:scroll}.swagger-ui .overflow-auto{overflow:auto}.swagger-ui .overflow-x-visible{overflow-x:visible}.swagger-ui .overflow-x-hidden{overflow-x:hidden}.swagger-ui .overflow-x-scroll{overflow-x:scroll}.swagger-ui .overflow-x-auto{overflow-x:auto}.swagger-ui .overflow-y-visible{overflow-y:visible}.swagger-ui .overflow-y-hidden{overflow-y:hidden}.swagger-ui .overflow-y-scroll{overflow-y:scroll}.swagger-ui .overflow-y-auto{overflow-y:auto}@media screen and (min-width:30em){.swagger-ui .overflow-visible-ns{overflow:visible}.swagger-ui .overflow-hidden-ns{overflow:hidden}.swagger-ui .overflow-scroll-ns{overflow:scroll}.swagger-ui .overflow-auto-ns{overflow:auto}.swagger-ui .overflow-x-visible-ns{overflow-x:visible}.swagger-ui .overflow-x-hidden-ns{overflow-x:hidden}.swagger-ui .overflow-x-scroll-ns{overflow-x:scroll}.swagger-ui .overflow-x-auto-ns{overflow-x:auto}.swagger-ui .overflow-y-visible-ns{overflow-y:visible}.swagger-ui .overflow-y-hidden-ns{overflow-y:hidden}.swagger-ui .overflow-y-scroll-ns{overflow-y:scroll}.swagger-ui .overflow-y-auto-ns{overflow-y:auto}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .overflow-visible-m{overflow:visible}.swagger-ui .overflow-hidden-m{overflow:hidden}.swagger-ui .overflow-scroll-m{overflow:scroll}.swagger-ui .overflow-auto-m{overflow:auto}.swagger-ui .overflow-x-visible-m{overflow-x:visible}.swagger-ui .overflow-x-hidden-m{overflow-x:hidden}.swagger-ui .overflow-x-scroll-m{overflow-x:scroll}.swagger-ui .overflow-x-auto-m{overflow-x:auto}.swagger-ui .overflow-y-visible-m{overflow-y:visible}.swagger-ui .overflow-y-hidden-m{overflow-y:hidden}.swagger-ui .overflow-y-scroll-m{overflow-y:scroll}.swagger-ui .overflow-y-auto-m{overflow-y:auto}}@media screen and (min-width:60em){.swagger-ui .overflow-visible-l{overflow:visible}.swagger-ui .overflow-hidden-l{overflow:hidden}.swagger-ui .overflow-scroll-l{overflow:scroll}.swagger-ui .overflow-auto-l{overflow:auto}.swagger-ui .overflow-x-visible-l{overflow-x:visible}.swagger-ui .overflow-x-hidden-l{overflow-x:hidden}.swagger-ui .overflow-x-scroll-l{overflow-x:scroll}.swagger-ui .overflow-x-auto-l{overflow-x:auto}.swagger-ui .overflow-y-visible-l{overflow-y:visible}.swagger-ui .overflow-y-hidden-l{overflow-y:hidden}.swagger-ui .overflow-y-scroll-l{overflow-y:scroll}.swagger-ui .overflow-y-auto-l{overflow-y:auto}}.swagger-ui .static{position:static}.swagger-ui .relative{position:relative}.swagger-ui .absolute{position:absolute}.swagger-ui .fixed{position:fixed}@media screen and (min-width:30em){.swagger-ui .static-ns{position:static}.swagger-ui .relative-ns{position:relative}.swagger-ui .absolute-ns{position:absolute}.swagger-ui .fixed-ns{position:fixed}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .static-m{position:static}.swagger-ui .relative-m{position:relative}.swagger-ui .absolute-m{position:absolute}.swagger-ui .fixed-m{position:fixed}}@media screen and (min-width:60em){.swagger-ui .static-l{position:static}.swagger-ui .relative-l{position:relative}.swagger-ui .absolute-l{position:absolute}.swagger-ui .fixed-l{position:fixed}}.swagger-ui .o-100{opacity:1}.swagger-ui .o-90{opacity:.9}.swagger-ui .o-80{opacity:.8}.swagger-ui .o-70{opacity:.7}.swagger-ui .o-60{opacity:.6}.swagger-ui .o-50{opacity:.5}.swagger-ui .o-40{opacity:.4}.swagger-ui .o-30{opacity:.3}.swagger-ui .o-20{opacity:.2}.swagger-ui .o-10{opacity:.1}.swagger-ui .o-05{opacity:.05}.swagger-ui .o-025{opacity:.025}.swagger-ui .o-0{opacity:0}.swagger-ui .rotate-45{-webkit-transform:rotate(45deg);transform:rotate(45deg)}.swagger-ui .rotate-90{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.swagger-ui .rotate-135{-webkit-transform:rotate(135deg);transform:rotate(135deg)}.swagger-ui .rotate-180{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.swagger-ui .rotate-225{-webkit-transform:rotate(225deg);transform:rotate(225deg)}.swagger-ui .rotate-270{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.swagger-ui .rotate-315{-webkit-transform:rotate(315deg);transform:rotate(315deg)}@media screen and (min-width:30em){.swagger-ui .rotate-45-ns{-webkit-transform:rotate(45deg);transform:rotate(45deg)}.swagger-ui .rotate-90-ns{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.swagger-ui .rotate-135-ns{-webkit-transform:rotate(135deg);transform:rotate(135deg)}.swagger-ui .rotate-180-ns{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.swagger-ui .rotate-225-ns{-webkit-transform:rotate(225deg);transform:rotate(225deg)}.swagger-ui .rotate-270-ns{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.swagger-ui .rotate-315-ns{-webkit-transform:rotate(315deg);transform:rotate(315deg)}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .rotate-45-m{-webkit-transform:rotate(45deg);transform:rotate(45deg)}.swagger-ui .rotate-90-m{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.swagger-ui .rotate-135-m{-webkit-transform:rotate(135deg);transform:rotate(135deg)}.swagger-ui .rotate-180-m{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.swagger-ui .rotate-225-m{-webkit-transform:rotate(225deg);transform:rotate(225deg)}.swagger-ui .rotate-270-m{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.swagger-ui .rotate-315-m{-webkit-transform:rotate(315deg);transform:rotate(315deg)}}@media screen and (min-width:60em){.swagger-ui .rotate-45-l{-webkit-transform:rotate(45deg);transform:rotate(45deg)}.swagger-ui .rotate-90-l{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.swagger-ui .rotate-135-l{-webkit-transform:rotate(135deg);transform:rotate(135deg)}.swagger-ui .rotate-180-l{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.swagger-ui .rotate-225-l{-webkit-transform:rotate(225deg);transform:rotate(225deg)}.swagger-ui .rotate-270-l{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.swagger-ui .rotate-315-l{-webkit-transform:rotate(315deg);transform:rotate(315deg)}}.swagger-ui .black-90{color:rgba(0,0,0,.9)}.swagger-ui .black-80{color:rgba(0,0,0,.8)}.swagger-ui .black-70{color:rgba(0,0,0,.7)}.swagger-ui .black-60{color:rgba(0,0,0,.6)}.swagger-ui .black-50{color:rgba(0,0,0,.5)}.swagger-ui .black-40{color:rgba(0,0,0,.4)}.swagger-ui .black-30{color:rgba(0,0,0,.3)}.swagger-ui .black-20{color:rgba(0,0,0,.2)}.swagger-ui .black-10{color:rgba(0,0,0,.1)}.swagger-ui .black-05{color:rgba(0,0,0,.05)}.swagger-ui .white-90{color:hsla(0,0%,100%,.9)}.swagger-ui .white-80{color:hsla(0,0%,100%,.8)}.swagger-ui .white-70{color:hsla(0,0%,100%,.7)}.swagger-ui .white-60{color:hsla(0,0%,100%,.6)}.swagger-ui .white-50{color:hsla(0,0%,100%,.5)}.swagger-ui .white-40{color:hsla(0,0%,100%,.4)}.swagger-ui .white-30{color:hsla(0,0%,100%,.3)}.swagger-ui .white-20{color:hsla(0,0%,100%,.2)}.swagger-ui .white-10{color:hsla(0,0%,100%,.1)}.swagger-ui .black{color:#000}.swagger-ui .near-black{color:#111}.swagger-ui .dark-gray{color:#333}.swagger-ui .mid-gray{color:#555}.swagger-ui .gray{color:#777}.swagger-ui .silver{color:#999}.swagger-ui .light-silver{color:#aaa}.swagger-ui .moon-gray{color:#ccc}.swagger-ui .light-gray{color:#eee}.swagger-ui .near-white{color:#f4f4f4}.swagger-ui .white{color:#fff}.swagger-ui .dark-red{color:#e7040f}.swagger-ui .red{color:#ff4136}.swagger-ui .light-red{color:#ff725c}.swagger-ui .orange{color:#ff6300}.swagger-ui .gold{color:#ffb700}.swagger-ui .yellow{color:gold}.swagger-ui .light-yellow{color:#fbf1a9}.swagger-ui .purple{color:#5e2ca5}.swagger-ui .light-purple{color:#a463f2}.swagger-ui .dark-pink{color:#d5008f}.swagger-ui .hot-pink{color:#ff41b4}.swagger-ui .pink{color:#ff80cc}.swagger-ui .light-pink{color:#ffa3d7}.swagger-ui .dark-green{color:#137752}.swagger-ui .green{color:#19a974}.swagger-ui .light-green{color:#9eebcf}.swagger-ui .navy{color:#001b44}.swagger-ui .dark-blue{color:#00449e}.swagger-ui .blue{color:#357edd}.swagger-ui .light-blue{color:#96ccff}.swagger-ui .lightest-blue{color:#cdecff}.swagger-ui .washed-blue{color:#f6fffe}.swagger-ui .washed-green{color:#e8fdf5}.swagger-ui .washed-yellow{color:#fffceb}.swagger-ui .washed-red{color:#ffdfdf}.swagger-ui .color-inherit{color:inherit}.swagger-ui .bg-black-90{background-color:rgba(0,0,0,.9)}.swagger-ui .bg-black-80{background-color:rgba(0,0,0,.8)}.swagger-ui .bg-black-70{background-color:rgba(0,0,0,.7)}.swagger-ui .bg-black-60{background-color:rgba(0,0,0,.6)}.swagger-ui .bg-black-50{background-color:rgba(0,0,0,.5)}.swagger-ui .bg-black-40{background-color:rgba(0,0,0,.4)}.swagger-ui .bg-black-30{background-color:rgba(0,0,0,.3)}.swagger-ui .bg-black-20{background-color:rgba(0,0,0,.2)}.swagger-ui .bg-black-10{background-color:rgba(0,0,0,.1)}.swagger-ui .bg-black-05{background-color:rgba(0,0,0,.05)}.swagger-ui .bg-white-90{background-color:hsla(0,0%,100%,.9)}.swagger-ui .bg-white-80{background-color:hsla(0,0%,100%,.8)}.swagger-ui .bg-white-70{background-color:hsla(0,0%,100%,.7)}.swagger-ui .bg-white-60{background-color:hsla(0,0%,100%,.6)}.swagger-ui .bg-white-50{background-color:hsla(0,0%,100%,.5)}.swagger-ui .bg-white-40{background-color:hsla(0,0%,100%,.4)}.swagger-ui .bg-white-30{background-color:hsla(0,0%,100%,.3)}.swagger-ui .bg-white-20{background-color:hsla(0,0%,100%,.2)}.swagger-ui .bg-white-10{background-color:hsla(0,0%,100%,.1)}.swagger-ui .bg-black{background-color:#000}.swagger-ui .bg-near-black{background-color:#111}.swagger-ui .bg-dark-gray{background-color:#333}.swagger-ui .bg-mid-gray{background-color:#555}.swagger-ui .bg-gray{background-color:#777}.swagger-ui .bg-silver{background-color:#999}.swagger-ui .bg-light-silver{background-color:#aaa}.swagger-ui .bg-moon-gray{background-color:#ccc}.swagger-ui .bg-light-gray{background-color:#eee}.swagger-ui .bg-near-white{background-color:#f4f4f4}.swagger-ui .bg-white{background-color:#fff}.swagger-ui .bg-transparent{background-color:transparent}.swagger-ui .bg-dark-red{background-color:#e7040f}.swagger-ui .bg-red{background-color:#ff4136}.swagger-ui .bg-light-red{background-color:#ff725c}.swagger-ui .bg-orange{background-color:#ff6300}.swagger-ui .bg-gold{background-color:#ffb700}.swagger-ui .bg-yellow{background-color:gold}.swagger-ui .bg-light-yellow{background-color:#fbf1a9}.swagger-ui .bg-purple{background-color:#5e2ca5}.swagger-ui .bg-light-purple{background-color:#a463f2}.swagger-ui .bg-dark-pink{background-color:#d5008f}.swagger-ui .bg-hot-pink{background-color:#ff41b4}.swagger-ui .bg-pink{background-color:#ff80cc}.swagger-ui .bg-light-pink{background-color:#ffa3d7}.swagger-ui .bg-dark-green{background-color:#137752}.swagger-ui .bg-green{background-color:#19a974}.swagger-ui .bg-light-green{background-color:#9eebcf}.swagger-ui .bg-navy{background-color:#001b44}.swagger-ui .bg-dark-blue{background-color:#00449e}.swagger-ui .bg-blue{background-color:#357edd}.swagger-ui .bg-light-blue{background-color:#96ccff}.swagger-ui .bg-lightest-blue{background-color:#cdecff}.swagger-ui .bg-washed-blue{background-color:#f6fffe}.swagger-ui .bg-washed-green{background-color:#e8fdf5}.swagger-ui .bg-washed-yellow{background-color:#fffceb}.swagger-ui .bg-washed-red{background-color:#ffdfdf}.swagger-ui .bg-inherit{background-color:inherit}.swagger-ui .hover-black:focus,.swagger-ui .hover-black:hover{color:#000}.swagger-ui .hover-near-black:focus,.swagger-ui .hover-near-black:hover{color:#111}.swagger-ui .hover-dark-gray:focus,.swagger-ui .hover-dark-gray:hover{color:#333}.swagger-ui .hover-mid-gray:focus,.swagger-ui .hover-mid-gray:hover{color:#555}.swagger-ui .hover-gray:focus,.swagger-ui .hover-gray:hover{color:#777}.swagger-ui .hover-silver:focus,.swagger-ui .hover-silver:hover{color:#999}.swagger-ui .hover-light-silver:focus,.swagger-ui .hover-light-silver:hover{color:#aaa}.swagger-ui .hover-moon-gray:focus,.swagger-ui .hover-moon-gray:hover{color:#ccc}.swagger-ui .hover-light-gray:focus,.swagger-ui .hover-light-gray:hover{color:#eee}.swagger-ui .hover-near-white:focus,.swagger-ui .hover-near-white:hover{color:#f4f4f4}.swagger-ui .hover-white:focus,.swagger-ui .hover-white:hover{color:#fff}.swagger-ui .hover-black-90:focus,.swagger-ui .hover-black-90:hover{color:rgba(0,0,0,.9)}.swagger-ui .hover-black-80:focus,.swagger-ui .hover-black-80:hover{color:rgba(0,0,0,.8)}.swagger-ui .hover-black-70:focus,.swagger-ui .hover-black-70:hover{color:rgba(0,0,0,.7)}.swagger-ui .hover-black-60:focus,.swagger-ui .hover-black-60:hover{color:rgba(0,0,0,.6)}.swagger-ui .hover-black-50:focus,.swagger-ui .hover-black-50:hover{color:rgba(0,0,0,.5)}.swagger-ui .hover-black-40:focus,.swagger-ui .hover-black-40:hover{color:rgba(0,0,0,.4)}.swagger-ui .hover-black-30:focus,.swagger-ui .hover-black-30:hover{color:rgba(0,0,0,.3)}.swagger-ui .hover-black-20:focus,.swagger-ui .hover-black-20:hover{color:rgba(0,0,0,.2)}.swagger-ui .hover-black-10:focus,.swagger-ui .hover-black-10:hover{color:rgba(0,0,0,.1)}.swagger-ui .hover-white-90:focus,.swagger-ui .hover-white-90:hover{color:hsla(0,0%,100%,.9)}.swagger-ui .hover-white-80:focus,.swagger-ui .hover-white-80:hover{color:hsla(0,0%,100%,.8)}.swagger-ui .hover-white-70:focus,.swagger-ui .hover-white-70:hover{color:hsla(0,0%,100%,.7)}.swagger-ui .hover-white-60:focus,.swagger-ui .hover-white-60:hover{color:hsla(0,0%,100%,.6)}.swagger-ui .hover-white-50:focus,.swagger-ui .hover-white-50:hover{color:hsla(0,0%,100%,.5)}.swagger-ui .hover-white-40:focus,.swagger-ui .hover-white-40:hover{color:hsla(0,0%,100%,.4)}.swagger-ui .hover-white-30:focus,.swagger-ui .hover-white-30:hover{color:hsla(0,0%,100%,.3)}.swagger-ui .hover-white-20:focus,.swagger-ui .hover-white-20:hover{color:hsla(0,0%,100%,.2)}.swagger-ui .hover-white-10:focus,.swagger-ui .hover-white-10:hover{color:hsla(0,0%,100%,.1)}.swagger-ui .hover-inherit:focus,.swagger-ui .hover-inherit:hover{color:inherit}.swagger-ui .hover-bg-black:focus,.swagger-ui .hover-bg-black:hover{background-color:#000}.swagger-ui .hover-bg-near-black:focus,.swagger-ui .hover-bg-near-black:hover{background-color:#111}.swagger-ui .hover-bg-dark-gray:focus,.swagger-ui .hover-bg-dark-gray:hover{background-color:#333}.swagger-ui .hover-bg-mid-gray:focus,.swagger-ui .hover-bg-mid-gray:hover{background-color:#555}.swagger-ui .hover-bg-gray:focus,.swagger-ui .hover-bg-gray:hover{background-color:#777}.swagger-ui .hover-bg-silver:focus,.swagger-ui .hover-bg-silver:hover{background-color:#999}.swagger-ui .hover-bg-light-silver:focus,.swagger-ui .hover-bg-light-silver:hover{background-color:#aaa}.swagger-ui .hover-bg-moon-gray:focus,.swagger-ui .hover-bg-moon-gray:hover{background-color:#ccc}.swagger-ui .hover-bg-light-gray:focus,.swagger-ui .hover-bg-light-gray:hover{background-color:#eee}.swagger-ui .hover-bg-near-white:focus,.swagger-ui .hover-bg-near-white:hover{background-color:#f4f4f4}.swagger-ui .hover-bg-white:focus,.swagger-ui .hover-bg-white:hover{background-color:#fff}.swagger-ui .hover-bg-transparent:focus,.swagger-ui .hover-bg-transparent:hover{background-color:transparent}.swagger-ui .hover-bg-black-90:focus,.swagger-ui .hover-bg-black-90:hover{background-color:rgba(0,0,0,.9)}.swagger-ui .hover-bg-black-80:focus,.swagger-ui .hover-bg-black-80:hover{background-color:rgba(0,0,0,.8)}.swagger-ui .hover-bg-black-70:focus,.swagger-ui .hover-bg-black-70:hover{background-color:rgba(0,0,0,.7)}.swagger-ui .hover-bg-black-60:focus,.swagger-ui .hover-bg-black-60:hover{background-color:rgba(0,0,0,.6)}.swagger-ui .hover-bg-black-50:focus,.swagger-ui .hover-bg-black-50:hover{background-color:rgba(0,0,0,.5)}.swagger-ui .hover-bg-black-40:focus,.swagger-ui .hover-bg-black-40:hover{background-color:rgba(0,0,0,.4)}.swagger-ui .hover-bg-black-30:focus,.swagger-ui .hover-bg-black-30:hover{background-color:rgba(0,0,0,.3)}.swagger-ui .hover-bg-black-20:focus,.swagger-ui .hover-bg-black-20:hover{background-color:rgba(0,0,0,.2)}.swagger-ui .hover-bg-black-10:focus,.swagger-ui .hover-bg-black-10:hover{background-color:rgba(0,0,0,.1)}.swagger-ui .hover-bg-white-90:focus,.swagger-ui .hover-bg-white-90:hover{background-color:hsla(0,0%,100%,.9)}.swagger-ui .hover-bg-white-80:focus,.swagger-ui .hover-bg-white-80:hover{background-color:hsla(0,0%,100%,.8)}.swagger-ui .hover-bg-white-70:focus,.swagger-ui .hover-bg-white-70:hover{background-color:hsla(0,0%,100%,.7)}.swagger-ui .hover-bg-white-60:focus,.swagger-ui .hover-bg-white-60:hover{background-color:hsla(0,0%,100%,.6)}.swagger-ui .hover-bg-white-50:focus,.swagger-ui .hover-bg-white-50:hover{background-color:hsla(0,0%,100%,.5)}.swagger-ui .hover-bg-white-40:focus,.swagger-ui .hover-bg-white-40:hover{background-color:hsla(0,0%,100%,.4)}.swagger-ui .hover-bg-white-30:focus,.swagger-ui .hover-bg-white-30:hover{background-color:hsla(0,0%,100%,.3)}.swagger-ui .hover-bg-white-20:focus,.swagger-ui .hover-bg-white-20:hover{background-color:hsla(0,0%,100%,.2)}.swagger-ui .hover-bg-white-10:focus,.swagger-ui .hover-bg-white-10:hover{background-color:hsla(0,0%,100%,.1)}.swagger-ui .hover-dark-red:focus,.swagger-ui .hover-dark-red:hover{color:#e7040f}.swagger-ui .hover-red:focus,.swagger-ui .hover-red:hover{color:#ff4136}.swagger-ui .hover-light-red:focus,.swagger-ui .hover-light-red:hover{color:#ff725c}.swagger-ui .hover-orange:focus,.swagger-ui .hover-orange:hover{color:#ff6300}.swagger-ui .hover-gold:focus,.swagger-ui .hover-gold:hover{color:#ffb700}.swagger-ui .hover-yellow:focus,.swagger-ui .hover-yellow:hover{color:gold}.swagger-ui .hover-light-yellow:focus,.swagger-ui .hover-light-yellow:hover{color:#fbf1a9}.swagger-ui .hover-purple:focus,.swagger-ui .hover-purple:hover{color:#5e2ca5}.swagger-ui .hover-light-purple:focus,.swagger-ui .hover-light-purple:hover{color:#a463f2}.swagger-ui .hover-dark-pink:focus,.swagger-ui .hover-dark-pink:hover{color:#d5008f}.swagger-ui .hover-hot-pink:focus,.swagger-ui .hover-hot-pink:hover{color:#ff41b4}.swagger-ui .hover-pink:focus,.swagger-ui .hover-pink:hover{color:#ff80cc}.swagger-ui .hover-light-pink:focus,.swagger-ui .hover-light-pink:hover{color:#ffa3d7}.swagger-ui .hover-dark-green:focus,.swagger-ui .hover-dark-green:hover{color:#137752}.swagger-ui .hover-green:focus,.swagger-ui .hover-green:hover{color:#19a974}.swagger-ui .hover-light-green:focus,.swagger-ui .hover-light-green:hover{color:#9eebcf}.swagger-ui .hover-navy:focus,.swagger-ui .hover-navy:hover{color:#001b44}.swagger-ui .hover-dark-blue:focus,.swagger-ui .hover-dark-blue:hover{color:#00449e}.swagger-ui .hover-blue:focus,.swagger-ui .hover-blue:hover{color:#357edd}.swagger-ui .hover-light-blue:focus,.swagger-ui .hover-light-blue:hover{color:#96ccff}.swagger-ui .hover-lightest-blue:focus,.swagger-ui .hover-lightest-blue:hover{color:#cdecff}.swagger-ui .hover-washed-blue:focus,.swagger-ui .hover-washed-blue:hover{color:#f6fffe}.swagger-ui .hover-washed-green:focus,.swagger-ui .hover-washed-green:hover{color:#e8fdf5}.swagger-ui .hover-washed-yellow:focus,.swagger-ui .hover-washed-yellow:hover{color:#fffceb}.swagger-ui .hover-washed-red:focus,.swagger-ui .hover-washed-red:hover{color:#ffdfdf}.swagger-ui .hover-bg-dark-red:focus,.swagger-ui .hover-bg-dark-red:hover{background-color:#e7040f}.swagger-ui .hover-bg-red:focus,.swagger-ui .hover-bg-red:hover{background-color:#ff4136}.swagger-ui .hover-bg-light-red:focus,.swagger-ui .hover-bg-light-red:hover{background-color:#ff725c}.swagger-ui .hover-bg-orange:focus,.swagger-ui .hover-bg-orange:hover{background-color:#ff6300}.swagger-ui .hover-bg-gold:focus,.swagger-ui .hover-bg-gold:hover{background-color:#ffb700}.swagger-ui .hover-bg-yellow:focus,.swagger-ui .hover-bg-yellow:hover{background-color:gold}.swagger-ui .hover-bg-light-yellow:focus,.swagger-ui .hover-bg-light-yellow:hover{background-color:#fbf1a9}.swagger-ui .hover-bg-purple:focus,.swagger-ui .hover-bg-purple:hover{background-color:#5e2ca5}.swagger-ui .hover-bg-light-purple:focus,.swagger-ui .hover-bg-light-purple:hover{background-color:#a463f2}.swagger-ui .hover-bg-dark-pink:focus,.swagger-ui .hover-bg-dark-pink:hover{background-color:#d5008f}.swagger-ui .hover-bg-hot-pink:focus,.swagger-ui .hover-bg-hot-pink:hover{background-color:#ff41b4}.swagger-ui .hover-bg-pink:focus,.swagger-ui .hover-bg-pink:hover{background-color:#ff80cc}.swagger-ui .hover-bg-light-pink:focus,.swagger-ui .hover-bg-light-pink:hover{background-color:#ffa3d7}.swagger-ui .hover-bg-dark-green:focus,.swagger-ui .hover-bg-dark-green:hover{background-color:#137752}.swagger-ui .hover-bg-green:focus,.swagger-ui .hover-bg-green:hover{background-color:#19a974}.swagger-ui .hover-bg-light-green:focus,.swagger-ui .hover-bg-light-green:hover{background-color:#9eebcf}.swagger-ui .hover-bg-navy:focus,.swagger-ui .hover-bg-navy:hover{background-color:#001b44}.swagger-ui .hover-bg-dark-blue:focus,.swagger-ui .hover-bg-dark-blue:hover{background-color:#00449e}.swagger-ui .hover-bg-blue:focus,.swagger-ui .hover-bg-blue:hover{background-color:#357edd}.swagger-ui .hover-bg-light-blue:focus,.swagger-ui .hover-bg-light-blue:hover{background-color:#96ccff}.swagger-ui .hover-bg-lightest-blue:focus,.swagger-ui .hover-bg-lightest-blue:hover{background-color:#cdecff}.swagger-ui .hover-bg-washed-blue:focus,.swagger-ui .hover-bg-washed-blue:hover{background-color:#f6fffe}.swagger-ui .hover-bg-washed-green:focus,.swagger-ui .hover-bg-washed-green:hover{background-color:#e8fdf5}.swagger-ui .hover-bg-washed-yellow:focus,.swagger-ui .hover-bg-washed-yellow:hover{background-color:#fffceb}.swagger-ui .hover-bg-washed-red:focus,.swagger-ui .hover-bg-washed-red:hover{background-color:#ffdfdf}.swagger-ui .hover-bg-inherit:focus,.swagger-ui .hover-bg-inherit:hover{background-color:inherit}.swagger-ui .pa0{padding:0}.swagger-ui .pa1{padding:.25rem}.swagger-ui .pa2{padding:.5rem}.swagger-ui .pa3{padding:1rem}.swagger-ui .pa4{padding:2rem}.swagger-ui .pa5{padding:4rem}.swagger-ui .pa6{padding:8rem}.swagger-ui .pa7{padding:16rem}.swagger-ui .pl0{padding-left:0}.swagger-ui .pl1{padding-left:.25rem}.swagger-ui .pl2{padding-left:.5rem}.swagger-ui .pl3{padding-left:1rem}.swagger-ui .pl4{padding-left:2rem}.swagger-ui .pl5{padding-left:4rem}.swagger-ui .pl6{padding-left:8rem}.swagger-ui .pl7{padding-left:16rem}.swagger-ui .pr0{padding-right:0}.swagger-ui .pr1{padding-right:.25rem}.swagger-ui .pr2{padding-right:.5rem}.swagger-ui .pr3{padding-right:1rem}.swagger-ui .pr4{padding-right:2rem}.swagger-ui .pr5{padding-right:4rem}.swagger-ui .pr6{padding-right:8rem}.swagger-ui .pr7{padding-right:16rem}.swagger-ui .pb0{padding-bottom:0}.swagger-ui .pb1{padding-bottom:.25rem}.swagger-ui .pb2{padding-bottom:.5rem}.swagger-ui .pb3{padding-bottom:1rem}.swagger-ui .pb4{padding-bottom:2rem}.swagger-ui .pb5{padding-bottom:4rem}.swagger-ui .pb6{padding-bottom:8rem}.swagger-ui .pb7{padding-bottom:16rem}.swagger-ui .pt0{padding-top:0}.swagger-ui .pt1{padding-top:.25rem}.swagger-ui .pt2{padding-top:.5rem}.swagger-ui .pt3{padding-top:1rem}.swagger-ui .pt4{padding-top:2rem}.swagger-ui .pt5{padding-top:4rem}.swagger-ui .pt6{padding-top:8rem}.swagger-ui .pt7{padding-top:16rem}.swagger-ui .pv0{padding-top:0;padding-bottom:0}.swagger-ui .pv1{padding-top:.25rem;padding-bottom:.25rem}.swagger-ui .pv2{padding-top:.5rem;padding-bottom:.5rem}.swagger-ui .pv3{padding-top:1rem;padding-bottom:1rem}.swagger-ui .pv4{padding-top:2rem;padding-bottom:2rem}.swagger-ui .pv5{padding-top:4rem;padding-bottom:4rem}.swagger-ui .pv6{padding-top:8rem;padding-bottom:8rem}.swagger-ui .pv7{padding-top:16rem;padding-bottom:16rem}.swagger-ui .ph0{padding-left:0;padding-right:0}.swagger-ui .ph1{padding-left:.25rem;padding-right:.25rem}.swagger-ui .ph2{padding-left:.5rem;padding-right:.5rem}.swagger-ui .ph3{padding-left:1rem;padding-right:1rem}.swagger-ui .ph4{padding-left:2rem;padding-right:2rem}.swagger-ui .ph5{padding-left:4rem;padding-right:4rem}.swagger-ui .ph6{padding-left:8rem;padding-right:8rem}.swagger-ui .ph7{padding-left:16rem;padding-right:16rem}.swagger-ui .ma0{margin:0}.swagger-ui .ma1{margin:.25rem}.swagger-ui .ma2{margin:.5rem}.swagger-ui .ma3{margin:1rem}.swagger-ui .ma4{margin:2rem}.swagger-ui .ma5{margin:4rem}.swagger-ui .ma6{margin:8rem}.swagger-ui .ma7{margin:16rem}.swagger-ui .ml0{margin-left:0}.swagger-ui .ml1{margin-left:.25rem}.swagger-ui .ml2{margin-left:.5rem}.swagger-ui .ml3{margin-left:1rem}.swagger-ui .ml4{margin-left:2rem}.swagger-ui .ml5{margin-left:4rem}.swagger-ui .ml6{margin-left:8rem}.swagger-ui .ml7{margin-left:16rem}.swagger-ui .mr0{margin-right:0}.swagger-ui .mr1{margin-right:.25rem}.swagger-ui .mr2{margin-right:.5rem}.swagger-ui .mr3{margin-right:1rem}.swagger-ui .mr4{margin-right:2rem}.swagger-ui .mr5{margin-right:4rem}.swagger-ui .mr6{margin-right:8rem}.swagger-ui .mr7{margin-right:16rem}.swagger-ui .mb0{margin-bottom:0}.swagger-ui .mb1{margin-bottom:.25rem}.swagger-ui .mb2{margin-bottom:.5rem}.swagger-ui .mb3{margin-bottom:1rem}.swagger-ui .mb4{margin-bottom:2rem}.swagger-ui .mb5{margin-bottom:4rem}.swagger-ui .mb6{margin-bottom:8rem}.swagger-ui .mb7{margin-bottom:16rem}.swagger-ui .mt0{margin-top:0}.swagger-ui .mt1{margin-top:.25rem}.swagger-ui .mt2{margin-top:.5rem}.swagger-ui .mt3{margin-top:1rem}.swagger-ui .mt4{margin-top:2rem}.swagger-ui .mt5{margin-top:4rem}.swagger-ui .mt6{margin-top:8rem}.swagger-ui .mt7{margin-top:16rem}.swagger-ui .mv0{margin-top:0;margin-bottom:0}.swagger-ui .mv1{margin-top:.25rem;margin-bottom:.25rem}.swagger-ui .mv2{margin-top:.5rem;margin-bottom:.5rem}.swagger-ui .mv3{margin-top:1rem;margin-bottom:1rem}.swagger-ui .mv4{margin-top:2rem;margin-bottom:2rem}.swagger-ui .mv5{margin-top:4rem;margin-bottom:4rem}.swagger-ui .mv6{margin-top:8rem;margin-bottom:8rem}.swagger-ui .mv7{margin-top:16rem;margin-bottom:16rem}.swagger-ui .mh0{margin-left:0;margin-right:0}.swagger-ui .mh1{margin-left:.25rem;margin-right:.25rem}.swagger-ui .mh2{margin-left:.5rem;margin-right:.5rem}.swagger-ui .mh3{margin-left:1rem;margin-right:1rem}.swagger-ui .mh4{margin-left:2rem;margin-right:2rem}.swagger-ui .mh5{margin-left:4rem;margin-right:4rem}.swagger-ui .mh6{margin-left:8rem;margin-right:8rem}.swagger-ui .mh7{margin-left:16rem;margin-right:16rem}@media screen and (min-width:30em){.swagger-ui .pa0-ns{padding:0}.swagger-ui .pa1-ns{padding:.25rem}.swagger-ui .pa2-ns{padding:.5rem}.swagger-ui .pa3-ns{padding:1rem}.swagger-ui .pa4-ns{padding:2rem}.swagger-ui .pa5-ns{padding:4rem}.swagger-ui .pa6-ns{padding:8rem}.swagger-ui .pa7-ns{padding:16rem}.swagger-ui .pl0-ns{padding-left:0}.swagger-ui .pl1-ns{padding-left:.25rem}.swagger-ui .pl2-ns{padding-left:.5rem}.swagger-ui .pl3-ns{padding-left:1rem}.swagger-ui .pl4-ns{padding-left:2rem}.swagger-ui .pl5-ns{padding-left:4rem}.swagger-ui .pl6-ns{padding-left:8rem}.swagger-ui .pl7-ns{padding-left:16rem}.swagger-ui .pr0-ns{padding-right:0}.swagger-ui .pr1-ns{padding-right:.25rem}.swagger-ui .pr2-ns{padding-right:.5rem}.swagger-ui .pr3-ns{padding-right:1rem}.swagger-ui .pr4-ns{padding-right:2rem}.swagger-ui .pr5-ns{padding-right:4rem}.swagger-ui .pr6-ns{padding-right:8rem}.swagger-ui .pr7-ns{padding-right:16rem}.swagger-ui .pb0-ns{padding-bottom:0}.swagger-ui .pb1-ns{padding-bottom:.25rem}.swagger-ui .pb2-ns{padding-bottom:.5rem}.swagger-ui .pb3-ns{padding-bottom:1rem}.swagger-ui .pb4-ns{padding-bottom:2rem}.swagger-ui .pb5-ns{padding-bottom:4rem}.swagger-ui .pb6-ns{padding-bottom:8rem}.swagger-ui .pb7-ns{padding-bottom:16rem}.swagger-ui .pt0-ns{padding-top:0}.swagger-ui .pt1-ns{padding-top:.25rem}.swagger-ui .pt2-ns{padding-top:.5rem}.swagger-ui .pt3-ns{padding-top:1rem}.swagger-ui .pt4-ns{padding-top:2rem}.swagger-ui .pt5-ns{padding-top:4rem}.swagger-ui .pt6-ns{padding-top:8rem}.swagger-ui .pt7-ns{padding-top:16rem}.swagger-ui .pv0-ns{padding-top:0;padding-bottom:0}.swagger-ui .pv1-ns{padding-top:.25rem;padding-bottom:.25rem}.swagger-ui .pv2-ns{padding-top:.5rem;padding-bottom:.5rem}.swagger-ui .pv3-ns{padding-top:1rem;padding-bottom:1rem}.swagger-ui .pv4-ns{padding-top:2rem;padding-bottom:2rem}.swagger-ui .pv5-ns{padding-top:4rem;padding-bottom:4rem}.swagger-ui .pv6-ns{padding-top:8rem;padding-bottom:8rem}.swagger-ui .pv7-ns{padding-top:16rem;padding-bottom:16rem}.swagger-ui .ph0-ns{padding-left:0;padding-right:0}.swagger-ui .ph1-ns{padding-left:.25rem;padding-right:.25rem}.swagger-ui .ph2-ns{padding-left:.5rem;padding-right:.5rem}.swagger-ui .ph3-ns{padding-left:1rem;padding-right:1rem}.swagger-ui .ph4-ns{padding-left:2rem;padding-right:2rem}.swagger-ui .ph5-ns{padding-left:4rem;padding-right:4rem}.swagger-ui .ph6-ns{padding-left:8rem;padding-right:8rem}.swagger-ui .ph7-ns{padding-left:16rem;padding-right:16rem}.swagger-ui .ma0-ns{margin:0}.swagger-ui .ma1-ns{margin:.25rem}.swagger-ui .ma2-ns{margin:.5rem}.swagger-ui .ma3-ns{margin:1rem}.swagger-ui .ma4-ns{margin:2rem}.swagger-ui .ma5-ns{margin:4rem}.swagger-ui .ma6-ns{margin:8rem}.swagger-ui .ma7-ns{margin:16rem}.swagger-ui .ml0-ns{margin-left:0}.swagger-ui .ml1-ns{margin-left:.25rem}.swagger-ui .ml2-ns{margin-left:.5rem}.swagger-ui .ml3-ns{margin-left:1rem}.swagger-ui .ml4-ns{margin-left:2rem}.swagger-ui .ml5-ns{margin-left:4rem}.swagger-ui .ml6-ns{margin-left:8rem}.swagger-ui .ml7-ns{margin-left:16rem}.swagger-ui .mr0-ns{margin-right:0}.swagger-ui .mr1-ns{margin-right:.25rem}.swagger-ui .mr2-ns{margin-right:.5rem}.swagger-ui .mr3-ns{margin-right:1rem}.swagger-ui .mr4-ns{margin-right:2rem}.swagger-ui .mr5-ns{margin-right:4rem}.swagger-ui .mr6-ns{margin-right:8rem}.swagger-ui .mr7-ns{margin-right:16rem}.swagger-ui .mb0-ns{margin-bottom:0}.swagger-ui .mb1-ns{margin-bottom:.25rem}.swagger-ui .mb2-ns{margin-bottom:.5rem}.swagger-ui .mb3-ns{margin-bottom:1rem}.swagger-ui .mb4-ns{margin-bottom:2rem}.swagger-ui .mb5-ns{margin-bottom:4rem}.swagger-ui .mb6-ns{margin-bottom:8rem}.swagger-ui .mb7-ns{margin-bottom:16rem}.swagger-ui .mt0-ns{margin-top:0}.swagger-ui .mt1-ns{margin-top:.25rem}.swagger-ui .mt2-ns{margin-top:.5rem}.swagger-ui .mt3-ns{margin-top:1rem}.swagger-ui .mt4-ns{margin-top:2rem}.swagger-ui .mt5-ns{margin-top:4rem}.swagger-ui .mt6-ns{margin-top:8rem}.swagger-ui .mt7-ns{margin-top:16rem}.swagger-ui .mv0-ns{margin-top:0;margin-bottom:0}.swagger-ui .mv1-ns{margin-top:.25rem;margin-bottom:.25rem}.swagger-ui .mv2-ns{margin-top:.5rem;margin-bottom:.5rem}.swagger-ui .mv3-ns{margin-top:1rem;margin-bottom:1rem}.swagger-ui .mv4-ns{margin-top:2rem;margin-bottom:2rem}.swagger-ui .mv5-ns{margin-top:4rem;margin-bottom:4rem}.swagger-ui .mv6-ns{margin-top:8rem;margin-bottom:8rem}.swagger-ui .mv7-ns{margin-top:16rem;margin-bottom:16rem}.swagger-ui .mh0-ns{margin-left:0;margin-right:0}.swagger-ui .mh1-ns{margin-left:.25rem;margin-right:.25rem}.swagger-ui .mh2-ns{margin-left:.5rem;margin-right:.5rem}.swagger-ui .mh3-ns{margin-left:1rem;margin-right:1rem}.swagger-ui .mh4-ns{margin-left:2rem;margin-right:2rem}.swagger-ui .mh5-ns{margin-left:4rem;margin-right:4rem}.swagger-ui .mh6-ns{margin-left:8rem;margin-right:8rem}.swagger-ui .mh7-ns{margin-left:16rem;margin-right:16rem}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .pa0-m{padding:0}.swagger-ui .pa1-m{padding:.25rem}.swagger-ui .pa2-m{padding:.5rem}.swagger-ui .pa3-m{padding:1rem}.swagger-ui .pa4-m{padding:2rem}.swagger-ui .pa5-m{padding:4rem}.swagger-ui .pa6-m{padding:8rem}.swagger-ui .pa7-m{padding:16rem}.swagger-ui .pl0-m{padding-left:0}.swagger-ui .pl1-m{padding-left:.25rem}.swagger-ui .pl2-m{padding-left:.5rem}.swagger-ui .pl3-m{padding-left:1rem}.swagger-ui .pl4-m{padding-left:2rem}.swagger-ui .pl5-m{padding-left:4rem}.swagger-ui .pl6-m{padding-left:8rem}.swagger-ui .pl7-m{padding-left:16rem}.swagger-ui .pr0-m{padding-right:0}.swagger-ui .pr1-m{padding-right:.25rem}.swagger-ui .pr2-m{padding-right:.5rem}.swagger-ui .pr3-m{padding-right:1rem}.swagger-ui .pr4-m{padding-right:2rem}.swagger-ui .pr5-m{padding-right:4rem}.swagger-ui .pr6-m{padding-right:8rem}.swagger-ui .pr7-m{padding-right:16rem}.swagger-ui .pb0-m{padding-bottom:0}.swagger-ui .pb1-m{padding-bottom:.25rem}.swagger-ui .pb2-m{padding-bottom:.5rem}.swagger-ui .pb3-m{padding-bottom:1rem}.swagger-ui .pb4-m{padding-bottom:2rem}.swagger-ui .pb5-m{padding-bottom:4rem}.swagger-ui .pb6-m{padding-bottom:8rem}.swagger-ui .pb7-m{padding-bottom:16rem}.swagger-ui .pt0-m{padding-top:0}.swagger-ui .pt1-m{padding-top:.25rem}.swagger-ui .pt2-m{padding-top:.5rem}.swagger-ui .pt3-m{padding-top:1rem}.swagger-ui .pt4-m{padding-top:2rem}.swagger-ui .pt5-m{padding-top:4rem}.swagger-ui .pt6-m{padding-top:8rem}.swagger-ui .pt7-m{padding-top:16rem}.swagger-ui .pv0-m{padding-top:0;padding-bottom:0}.swagger-ui .pv1-m{padding-top:.25rem;padding-bottom:.25rem}.swagger-ui .pv2-m{padding-top:.5rem;padding-bottom:.5rem}.swagger-ui .pv3-m{padding-top:1rem;padding-bottom:1rem}.swagger-ui .pv4-m{padding-top:2rem;padding-bottom:2rem}.swagger-ui .pv5-m{padding-top:4rem;padding-bottom:4rem}.swagger-ui .pv6-m{padding-top:8rem;padding-bottom:8rem}.swagger-ui .pv7-m{padding-top:16rem;padding-bottom:16rem}.swagger-ui .ph0-m{padding-left:0;padding-right:0}.swagger-ui .ph1-m{padding-left:.25rem;padding-right:.25rem}.swagger-ui .ph2-m{padding-left:.5rem;padding-right:.5rem}.swagger-ui .ph3-m{padding-left:1rem;padding-right:1rem}.swagger-ui .ph4-m{padding-left:2rem;padding-right:2rem}.swagger-ui .ph5-m{padding-left:4rem;padding-right:4rem}.swagger-ui .ph6-m{padding-left:8rem;padding-right:8rem}.swagger-ui .ph7-m{padding-left:16rem;padding-right:16rem}.swagger-ui .ma0-m{margin:0}.swagger-ui .ma1-m{margin:.25rem}.swagger-ui .ma2-m{margin:.5rem}.swagger-ui .ma3-m{margin:1rem}.swagger-ui .ma4-m{margin:2rem}.swagger-ui .ma5-m{margin:4rem}.swagger-ui .ma6-m{margin:8rem}.swagger-ui .ma7-m{margin:16rem}.swagger-ui .ml0-m{margin-left:0}.swagger-ui .ml1-m{margin-left:.25rem}.swagger-ui .ml2-m{margin-left:.5rem}.swagger-ui .ml3-m{margin-left:1rem}.swagger-ui .ml4-m{margin-left:2rem}.swagger-ui .ml5-m{margin-left:4rem}.swagger-ui .ml6-m{margin-left:8rem}.swagger-ui .ml7-m{margin-left:16rem}.swagger-ui .mr0-m{margin-right:0}.swagger-ui .mr1-m{margin-right:.25rem}.swagger-ui .mr2-m{margin-right:.5rem}.swagger-ui .mr3-m{margin-right:1rem}.swagger-ui .mr4-m{margin-right:2rem}.swagger-ui .mr5-m{margin-right:4rem}.swagger-ui .mr6-m{margin-right:8rem}.swagger-ui .mr7-m{margin-right:16rem}.swagger-ui .mb0-m{margin-bottom:0}.swagger-ui .mb1-m{margin-bottom:.25rem}.swagger-ui .mb2-m{margin-bottom:.5rem}.swagger-ui .mb3-m{margin-bottom:1rem}.swagger-ui .mb4-m{margin-bottom:2rem}.swagger-ui .mb5-m{margin-bottom:4rem}.swagger-ui .mb6-m{margin-bottom:8rem}.swagger-ui .mb7-m{margin-bottom:16rem}.swagger-ui .mt0-m{margin-top:0}.swagger-ui .mt1-m{margin-top:.25rem}.swagger-ui .mt2-m{margin-top:.5rem}.swagger-ui .mt3-m{margin-top:1rem}.swagger-ui .mt4-m{margin-top:2rem}.swagger-ui .mt5-m{margin-top:4rem}.swagger-ui .mt6-m{margin-top:8rem}.swagger-ui .mt7-m{margin-top:16rem}.swagger-ui .mv0-m{margin-top:0;margin-bottom:0}.swagger-ui .mv1-m{margin-top:.25rem;margin-bottom:.25rem}.swagger-ui .mv2-m{margin-top:.5rem;margin-bottom:.5rem}.swagger-ui .mv3-m{margin-top:1rem;margin-bottom:1rem}.swagger-ui .mv4-m{margin-top:2rem;margin-bottom:2rem}.swagger-ui .mv5-m{margin-top:4rem;margin-bottom:4rem}.swagger-ui .mv6-m{margin-top:8rem;margin-bottom:8rem}.swagger-ui .mv7-m{margin-top:16rem;margin-bottom:16rem}.swagger-ui .mh0-m{margin-left:0;margin-right:0}.swagger-ui .mh1-m{margin-left:.25rem;margin-right:.25rem}.swagger-ui .mh2-m{margin-left:.5rem;margin-right:.5rem}.swagger-ui .mh3-m{margin-left:1rem;margin-right:1rem}.swagger-ui .mh4-m{margin-left:2rem;margin-right:2rem}.swagger-ui .mh5-m{margin-left:4rem;margin-right:4rem}.swagger-ui .mh6-m{margin-left:8rem;margin-right:8rem}.swagger-ui .mh7-m{margin-left:16rem;margin-right:16rem}}@media screen and (min-width:60em){.swagger-ui .pa0-l{padding:0}.swagger-ui .pa1-l{padding:.25rem}.swagger-ui .pa2-l{padding:.5rem}.swagger-ui .pa3-l{padding:1rem}.swagger-ui .pa4-l{padding:2rem}.swagger-ui .pa5-l{padding:4rem}.swagger-ui .pa6-l{padding:8rem}.swagger-ui .pa7-l{padding:16rem}.swagger-ui .pl0-l{padding-left:0}.swagger-ui .pl1-l{padding-left:.25rem}.swagger-ui .pl2-l{padding-left:.5rem}.swagger-ui .pl3-l{padding-left:1rem}.swagger-ui .pl4-l{padding-left:2rem}.swagger-ui .pl5-l{padding-left:4rem}.swagger-ui .pl6-l{padding-left:8rem}.swagger-ui .pl7-l{padding-left:16rem}.swagger-ui .pr0-l{padding-right:0}.swagger-ui .pr1-l{padding-right:.25rem}.swagger-ui .pr2-l{padding-right:.5rem}.swagger-ui .pr3-l{padding-right:1rem}.swagger-ui .pr4-l{padding-right:2rem}.swagger-ui .pr5-l{padding-right:4rem}.swagger-ui .pr6-l{padding-right:8rem}.swagger-ui .pr7-l{padding-right:16rem}.swagger-ui .pb0-l{padding-bottom:0}.swagger-ui .pb1-l{padding-bottom:.25rem}.swagger-ui .pb2-l{padding-bottom:.5rem}.swagger-ui .pb3-l{padding-bottom:1rem}.swagger-ui .pb4-l{padding-bottom:2rem}.swagger-ui .pb5-l{padding-bottom:4rem}.swagger-ui .pb6-l{padding-bottom:8rem}.swagger-ui .pb7-l{padding-bottom:16rem}.swagger-ui .pt0-l{padding-top:0}.swagger-ui .pt1-l{padding-top:.25rem}.swagger-ui .pt2-l{padding-top:.5rem}.swagger-ui .pt3-l{padding-top:1rem}.swagger-ui .pt4-l{padding-top:2rem}.swagger-ui .pt5-l{padding-top:4rem}.swagger-ui .pt6-l{padding-top:8rem}.swagger-ui .pt7-l{padding-top:16rem}.swagger-ui .pv0-l{padding-top:0;padding-bottom:0}.swagger-ui .pv1-l{padding-top:.25rem;padding-bottom:.25rem}.swagger-ui .pv2-l{padding-top:.5rem;padding-bottom:.5rem}.swagger-ui .pv3-l{padding-top:1rem;padding-bottom:1rem}.swagger-ui .pv4-l{padding-top:2rem;padding-bottom:2rem}.swagger-ui .pv5-l{padding-top:4rem;padding-bottom:4rem}.swagger-ui .pv6-l{padding-top:8rem;padding-bottom:8rem}.swagger-ui .pv7-l{padding-top:16rem;padding-bottom:16rem}.swagger-ui .ph0-l{padding-left:0;padding-right:0}.swagger-ui .ph1-l{padding-left:.25rem;padding-right:.25rem}.swagger-ui .ph2-l{padding-left:.5rem;padding-right:.5rem}.swagger-ui .ph3-l{padding-left:1rem;padding-right:1rem}.swagger-ui .ph4-l{padding-left:2rem;padding-right:2rem}.swagger-ui .ph5-l{padding-left:4rem;padding-right:4rem}.swagger-ui .ph6-l{padding-left:8rem;padding-right:8rem}.swagger-ui .ph7-l{padding-left:16rem;padding-right:16rem}.swagger-ui .ma0-l{margin:0}.swagger-ui .ma1-l{margin:.25rem}.swagger-ui .ma2-l{margin:.5rem}.swagger-ui .ma3-l{margin:1rem}.swagger-ui .ma4-l{margin:2rem}.swagger-ui .ma5-l{margin:4rem}.swagger-ui .ma6-l{margin:8rem}.swagger-ui .ma7-l{margin:16rem}.swagger-ui .ml0-l{margin-left:0}.swagger-ui .ml1-l{margin-left:.25rem}.swagger-ui .ml2-l{margin-left:.5rem}.swagger-ui .ml3-l{margin-left:1rem}.swagger-ui .ml4-l{margin-left:2rem}.swagger-ui .ml5-l{margin-left:4rem}.swagger-ui .ml6-l{margin-left:8rem}.swagger-ui .ml7-l{margin-left:16rem}.swagger-ui .mr0-l{margin-right:0}.swagger-ui .mr1-l{margin-right:.25rem}.swagger-ui .mr2-l{margin-right:.5rem}.swagger-ui .mr3-l{margin-right:1rem}.swagger-ui .mr4-l{margin-right:2rem}.swagger-ui .mr5-l{margin-right:4rem}.swagger-ui .mr6-l{margin-right:8rem}.swagger-ui .mr7-l{margin-right:16rem}.swagger-ui .mb0-l{margin-bottom:0}.swagger-ui .mb1-l{margin-bottom:.25rem}.swagger-ui .mb2-l{margin-bottom:.5rem}.swagger-ui .mb3-l{margin-bottom:1rem}.swagger-ui .mb4-l{margin-bottom:2rem}.swagger-ui .mb5-l{margin-bottom:4rem}.swagger-ui .mb6-l{margin-bottom:8rem}.swagger-ui .mb7-l{margin-bottom:16rem}.swagger-ui .mt0-l{margin-top:0}.swagger-ui .mt1-l{margin-top:.25rem}.swagger-ui .mt2-l{margin-top:.5rem}.swagger-ui .mt3-l{margin-top:1rem}.swagger-ui .mt4-l{margin-top:2rem}.swagger-ui .mt5-l{margin-top:4rem}.swagger-ui .mt6-l{margin-top:8rem}.swagger-ui .mt7-l{margin-top:16rem}.swagger-ui .mv0-l{margin-top:0;margin-bottom:0}.swagger-ui .mv1-l{margin-top:.25rem;margin-bottom:.25rem}.swagger-ui .mv2-l{margin-top:.5rem;margin-bottom:.5rem}.swagger-ui .mv3-l{margin-top:1rem;margin-bottom:1rem}.swagger-ui .mv4-l{margin-top:2rem;margin-bottom:2rem}.swagger-ui .mv5-l{margin-top:4rem;margin-bottom:4rem}.swagger-ui .mv6-l{margin-top:8rem;margin-bottom:8rem}.swagger-ui .mv7-l{margin-top:16rem;margin-bottom:16rem}.swagger-ui .mh0-l{margin-left:0;margin-right:0}.swagger-ui .mh1-l{margin-left:.25rem;margin-right:.25rem}.swagger-ui .mh2-l{margin-left:.5rem;margin-right:.5rem}.swagger-ui .mh3-l{margin-left:1rem;margin-right:1rem}.swagger-ui .mh4-l{margin-left:2rem;margin-right:2rem}.swagger-ui .mh5-l{margin-left:4rem;margin-right:4rem}.swagger-ui .mh6-l{margin-left:8rem;margin-right:8rem}.swagger-ui .mh7-l{margin-left:16rem;margin-right:16rem}}.swagger-ui .na1{margin:-.25rem}.swagger-ui .na2{margin:-.5rem}.swagger-ui .na3{margin:-1rem}.swagger-ui .na4{margin:-2rem}.swagger-ui .na5{margin:-4rem}.swagger-ui .na6{margin:-8rem}.swagger-ui .na7{margin:-16rem}.swagger-ui .nl1{margin-left:-.25rem}.swagger-ui .nl2{margin-left:-.5rem}.swagger-ui .nl3{margin-left:-1rem}.swagger-ui .nl4{margin-left:-2rem}.swagger-ui .nl5{margin-left:-4rem}.swagger-ui .nl6{margin-left:-8rem}.swagger-ui .nl7{margin-left:-16rem}.swagger-ui .nr1{margin-right:-.25rem}.swagger-ui .nr2{margin-right:-.5rem}.swagger-ui .nr3{margin-right:-1rem}.swagger-ui .nr4{margin-right:-2rem}.swagger-ui .nr5{margin-right:-4rem}.swagger-ui .nr6{margin-right:-8rem}.swagger-ui .nr7{margin-right:-16rem}.swagger-ui .nb1{margin-bottom:-.25rem}.swagger-ui .nb2{margin-bottom:-.5rem}.swagger-ui .nb3{margin-bottom:-1rem}.swagger-ui .nb4{margin-bottom:-2rem}.swagger-ui .nb5{margin-bottom:-4rem}.swagger-ui .nb6{margin-bottom:-8rem}.swagger-ui .nb7{margin-bottom:-16rem}.swagger-ui .nt1{margin-top:-.25rem}.swagger-ui .nt2{margin-top:-.5rem}.swagger-ui .nt3{margin-top:-1rem}.swagger-ui .nt4{margin-top:-2rem}.swagger-ui .nt5{margin-top:-4rem}.swagger-ui .nt6{margin-top:-8rem}.swagger-ui .nt7{margin-top:-16rem}@media screen and (min-width:30em){.swagger-ui .na1-ns{margin:-.25rem}.swagger-ui .na2-ns{margin:-.5rem}.swagger-ui .na3-ns{margin:-1rem}.swagger-ui .na4-ns{margin:-2rem}.swagger-ui .na5-ns{margin:-4rem}.swagger-ui .na6-ns{margin:-8rem}.swagger-ui .na7-ns{margin:-16rem}.swagger-ui .nl1-ns{margin-left:-.25rem}.swagger-ui .nl2-ns{margin-left:-.5rem}.swagger-ui .nl3-ns{margin-left:-1rem}.swagger-ui .nl4-ns{margin-left:-2rem}.swagger-ui .nl5-ns{margin-left:-4rem}.swagger-ui .nl6-ns{margin-left:-8rem}.swagger-ui .nl7-ns{margin-left:-16rem}.swagger-ui .nr1-ns{margin-right:-.25rem}.swagger-ui .nr2-ns{margin-right:-.5rem}.swagger-ui .nr3-ns{margin-right:-1rem}.swagger-ui .nr4-ns{margin-right:-2rem}.swagger-ui .nr5-ns{margin-right:-4rem}.swagger-ui .nr6-ns{margin-right:-8rem}.swagger-ui .nr7-ns{margin-right:-16rem}.swagger-ui .nb1-ns{margin-bottom:-.25rem}.swagger-ui .nb2-ns{margin-bottom:-.5rem}.swagger-ui .nb3-ns{margin-bottom:-1rem}.swagger-ui .nb4-ns{margin-bottom:-2rem}.swagger-ui .nb5-ns{margin-bottom:-4rem}.swagger-ui .nb6-ns{margin-bottom:-8rem}.swagger-ui .nb7-ns{margin-bottom:-16rem}.swagger-ui .nt1-ns{margin-top:-.25rem}.swagger-ui .nt2-ns{margin-top:-.5rem}.swagger-ui .nt3-ns{margin-top:-1rem}.swagger-ui .nt4-ns{margin-top:-2rem}.swagger-ui .nt5-ns{margin-top:-4rem}.swagger-ui .nt6-ns{margin-top:-8rem}.swagger-ui .nt7-ns{margin-top:-16rem}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .na1-m{margin:-.25rem}.swagger-ui .na2-m{margin:-.5rem}.swagger-ui .na3-m{margin:-1rem}.swagger-ui .na4-m{margin:-2rem}.swagger-ui .na5-m{margin:-4rem}.swagger-ui .na6-m{margin:-8rem}.swagger-ui .na7-m{margin:-16rem}.swagger-ui .nl1-m{margin-left:-.25rem}.swagger-ui .nl2-m{margin-left:-.5rem}.swagger-ui .nl3-m{margin-left:-1rem}.swagger-ui .nl4-m{margin-left:-2rem}.swagger-ui .nl5-m{margin-left:-4rem}.swagger-ui .nl6-m{margin-left:-8rem}.swagger-ui .nl7-m{margin-left:-16rem}.swagger-ui .nr1-m{margin-right:-.25rem}.swagger-ui .nr2-m{margin-right:-.5rem}.swagger-ui .nr3-m{margin-right:-1rem}.swagger-ui .nr4-m{margin-right:-2rem}.swagger-ui .nr5-m{margin-right:-4rem}.swagger-ui .nr6-m{margin-right:-8rem}.swagger-ui .nr7-m{margin-right:-16rem}.swagger-ui .nb1-m{margin-bottom:-.25rem}.swagger-ui .nb2-m{margin-bottom:-.5rem}.swagger-ui .nb3-m{margin-bottom:-1rem}.swagger-ui .nb4-m{margin-bottom:-2rem}.swagger-ui .nb5-m{margin-bottom:-4rem}.swagger-ui .nb6-m{margin-bottom:-8rem}.swagger-ui .nb7-m{margin-bottom:-16rem}.swagger-ui .nt1-m{margin-top:-.25rem}.swagger-ui .nt2-m{margin-top:-.5rem}.swagger-ui .nt3-m{margin-top:-1rem}.swagger-ui .nt4-m{margin-top:-2rem}.swagger-ui .nt5-m{margin-top:-4rem}.swagger-ui .nt6-m{margin-top:-8rem}.swagger-ui .nt7-m{margin-top:-16rem}}@media screen and (min-width:60em){.swagger-ui .na1-l{margin:-.25rem}.swagger-ui .na2-l{margin:-.5rem}.swagger-ui .na3-l{margin:-1rem}.swagger-ui .na4-l{margin:-2rem}.swagger-ui .na5-l{margin:-4rem}.swagger-ui .na6-l{margin:-8rem}.swagger-ui .na7-l{margin:-16rem}.swagger-ui .nl1-l{margin-left:-.25rem}.swagger-ui .nl2-l{margin-left:-.5rem}.swagger-ui .nl3-l{margin-left:-1rem}.swagger-ui .nl4-l{margin-left:-2rem}.swagger-ui .nl5-l{margin-left:-4rem}.swagger-ui .nl6-l{margin-left:-8rem}.swagger-ui .nl7-l{margin-left:-16rem}.swagger-ui .nr1-l{margin-right:-.25rem}.swagger-ui .nr2-l{margin-right:-.5rem}.swagger-ui .nr3-l{margin-right:-1rem}.swagger-ui .nr4-l{margin-right:-2rem}.swagger-ui .nr5-l{margin-right:-4rem}.swagger-ui .nr6-l{margin-right:-8rem}.swagger-ui .nr7-l{margin-right:-16rem}.swagger-ui .nb1-l{margin-bottom:-.25rem}.swagger-ui .nb2-l{margin-bottom:-.5rem}.swagger-ui .nb3-l{margin-bottom:-1rem}.swagger-ui .nb4-l{margin-bottom:-2rem}.swagger-ui .nb5-l{margin-bottom:-4rem}.swagger-ui .nb6-l{margin-bottom:-8rem}.swagger-ui .nb7-l{margin-bottom:-16rem}.swagger-ui .nt1-l{margin-top:-.25rem}.swagger-ui .nt2-l{margin-top:-.5rem}.swagger-ui .nt3-l{margin-top:-1rem}.swagger-ui .nt4-l{margin-top:-2rem}.swagger-ui .nt5-l{margin-top:-4rem}.swagger-ui .nt6-l{margin-top:-8rem}.swagger-ui .nt7-l{margin-top:-16rem}}.swagger-ui .collapse{border-collapse:collapse;border-spacing:0}.swagger-ui .striped--light-silver:nth-child(odd){background-color:#aaa}.swagger-ui .striped--moon-gray:nth-child(odd){background-color:#ccc}.swagger-ui .striped--light-gray:nth-child(odd){background-color:#eee}.swagger-ui .striped--near-white:nth-child(odd){background-color:#f4f4f4}.swagger-ui .stripe-light:nth-child(odd){background-color:hsla(0,0%,100%,.1)}.swagger-ui .stripe-dark:nth-child(odd){background-color:rgba(0,0,0,.1)}.swagger-ui .strike{text-decoration:line-through}.swagger-ui .underline{text-decoration:underline}.swagger-ui .no-underline{text-decoration:none}@media screen and (min-width:30em){.swagger-ui .strike-ns{text-decoration:line-through}.swagger-ui .underline-ns{text-decoration:underline}.swagger-ui .no-underline-ns{text-decoration:none}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .strike-m{text-decoration:line-through}.swagger-ui .underline-m{text-decoration:underline}.swagger-ui .no-underline-m{text-decoration:none}}@media screen and (min-width:60em){.swagger-ui .strike-l{text-decoration:line-through}.swagger-ui .underline-l{text-decoration:underline}.swagger-ui .no-underline-l{text-decoration:none}}.swagger-ui .tl{text-align:left}.swagger-ui .tr{text-align:right}.swagger-ui .tc{text-align:center}.swagger-ui .tj{text-align:justify}@media screen and (min-width:30em){.swagger-ui .tl-ns{text-align:left}.swagger-ui .tr-ns{text-align:right}.swagger-ui .tc-ns{text-align:center}.swagger-ui .tj-ns{text-align:justify}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .tl-m{text-align:left}.swagger-ui .tr-m{text-align:right}.swagger-ui .tc-m{text-align:center}.swagger-ui .tj-m{text-align:justify}}@media screen and (min-width:60em){.swagger-ui .tl-l{text-align:left}.swagger-ui .tr-l{text-align:right}.swagger-ui .tc-l{text-align:center}.swagger-ui .tj-l{text-align:justify}}.swagger-ui .ttc{text-transform:capitalize}.swagger-ui .ttl{text-transform:lowercase}.swagger-ui .ttu{text-transform:uppercase}.swagger-ui .ttn{text-transform:none}@media screen and (min-width:30em){.swagger-ui .ttc-ns{text-transform:capitalize}.swagger-ui .ttl-ns{text-transform:lowercase}.swagger-ui .ttu-ns{text-transform:uppercase}.swagger-ui .ttn-ns{text-transform:none}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .ttc-m{text-transform:capitalize}.swagger-ui .ttl-m{text-transform:lowercase}.swagger-ui .ttu-m{text-transform:uppercase}.swagger-ui .ttn-m{text-transform:none}}@media screen and (min-width:60em){.swagger-ui .ttc-l{text-transform:capitalize}.swagger-ui .ttl-l{text-transform:lowercase}.swagger-ui .ttu-l{text-transform:uppercase}.swagger-ui .ttn-l{text-transform:none}}.swagger-ui .f-6,.swagger-ui .f-headline{font-size:6rem}.swagger-ui .f-5,.swagger-ui .f-subheadline{font-size:5rem}.swagger-ui .f1{font-size:3rem}.swagger-ui .f2{font-size:2.25rem}.swagger-ui .f3{font-size:1.5rem}.swagger-ui .f4{font-size:1.25rem}.swagger-ui .f5{font-size:1rem}.swagger-ui .f6{font-size:.875rem}.swagger-ui .f7{font-size:.75rem}@media screen and (min-width:30em){.swagger-ui .f-6-ns,.swagger-ui .f-headline-ns{font-size:6rem}.swagger-ui .f-5-ns,.swagger-ui .f-subheadline-ns{font-size:5rem}.swagger-ui .f1-ns{font-size:3rem}.swagger-ui .f2-ns{font-size:2.25rem}.swagger-ui .f3-ns{font-size:1.5rem}.swagger-ui .f4-ns{font-size:1.25rem}.swagger-ui .f5-ns{font-size:1rem}.swagger-ui .f6-ns{font-size:.875rem}.swagger-ui .f7-ns{font-size:.75rem}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .f-6-m,.swagger-ui .f-headline-m{font-size:6rem}.swagger-ui .f-5-m,.swagger-ui .f-subheadline-m{font-size:5rem}.swagger-ui .f1-m{font-size:3rem}.swagger-ui .f2-m{font-size:2.25rem}.swagger-ui .f3-m{font-size:1.5rem}.swagger-ui .f4-m{font-size:1.25rem}.swagger-ui .f5-m{font-size:1rem}.swagger-ui .f6-m{font-size:.875rem}.swagger-ui .f7-m{font-size:.75rem}}@media screen and (min-width:60em){.swagger-ui .f-6-l,.swagger-ui .f-headline-l{font-size:6rem}.swagger-ui .f-5-l,.swagger-ui .f-subheadline-l{font-size:5rem}.swagger-ui .f1-l{font-size:3rem}.swagger-ui .f2-l{font-size:2.25rem}.swagger-ui .f3-l{font-size:1.5rem}.swagger-ui .f4-l{font-size:1.25rem}.swagger-ui .f5-l{font-size:1rem}.swagger-ui .f6-l{font-size:.875rem}.swagger-ui .f7-l{font-size:.75rem}}.swagger-ui .measure{max-width:30em}.swagger-ui .measure-wide{max-width:34em}.swagger-ui .measure-narrow{max-width:20em}.swagger-ui .indent{text-indent:1em;margin-top:0;margin-bottom:0}.swagger-ui .small-caps{font-variant:small-caps}.swagger-ui .truncate{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}@media screen and (min-width:30em){.swagger-ui .measure-ns{max-width:30em}.swagger-ui .measure-wide-ns{max-width:34em}.swagger-ui .measure-narrow-ns{max-width:20em}.swagger-ui .indent-ns{text-indent:1em;margin-top:0;margin-bottom:0}.swagger-ui .small-caps-ns{font-variant:small-caps}.swagger-ui .truncate-ns{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .measure-m{max-width:30em}.swagger-ui .measure-wide-m{max-width:34em}.swagger-ui .measure-narrow-m{max-width:20em}.swagger-ui .indent-m{text-indent:1em;margin-top:0;margin-bottom:0}.swagger-ui .small-caps-m{font-variant:small-caps}.swagger-ui .truncate-m{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}}@media screen and (min-width:60em){.swagger-ui .measure-l{max-width:30em}.swagger-ui .measure-wide-l{max-width:34em}.swagger-ui .measure-narrow-l{max-width:20em}.swagger-ui .indent-l{text-indent:1em;margin-top:0;margin-bottom:0}.swagger-ui .small-caps-l{font-variant:small-caps}.swagger-ui .truncate-l{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}}.swagger-ui .overflow-container{overflow-y:scroll}.swagger-ui .center{margin-right:auto;margin-left:auto}.swagger-ui .mr-auto{margin-right:auto}.swagger-ui .ml-auto{margin-left:auto}@media screen and (min-width:30em){.swagger-ui .center-ns{margin-right:auto;margin-left:auto}.swagger-ui .mr-auto-ns{margin-right:auto}.swagger-ui .ml-auto-ns{margin-left:auto}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .center-m{margin-right:auto;margin-left:auto}.swagger-ui .mr-auto-m{margin-right:auto}.swagger-ui .ml-auto-m{margin-left:auto}}@media screen and (min-width:60em){.swagger-ui .center-l{margin-right:auto;margin-left:auto}.swagger-ui .mr-auto-l{margin-right:auto}.swagger-ui .ml-auto-l{margin-left:auto}}.swagger-ui .clip{position:fixed!important;_position:absolute!important;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}@media screen and (min-width:30em){.swagger-ui .clip-ns{position:fixed!important;_position:absolute!important;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .clip-m{position:fixed!important;_position:absolute!important;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}}@media screen and (min-width:60em){.swagger-ui .clip-l{position:fixed!important;_position:absolute!important;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}}.swagger-ui .ws-normal{white-space:normal}.swagger-ui .nowrap{white-space:nowrap}.swagger-ui .pre{white-space:pre}@media screen and (min-width:30em){.swagger-ui .ws-normal-ns{white-space:normal}.swagger-ui .nowrap-ns{white-space:nowrap}.swagger-ui .pre-ns{white-space:pre}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .ws-normal-m{white-space:normal}.swagger-ui .nowrap-m{white-space:nowrap}.swagger-ui .pre-m{white-space:pre}}@media screen and (min-width:60em){.swagger-ui .ws-normal-l{white-space:normal}.swagger-ui .nowrap-l{white-space:nowrap}.swagger-ui .pre-l{white-space:pre}}.swagger-ui .v-base{vertical-align:baseline}.swagger-ui .v-mid{vertical-align:middle}.swagger-ui .v-top{vertical-align:top}.swagger-ui .v-btm{vertical-align:bottom}@media screen and (min-width:30em){.swagger-ui .v-base-ns{vertical-align:baseline}.swagger-ui .v-mid-ns{vertical-align:middle}.swagger-ui .v-top-ns{vertical-align:top}.swagger-ui .v-btm-ns{vertical-align:bottom}}@media screen and (min-width:30em) and (max-width:60em){.swagger-ui .v-base-m{vertical-align:baseline}.swagger-ui .v-mid-m{vertical-align:middle}.swagger-ui .v-top-m{vertical-align:top}.swagger-ui .v-btm-m{vertical-align:bottom}}@media screen and (min-width:60em){.swagger-ui .v-base-l{vertical-align:baseline}.swagger-ui .v-mid-l{vertical-align:middle}.swagger-ui .v-top-l{vertical-align:top}.swagger-ui .v-btm-l{vertical-align:bottom}}.swagger-ui .dim{opacity:1;transition:opacity .15s ease-in}.swagger-ui .dim:focus,.swagger-ui .dim:hover{opacity:.5;transition:opacity .15s ease-in}.swagger-ui .dim:active{opacity:.8;transition:opacity .15s ease-out}.swagger-ui .glow{transition:opacity .15s ease-in}.swagger-ui .glow:focus,.swagger-ui .glow:hover{opacity:1;transition:opacity .15s ease-in}.swagger-ui .hide-child .child{opacity:0;transition:opacity .15s ease-in}.swagger-ui .hide-child:active .child,.swagger-ui .hide-child:focus .child,.swagger-ui .hide-child:hover .child{opacity:1;transition:opacity .15s ease-in}.swagger-ui .underline-hover:focus,.swagger-ui .underline-hover:hover{text-decoration:underline}.swagger-ui .grow{-moz-osx-font-smoothing:grayscale;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform:translateZ(0);transform:translateZ(0);transition:-webkit-transform .25s ease-out;transition:transform .25s ease-out;transition:transform .25s ease-out, -webkit-transform .25s ease-out}.swagger-ui .grow:focus,.swagger-ui .grow:hover{-webkit-transform:scale(1.05);transform:scale(1.05)}.swagger-ui .grow:active{-webkit-transform:scale(.9);transform:scale(.9)}.swagger-ui .grow-large{-moz-osx-font-smoothing:grayscale;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform:translateZ(0);transform:translateZ(0);transition:-webkit-transform .25s ease-in-out;transition:transform .25s ease-in-out;transition:transform .25s ease-in-out, -webkit-transform .25s ease-in-out}.swagger-ui .grow-large:focus,.swagger-ui .grow-large:hover{-webkit-transform:scale(1.2);transform:scale(1.2)}.swagger-ui .grow-large:active{-webkit-transform:scale(.95);transform:scale(.95)}.swagger-ui .pointer:hover{cursor:pointer}.swagger-ui .shadow-hover{cursor:pointer;position:relative;transition:all .5s cubic-bezier(.165,.84,.44,1)}.swagger-ui .shadow-hover:after{content:"";box-shadow:0 0 16px 2px rgba(0,0,0,.2);border-radius:inherit;opacity:0;position:absolute;top:0;left:0;width:100%;height:100%;z-index:-1;transition:opacity .5s cubic-bezier(.165,.84,.44,1)}.swagger-ui .shadow-hover:focus:after,.swagger-ui .shadow-hover:hover:after{opacity:1}.swagger-ui .bg-animate,.swagger-ui .bg-animate:focus,.swagger-ui .bg-animate:hover{transition:background-color .15s ease-in-out}.swagger-ui .z-0{z-index:0}.swagger-ui .z-1{z-index:1}.swagger-ui .z-2{z-index:2}.swagger-ui .z-3{z-index:3}.swagger-ui .z-4{z-index:4}.swagger-ui .z-5{z-index:5}.swagger-ui .z-999{z-index:999}.swagger-ui .z-9999{z-index:9999}.swagger-ui .z-max{z-index:2147483647}.swagger-ui .z-inherit{z-index:inherit}.swagger-ui .z-initial{z-index:auto}.swagger-ui .z-unset{z-index:unset}.swagger-ui .nested-copy-line-height ol,.swagger-ui .nested-copy-line-height p,.swagger-ui .nested-copy-line-height ul{line-height:1.5}.swagger-ui .nested-headline-line-height h1,.swagger-ui .nested-headline-line-height h2,.swagger-ui .nested-headline-line-height h3,.swagger-ui .nested-headline-line-height h4,.swagger-ui .nested-headline-line-height h5,.swagger-ui .nested-headline-line-height h6{line-height:1.25}.swagger-ui .nested-list-reset ol,.swagger-ui .nested-list-reset ul{padding-left:0;margin-left:0;list-style-type:none}.swagger-ui .nested-copy-indent p+p{text-indent:.1em;margin-top:0;margin-bottom:0}.swagger-ui .nested-copy-seperator p+p{margin-top:1.5em}.swagger-ui .nested-img img{width:100%;max-width:100%;display:block}.swagger-ui .nested-links a{color:#357edd;transition:color .15s ease-in}.swagger-ui .nested-links a:focus,.swagger-ui .nested-links a:hover{color:#96ccff;transition:color .15s ease-in}.swagger-ui .wrapper{width:100%;max-width:1460px;margin:0 auto;padding:0 20px;box-sizing:border-box}.swagger-ui .opblock-tag-section{display:flex;flex-direction:column}.swagger-ui .opblock-tag{display:flex;align-items:center;padding:10px 20px 10px 10px;cursor:pointer;transition:all .2s;border-bottom:1px solid rgba(59,65,81,.3)}.swagger-ui .opblock-tag:hover{background:rgba(0,0,0,.02)}.swagger-ui .opblock-tag{font-size:24px;margin:0 0 5px;font-family:sans-serif;color:#3b4151}.swagger-ui .opblock-tag.no-desc span{flex:1}.swagger-ui .opblock-tag svg{transition:all .4s}.swagger-ui .opblock-tag small{font-size:14px;font-weight:400;flex:1;padding:0 10px;font-family:sans-serif;color:#3b4151}.swagger-ui .parameter__type{font-size:12px;padding:5px 0;font-family:monospace;font-weight:600;color:#3b4151}.swagger-ui .parameter-controls{margin-top:.75em}.swagger-ui .examples__title{display:block;font-size:1.1em;font-weight:700;margin-bottom:.75em}.swagger-ui .examples__section{margin-top:1.5em}.swagger-ui .examples__section-header{font-weight:700;font-size:.9rem;margin-bottom:.5rem}.swagger-ui .examples-select{margin-bottom:.75em}.swagger-ui .examples-select__section-label{font-weight:700;font-size:.9rem;margin-right:.5rem}.swagger-ui .example__section{margin-top:1.5em}.swagger-ui .example__section-header{font-weight:700;font-size:.9rem;margin-bottom:.5rem}.swagger-ui .view-line-link{position:relative;top:3px;width:20px;margin:0 5px;cursor:pointer;transition:all .5s}.swagger-ui .opblock{margin:0 0 15px;border:1px solid #000;border-radius:4px;box-shadow:0 0 3px rgba(0,0,0,.19)}.swagger-ui .opblock .tab-header{display:flex;flex:1}.swagger-ui .opblock .tab-header .tab-item{padding:0 40px;cursor:pointer}.swagger-ui .opblock .tab-header .tab-item:first-of-type{padding:0 40px 0 0}.swagger-ui .opblock .tab-header .tab-item.active h4 span{position:relative}.swagger-ui .opblock .tab-header .tab-item.active h4 span:after{position:absolute;bottom:-15px;left:50%;width:120%;height:4px;content:"";-webkit-transform:translateX(-50%);transform:translateX(-50%);background:grey}.swagger-ui .opblock.is-open .opblock-summary{border-bottom:1px solid #000}.swagger-ui .opblock .opblock-section-header{display:flex;align-items:center;padding:8px 20px;min-height:50px;background:hsla(0,0%,100%,.8);box-shadow:0 1px 2px rgba(0,0,0,.1)}.swagger-ui .opblock .opblock-section-header>label{font-size:12px;font-weight:700;display:flex;align-items:center;margin:0 0 0 auto;font-family:sans-serif;color:#3b4151}.swagger-ui .opblock .opblock-section-header>label>span{padding:0 10px 0 0}.swagger-ui .opblock .opblock-section-header h4{font-size:14px;flex:1;margin:0;font-family:sans-serif;color:#3b4151}.swagger-ui .opblock .opblock-summary-method{font-size:14px;font-weight:700;min-width:80px;padding:6px 15px;text-align:center;border-radius:3px;background:#000;text-shadow:0 1px 0 rgba(0,0,0,.1);font-family:sans-serif;color:#fff}.swagger-ui .opblock .opblock-summary-operation-id,.swagger-ui .opblock .opblock-summary-path,.swagger-ui .opblock .opblock-summary-path__deprecated{font-size:16px;display:flex;align-items:center;word-break:break-word;padding:0 10px;font-family:monospace;font-weight:600;color:#3b4151}@media (max-width:768px){.swagger-ui .opblock .opblock-summary-operation-id,.swagger-ui .opblock .opblock-summary-path,.swagger-ui .opblock .opblock-summary-path__deprecated{font-size:12px}}.swagger-ui .opblock .opblock-summary-path__deprecated{text-decoration:line-through}.swagger-ui .opblock .opblock-summary-operation-id{font-size:14px}.swagger-ui .opblock .opblock-summary-description{font-size:13px;flex:1 1 auto;word-break:break-word;font-family:sans-serif;color:#3b4151}.swagger-ui .opblock .opblock-summary{display:flex;align-items:center;padding:5px;cursor:pointer}.swagger-ui .opblock .opblock-summary .view-line-link{position:relative;top:2px;width:0;margin:0;cursor:pointer;transition:all .5s}.swagger-ui .opblock .opblock-summary:hover .view-line-link{width:18px;margin:0 5px}.swagger-ui .opblock.opblock-post{border-color:#49cc90;background:rgba(73,204,144,.1)}.swagger-ui .opblock.opblock-post .opblock-summary-method{background:#49cc90}.swagger-ui .opblock.opblock-post .opblock-summary{border-color:#49cc90}.swagger-ui .opblock.opblock-post .tab-header .tab-item.active h4 span:after{background:#49cc90}.swagger-ui .opblock.opblock-put{border-color:#fca130;background:rgba(252,161,48,.1)}.swagger-ui .opblock.opblock-put .opblock-summary-method{background:#fca130}.swagger-ui .opblock.opblock-put .opblock-summary{border-color:#fca130}.swagger-ui .opblock.opblock-put .tab-header .tab-item.active h4 span:after{background:#fca130}.swagger-ui .opblock.opblock-delete{border-color:#f93e3e;background:rgba(249,62,62,.1)}.swagger-ui .opblock.opblock-delete .opblock-summary-method{background:#f93e3e}.swagger-ui .opblock.opblock-delete .opblock-summary{border-color:#f93e3e}.swagger-ui .opblock.opblock-delete .tab-header .tab-item.active h4 span:after{background:#f93e3e}.swagger-ui .opblock.opblock-get{border-color:#61affe;background:rgba(97,175,254,.1)}.swagger-ui .opblock.opblock-get .opblock-summary-method{background:#61affe}.swagger-ui .opblock.opblock-get .opblock-summary{border-color:#61affe}.swagger-ui .opblock.opblock-get .tab-header .tab-item.active h4 span:after{background:#61affe}.swagger-ui .opblock.opblock-patch{border-color:#50e3c2;background:rgba(80,227,194,.1)}.swagger-ui .opblock.opblock-patch .opblock-summary-method{background:#50e3c2}.swagger-ui .opblock.opblock-patch .opblock-summary{border-color:#50e3c2}.swagger-ui .opblock.opblock-patch .tab-header .tab-item.active h4 span:after{background:#50e3c2}.swagger-ui .opblock.opblock-head{border-color:#9012fe;background:rgba(144,18,254,.1)}.swagger-ui .opblock.opblock-head .opblock-summary-method{background:#9012fe}.swagger-ui .opblock.opblock-head .opblock-summary{border-color:#9012fe}.swagger-ui .opblock.opblock-head .tab-header .tab-item.active h4 span:after{background:#9012fe}.swagger-ui .opblock.opblock-options{border-color:#0d5aa7;background:rgba(13,90,167,.1)}.swagger-ui .opblock.opblock-options .opblock-summary-method{background:#0d5aa7}.swagger-ui .opblock.opblock-options .opblock-summary{border-color:#0d5aa7}.swagger-ui .opblock.opblock-options .tab-header .tab-item.active h4 span:after{background:#0d5aa7}.swagger-ui .opblock.opblock-deprecated{opacity:.6;border-color:#ebebeb;background:hsla(0,0%,92.2%,.1)}.swagger-ui .opblock.opblock-deprecated .opblock-summary-method{background:#ebebeb}.swagger-ui .opblock.opblock-deprecated .opblock-summary{border-color:#ebebeb}.swagger-ui .opblock.opblock-deprecated .tab-header .tab-item.active h4 span:after{background:#ebebeb}.swagger-ui .opblock .opblock-schemes{padding:8px 20px}.swagger-ui .opblock .opblock-schemes .schemes-title{padding:0 10px 0 0}.swagger-ui .filter .operation-filter-input{width:100%;margin:20px 0;padding:10px;border:2px solid #d8dde7}.swagger-ui .model-example{margin-top:1em}.swagger-ui .tab{display:flex;padding:0;list-style:none}.swagger-ui .tab li{font-size:12px;min-width:60px;padding:0;cursor:pointer;font-family:sans-serif;color:#3b4151}.swagger-ui .tab li:first-of-type{position:relative;padding-left:0;padding-right:12px}.swagger-ui .tab li:first-of-type:after{position:absolute;top:0;right:6px;width:1px;height:100%;content:"";background:rgba(0,0,0,.2)}.swagger-ui .tab li.active{font-weight:700}.swagger-ui .opblock-description-wrapper,.swagger-ui .opblock-external-docs-wrapper,.swagger-ui .opblock-title_normal{font-size:12px;margin:0 0 5px;padding:15px 20px;font-family:sans-serif;color:#3b4151}.swagger-ui .opblock-description-wrapper h4,.swagger-ui .opblock-external-docs-wrapper h4,.swagger-ui .opblock-title_normal h4{font-size:12px;margin:0 0 5px;font-family:sans-serif;color:#3b4151}.swagger-ui .opblock-description-wrapper p,.swagger-ui .opblock-external-docs-wrapper p,.swagger-ui .opblock-title_normal p{font-size:14px;margin:0;font-family:sans-serif;color:#3b4151}.swagger-ui .opblock-external-docs-wrapper h4{padding-left:0}.swagger-ui .execute-wrapper{padding:20px;text-align:right}.swagger-ui .execute-wrapper .btn{width:100%;padding:8px 40px}.swagger-ui .body-param-options{display:flex;flex-direction:column}.swagger-ui .body-param-options .body-param-edit{padding:10px 0}.swagger-ui .body-param-options label{padding:8px 0}.swagger-ui .body-param-options label select{margin:3px 0 0}.swagger-ui .responses-inner{padding:20px}.swagger-ui .responses-inner h4,.swagger-ui .responses-inner h5{font-size:12px;margin:10px 0 5px;font-family:sans-serif;color:#3b4151}.swagger-ui .response-col_status{font-size:14px;font-family:sans-serif;color:#3b4151}.swagger-ui .response-col_status .response-undocumented{font-size:11px;font-family:monospace;font-weight:600;color:#909090}.swagger-ui .response-col_links{padding-left:2em;max-width:40em;font-size:14px;font-family:sans-serif;color:#3b4151}.swagger-ui .response-col_links .response-undocumented{font-size:11px;font-family:monospace;font-weight:600;color:#909090}.swagger-ui .opblock-body .opblock-loading-animation{display:block;margin:3em auto}.swagger-ui .opblock-body pre.microlight{font-size:12px;margin:0;padding:10px;white-space:pre-wrap;word-wrap:break-word;word-break:break-all;word-break:break-word;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto;border-radius:4px;background:#41444e;overflow-wrap:break-word;font-family:monospace;font-weight:600;color:#fff}.swagger-ui .opblock-body pre.microlight span{color:#fff!important}.swagger-ui .opblock-body pre.microlight .headerline{display:block}.swagger-ui .highlight-code{position:relative}.swagger-ui .highlight-code>.microlight{overflow-y:auto;max-height:400px;min-height:6em}.swagger-ui .download-contents{position:absolute;bottom:10px;right:10px;cursor:pointer;background:#7d8293;text-align:center;padding:5px;border-radius:4px;font-family:sans-serif;font-weight:600;color:#fff;font-size:14px;height:30px;width:75px}.swagger-ui .scheme-container{margin:0 0 20px;padding:30px 0;background:#fff;box-shadow:0 1px 2px 0 rgba(0,0,0,.15)}.swagger-ui .scheme-container .schemes{display:flex;align-items:flex-end}.swagger-ui .scheme-container .schemes>label{font-size:12px;font-weight:700;display:flex;flex-direction:column;margin:-20px 15px 0 0;font-family:sans-serif;color:#3b4151}.swagger-ui .scheme-container .schemes>label select{min-width:130px;text-transform:uppercase}.swagger-ui .loading-container{padding:40px 0 60px;margin-top:1em;min-height:1px;display:flex;justify-content:center;align-items:center;flex-direction:column}.swagger-ui .loading-container .loading{position:relative}.swagger-ui .loading-container .loading:after{font-size:10px;font-weight:700;position:absolute;top:50%;left:50%;content:"loading";-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);text-transform:uppercase;font-family:sans-serif;color:#3b4151}.swagger-ui .loading-container .loading:before{position:absolute;top:50%;left:50%;display:block;width:60px;height:60px;margin:-30px;content:"";-webkit-animation:rotation 1s linear infinite,opacity .5s;animation:rotation 1s linear infinite,opacity .5s;opacity:1;border:2px solid rgba(85,85,85,.1);border-top-color:rgba(0,0,0,.6);border-radius:100%;-webkit-backface-visibility:hidden;backface-visibility:hidden}@-webkit-keyframes rotation{to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes rotation{to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.swagger-ui .response-controls{padding-top:1em;display:flex}.swagger-ui .response-control-media-type{margin-right:1em}.swagger-ui .response-control-media-type--accept-controller select{border-color:green}.swagger-ui .response-control-media-type__accept-message{color:green;font-size:.7em}.swagger-ui .response-control-examples__title,.swagger-ui .response-control-media-type__title{display:block;margin-bottom:.2em;font-size:.7em}@-webkit-keyframes blinker{50%{opacity:0}}@keyframes blinker{50%{opacity:0}}.swagger-ui section h3{font-family:sans-serif;color:#3b4151}.swagger-ui a.nostyle{display:inline}.swagger-ui a.nostyle,.swagger-ui a.nostyle:visited{text-decoration:inherit;color:inherit;cursor:pointer}.swagger-ui .version-pragma{height:100%;padding:5em 0}.swagger-ui .version-pragma__message{display:flex;justify-content:center;height:100%;font-size:1.2em;text-align:center;line-height:1.5em;padding:0 .6em}.swagger-ui .version-pragma__message>div{max-width:55ch;flex:1}.swagger-ui .version-pragma__message code{background-color:#dedede;padding:4px 4px 2px;white-space:pre}.swagger-ui .btn{font-size:14px;font-weight:700;padding:5px 23px;transition:all .3s;border:2px solid grey;border-radius:4px;background:transparent;box-shadow:0 1px 2px rgba(0,0,0,.1);font-family:sans-serif;color:#3b4151}.swagger-ui .btn.btn-sm{font-size:12px;padding:4px 23px}.swagger-ui .btn[disabled]{cursor:not-allowed;opacity:.3}.swagger-ui .btn:hover{box-shadow:0 0 5px rgba(0,0,0,.3)}.swagger-ui .btn.cancel{border-color:#ff6060;background-color:transparent;font-family:sans-serif;color:#ff6060}.swagger-ui .btn.authorize{line-height:1;display:inline;color:#49cc90;border-color:#49cc90;background-color:transparent}.swagger-ui .btn.authorize span{float:left;padding:4px 20px 0 0}.swagger-ui .btn.authorize svg{fill:#49cc90}.swagger-ui .btn.execute{background-color:#4990e2;color:#fff;border-color:#4990e2}.swagger-ui .btn-group{display:flex;padding:30px}.swagger-ui .btn-group .btn{flex:1}.swagger-ui .btn-group .btn:first-child{border-radius:4px 0 0 4px}.swagger-ui .btn-group .btn:last-child{border-radius:0 4px 4px 0}.swagger-ui .authorization__btn{padding:0 10px;border:none;background:none}.swagger-ui .authorization__btn.locked{opacity:1}.swagger-ui .authorization__btn.unlocked{opacity:.4}.swagger-ui .expand-methods,.swagger-ui .expand-operation{border:none;background:none}.swagger-ui .expand-methods svg,.swagger-ui .expand-operation svg{width:20px;height:20px}.swagger-ui .expand-methods{padding:0 10px}.swagger-ui .expand-methods:hover svg{fill:#404040}.swagger-ui .expand-methods svg{transition:all .3s;fill:#707070}.swagger-ui button{cursor:pointer;outline:none}.swagger-ui button.invalid{-webkit-animation:shake .4s 1;animation:shake .4s 1;border-color:#f93e3e;background:#feebeb}.swagger-ui select{font-size:14px;font-weight:700;padding:5px 40px 5px 10px;border:2px solid #41444e;border-radius:4px;background:#f7f7f7 url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMCAyMCI+PHBhdGggZD0iTTEzLjQxOCA3Ljg1OWEuNjk1LjY5NSAwIDAxLjk3OCAwIC42OC42OCAwIDAxMCAuOTY5bC0zLjkwOCAzLjgzYS42OTcuNjk3IDAgMDEtLjk3OSAwbC0zLjkwOC0zLjgzYS42OC42OCAwIDAxMC0uOTY5LjY5NS42OTUgMCAwMS45NzggMEwxMCAxMWwzLjQxOC0zLjE0MXoiLz48L3N2Zz4=) right 10px center no-repeat;background-size:20px;box-shadow:0 1px 2px 0 rgba(0,0,0,.25);font-family:sans-serif;color:#3b4151;-webkit-appearance:none;-moz-appearance:none;appearance:none}.swagger-ui select[multiple]{margin:5px 0;padding:5px;background:#f7f7f7}.swagger-ui select.invalid{-webkit-animation:shake .4s 1;animation:shake .4s 1;border-color:#f93e3e;background:#feebeb}.swagger-ui .opblock-body select{min-width:230px}@media (max-width:768px){.swagger-ui .opblock-body select{min-width:180px}}.swagger-ui label{font-size:12px;font-weight:700;margin:0 0 5px;font-family:sans-serif;color:#3b4151}.swagger-ui input[type=email],.swagger-ui input[type=file],.swagger-ui input[type=password],.swagger-ui input[type=search],.swagger-ui input[type=text],.swagger-ui textarea{min-width:100px;margin:5px 0;padding:8px 10px;border:1px solid #d9d9d9;border-radius:4px;background:#fff}@media (max-width:768px){.swagger-ui input[type=email],.swagger-ui input[type=file],.swagger-ui input[type=password],.swagger-ui input[type=search],.swagger-ui input[type=text],.swagger-ui textarea{max-width:175px}}.swagger-ui input[type=email].invalid,.swagger-ui input[type=file].invalid,.swagger-ui input[type=password].invalid,.swagger-ui input[type=search].invalid,.swagger-ui input[type=text].invalid,.swagger-ui textarea.invalid{-webkit-animation:shake .4s 1;animation:shake .4s 1;border-color:#f93e3e;background:#feebeb}.swagger-ui input[disabled],.swagger-ui select[disabled],.swagger-ui textarea[disabled]{background-color:#fafafa;color:#888;cursor:not-allowed}.swagger-ui select[disabled]{border-color:#888}.swagger-ui textarea[disabled]{background-color:#41444e;color:#fff}@-webkit-keyframes shake{10%,90%{-webkit-transform:translate3d(-1px,0,0);transform:translate3d(-1px,0,0)}20%,80%{-webkit-transform:translate3d(2px,0,0);transform:translate3d(2px,0,0)}30%,50%,70%{-webkit-transform:translate3d(-4px,0,0);transform:translate3d(-4px,0,0)}40%,60%{-webkit-transform:translate3d(4px,0,0);transform:translate3d(4px,0,0)}}@keyframes shake{10%,90%{-webkit-transform:translate3d(-1px,0,0);transform:translate3d(-1px,0,0)}20%,80%{-webkit-transform:translate3d(2px,0,0);transform:translate3d(2px,0,0)}30%,50%,70%{-webkit-transform:translate3d(-4px,0,0);transform:translate3d(-4px,0,0)}40%,60%{-webkit-transform:translate3d(4px,0,0);transform:translate3d(4px,0,0)}}.swagger-ui textarea{font-size:12px;width:100%;min-height:280px;padding:10px;border:none;border-radius:4px;outline:none;background:hsla(0,0%,100%,.8);font-family:monospace;font-weight:600;color:#3b4151}.swagger-ui textarea:focus{border:2px solid #61affe}.swagger-ui textarea.curl{font-size:12px;min-height:100px;margin:0;padding:10px;resize:none;border-radius:4px;background:#41444e;font-family:monospace;font-weight:600;color:#fff}.swagger-ui .checkbox{padding:5px 0 10px;transition:opacity .5s;color:#303030}.swagger-ui .checkbox label{display:flex}.swagger-ui .checkbox p{font-weight:400!important;font-style:italic;margin:0!important;font-family:monospace;font-weight:600;color:#3b4151}.swagger-ui .checkbox input[type=checkbox]{display:none}.swagger-ui .checkbox input[type=checkbox]+label>.item{position:relative;top:3px;display:inline-block;width:16px;height:16px;margin:0 8px 0 0;padding:5px;cursor:pointer;border-radius:1px;background:#e8e8e8;box-shadow:0 0 0 2px #e8e8e8;flex:none}.swagger-ui .checkbox input[type=checkbox]+label>.item:active{-webkit-transform:scale(.9);transform:scale(.9)}.swagger-ui .checkbox input[type=checkbox]:checked+label>.item{background:#e8e8e8 url("data:image/svg+xml;charset=utf-8,%3Csvg width='10' height='8' viewBox='3 7 10 8' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%2341474E' fill-rule='evenodd' d='M6.333 15L3 11.667l1.333-1.334 2 2L11.667 7 13 8.333z'/%3E%3C/svg%3E") 50% no-repeat}.swagger-ui .dialog-ux{position:fixed;z-index:9999;top:0;right:0;bottom:0;left:0}.swagger-ui .dialog-ux .backdrop-ux{position:fixed;top:0;right:0;bottom:0;left:0;background:rgba(0,0,0,.8)}.swagger-ui .dialog-ux .modal-ux{position:absolute;z-index:9999;top:50%;left:50%;width:100%;min-width:300px;max-width:650px;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);border:1px solid #ebebeb;border-radius:4px;background:#fff;box-shadow:0 10px 30px 0 rgba(0,0,0,.2)}.swagger-ui .dialog-ux .modal-ux-content{overflow-y:auto;max-height:540px;padding:20px}.swagger-ui .dialog-ux .modal-ux-content p{font-size:12px;margin:0 0 5px;color:#41444e;font-family:sans-serif;color:#3b4151}.swagger-ui .dialog-ux .modal-ux-content h4{font-size:18px;font-weight:600;margin:15px 0 0;font-family:sans-serif;color:#3b4151}.swagger-ui .dialog-ux .modal-ux-header{display:flex;padding:12px 0;border-bottom:1px solid #ebebeb;align-items:center}.swagger-ui .dialog-ux .modal-ux-header .close-modal{padding:0 10px;border:none;background:none;-webkit-appearance:none;-moz-appearance:none;appearance:none}.swagger-ui .dialog-ux .modal-ux-header h3{font-size:20px;font-weight:600;margin:0;padding:0 20px;flex:1;font-family:sans-serif;color:#3b4151}.swagger-ui .model{font-size:12px;font-weight:300;font-family:monospace;font-weight:600;color:#3b4151}.swagger-ui .model .deprecated span,.swagger-ui .model .deprecated td{color:#a0a0a0!important}.swagger-ui .model .deprecated>td:first-of-type{text-decoration:line-through}.swagger-ui .model-toggle{font-size:10px;position:relative;top:6px;display:inline-block;margin:auto .3em;cursor:pointer;transition:-webkit-transform .15s ease-in;transition:transform .15s ease-in;transition:transform .15s ease-in, -webkit-transform .15s ease-in;-webkit-transform:rotate(90deg);transform:rotate(90deg);-webkit-transform-origin:50% 50%;transform-origin:50% 50%}.swagger-ui .model-toggle.collapsed{-webkit-transform:rotate(0deg);transform:rotate(0deg)}.swagger-ui .model-toggle:after{display:block;width:20px;height:20px;content:"";background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Cpath d='M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z'/%3E%3C/svg%3E") 50% no-repeat;background-size:100%}.swagger-ui .model-jump-to-path{position:relative;cursor:pointer}.swagger-ui .model-jump-to-path .view-line-link{position:absolute;top:-.4em;cursor:pointer}.swagger-ui .model-title{position:relative}.swagger-ui .model-title:hover .model-hint{visibility:visible}.swagger-ui .model-hint{position:absolute;top:-1.8em;visibility:hidden;padding:.1em .5em;white-space:nowrap;color:#ebebeb;border-radius:4px;background:rgba(0,0,0,.7)}.swagger-ui .model p{margin:0 0 1em}.swagger-ui section.models{margin:30px 0;border:1px solid rgba(59,65,81,.3);border-radius:4px}.swagger-ui section.models.is-open{padding:0 0 20px}.swagger-ui section.models.is-open h4{margin:0 0 5px;border-bottom:1px solid rgba(59,65,81,.3)}.swagger-ui section.models h4{font-size:16px;display:flex;align-items:center;margin:0;padding:10px 20px 10px 10px;cursor:pointer;transition:all .2s;font-family:sans-serif;color:#606060}.swagger-ui section.models h4 svg{transition:all .4s}.swagger-ui section.models h4 span{flex:1}.swagger-ui section.models h4:hover{background:rgba(0,0,0,.02)}.swagger-ui section.models h5{font-size:16px;margin:0 0 10px;font-family:sans-serif;color:#707070}.swagger-ui section.models .model-jump-to-path{position:relative;top:5px}.swagger-ui section.models .model-container{margin:0 20px 15px;position:relative;transition:all .5s;border-radius:4px;background:rgba(0,0,0,.05)}.swagger-ui section.models .model-container:hover{background:rgba(0,0,0,.07)}.swagger-ui section.models .model-container:first-of-type{margin:20px}.swagger-ui section.models .model-container:last-of-type{margin:0 20px}.swagger-ui section.models .model-container .models-jump-to-path{position:absolute;top:8px;right:5px;opacity:.65}.swagger-ui section.models .model-box{background:none}.swagger-ui .model-box{padding:10px;display:inline-block;border-radius:4px;background:rgba(0,0,0,.1)}.swagger-ui .model-box .model-jump-to-path{position:relative;top:4px}.swagger-ui .model-box.deprecated{opacity:.5}.swagger-ui .model-title{font-size:16px;font-family:sans-serif;color:#505050}.swagger-ui .model-deprecated-warning{font-size:16px;font-weight:600;margin-right:1em;font-family:sans-serif;color:#f93e3e}.swagger-ui span>span.model .brace-close{padding:0 0 0 10px}.swagger-ui .prop-name{display:inline-block;margin-right:1em}.swagger-ui .prop-type{color:#55a}.swagger-ui .prop-enum{display:block}.swagger-ui .prop-format{color:#606060}.swagger-ui .servers>label{font-size:12px;margin:-20px 15px 0 0;font-family:sans-serif;color:#3b4151}.swagger-ui .servers>label select{min-width:130px;max-width:100%}.swagger-ui .servers h4.message{padding-bottom:2em}.swagger-ui .servers table tr{width:30em}.swagger-ui .servers table td{display:inline-block;max-width:15em;vertical-align:middle;padding-top:10px;padding-bottom:10px}.swagger-ui .servers table td:first-of-type{padding-right:2em}.swagger-ui .servers table td input{width:100%;height:100%}.swagger-ui .servers .computed-url{margin:2em 0}.swagger-ui .servers .computed-url code{display:inline-block;padding:4px;font-size:16px;margin:0 1em}.swagger-ui .servers-title{font-size:12px;font-weight:700}.swagger-ui .operation-servers h4.message{margin-bottom:2em}.swagger-ui table{width:100%;padding:0 10px;border-collapse:collapse}.swagger-ui table.model tbody tr td{padding:0;vertical-align:top}.swagger-ui table.model tbody tr td:first-of-type{width:174px;padding:0 0 0 2em}.swagger-ui table.headers td{font-size:12px;font-weight:300;vertical-align:middle;font-family:monospace;font-weight:600;color:#3b4151}.swagger-ui table tbody tr td{padding:10px 0 0;vertical-align:top}.swagger-ui table tbody tr td:first-of-type{max-width:20%;min-width:6em;padding:10px 0}.swagger-ui table thead tr td,.swagger-ui table thead tr th{font-size:12px;font-weight:700;padding:12px 0;text-align:left;border-bottom:1px solid rgba(59,65,81,.2);font-family:sans-serif;color:#3b4151}.swagger-ui .parameters-col_description{width:99%;margin-bottom:2em}.swagger-ui .parameters-col_description input[type=text]{width:100%;max-width:340px}.swagger-ui .parameters-col_description select{border-width:1px}.swagger-ui .parameter__name{font-size:16px;font-weight:400;margin-right:.75em;font-family:sans-serif;color:#3b4151}.swagger-ui .parameter__name.required{font-weight:700}.swagger-ui .parameter__name.required:after{font-size:10px;position:relative;top:-6px;padding:5px;content:"required";color:rgba(255,0,0,.6)}.swagger-ui .parameter__extension,.swagger-ui .parameter__in{font-size:12px;font-style:italic;font-family:monospace;font-weight:600;color:grey}.swagger-ui .parameter__deprecated{font-size:12px;font-style:italic;font-family:monospace;font-weight:600;color:red}.swagger-ui .parameter__empty_value_toggle{font-size:13px;padding-top:5px;padding-bottom:12px}.swagger-ui .parameter__empty_value_toggle input{margin-right:7px}.swagger-ui .parameter__empty_value_toggle.disabled{opacity:.7}.swagger-ui .table-container{padding:20px}.swagger-ui .response-col_description{width:99%}.swagger-ui .response-col_links{min-width:6em}.swagger-ui .topbar{padding:10px 0;background-color:#1b1b1b}.swagger-ui .topbar .topbar-wrapper,.swagger-ui .topbar a{display:flex;align-items:center}.swagger-ui .topbar a{font-size:1.5em;font-weight:700;flex:1;max-width:300px;text-decoration:none;font-family:sans-serif;color:#fff}.swagger-ui .topbar a span{margin:0;padding:0 10px}.swagger-ui .topbar .download-url-wrapper{display:flex;flex:3;justify-content:flex-end}.swagger-ui .topbar .download-url-wrapper input[type=text]{width:100%;margin:0;border:2px solid #62a03f;border-radius:4px 0 0 4px;outline:none}.swagger-ui .topbar .download-url-wrapper .select-label{display:flex;align-items:center;width:100%;max-width:600px;margin:0;color:#f0f0f0}.swagger-ui .topbar .download-url-wrapper .select-label span{font-size:16px;flex:1;padding:0 10px 0 0;text-align:right}.swagger-ui .topbar .download-url-wrapper .select-label select{flex:2;width:100%;border:2px solid #62a03f;outline:none;box-shadow:none}.swagger-ui .topbar .download-url-wrapper .download-url-button{font-size:16px;font-weight:700;padding:4px 30px;border:none;border-radius:0 4px 4px 0;background:#62a03f;font-family:sans-serif;color:#fff}.swagger-ui .info{margin:50px 0}.swagger-ui .info hgroup.main{margin:0 0 20px}.swagger-ui .info hgroup.main a{font-size:12px}.swagger-ui .info pre{font-size:14px}.swagger-ui .info li,.swagger-ui .info p,.swagger-ui .info table{font-size:14px;font-family:sans-serif;color:#3b4151}.swagger-ui .info h1,.swagger-ui .info h2,.swagger-ui .info h3,.swagger-ui .info h4,.swagger-ui .info h5{font-family:sans-serif;color:#3b4151}.swagger-ui .info a{font-size:14px;transition:all .4s;font-family:sans-serif;color:#4990e2}.swagger-ui .info a:hover{color:#1f69c0}.swagger-ui .info>div{margin:0 0 5px}.swagger-ui .info .base-url{font-size:12px;font-weight:300!important;margin:0;font-family:monospace;font-weight:600;color:#3b4151}.swagger-ui .info .title{font-size:36px;margin:0;font-family:sans-serif;color:#3b4151}.swagger-ui .info .title small{font-size:10px;position:relative;top:-5px;display:inline-block;margin:0 0 0 5px;padding:2px 4px;vertical-align:super;border-radius:57px;background:#7d8492}.swagger-ui .info .title small pre{margin:0;padding:0;font-family:sans-serif;color:#fff}.swagger-ui .auth-btn-wrapper{display:flex;padding:10px 0;justify-content:center}.swagger-ui .auth-btn-wrapper .btn-done{margin-right:1em}.swagger-ui .auth-wrapper{display:flex;flex:1;justify-content:flex-end}.swagger-ui .auth-wrapper .authorize{padding-right:20px;margin-right:10px}.swagger-ui .auth-container{margin:0 0 10px;padding:10px 20px;border-bottom:1px solid #ebebeb}.swagger-ui .auth-container:last-of-type{margin:0;padding:10px 20px;border:0}.swagger-ui .auth-container h4{margin:5px 0 15px!important}.swagger-ui .auth-container .wrapper{margin:0;padding:0}.swagger-ui .auth-container input[type=password],.swagger-ui .auth-container input[type=text]{min-width:230px}.swagger-ui .auth-container .errors{font-size:12px;padding:10px;border-radius:4px;font-family:monospace;font-weight:600;color:#3b4151}.swagger-ui .scopes h2{font-size:14px;font-family:sans-serif;color:#3b4151}.swagger-ui .scope-def{padding:0 0 20px}.swagger-ui .errors-wrapper{margin:20px;padding:10px 20px;-webkit-animation:scaleUp .5s;animation:scaleUp .5s;border:2px solid #f93e3e;border-radius:4px;background:rgba(249,62,62,.1)}.swagger-ui .errors-wrapper .error-wrapper{margin:0 0 10px}.swagger-ui .errors-wrapper .errors h4{font-size:14px;margin:0;font-family:monospace;font-weight:600;color:#3b4151}.swagger-ui .errors-wrapper .errors small{color:#606060}.swagger-ui .errors-wrapper hgroup{display:flex;align-items:center}.swagger-ui .errors-wrapper hgroup h4{font-size:20px;margin:0;flex:1;font-family:sans-serif;color:#3b4151}@-webkit-keyframes scaleUp{0%{-webkit-transform:scale(.8);transform:scale(.8);opacity:0}to{-webkit-transform:scale(1);transform:scale(1);opacity:1}}@keyframes scaleUp{0%{-webkit-transform:scale(.8);transform:scale(.8);opacity:0}to{-webkit-transform:scale(1);transform:scale(1);opacity:1}}.swagger-ui .Resizer.vertical.disabled{display:none}.swagger-ui .markdown p,.swagger-ui .markdown pre,.swagger-ui .renderedMarkdown p,.swagger-ui .renderedMarkdown pre{margin:1em auto}.swagger-ui .markdown pre,.swagger-ui .renderedMarkdown pre{color:#000;font-weight:400;white-space:pre-wrap;background:none;padding:0}.swagger-ui .markdown code,.swagger-ui .renderedMarkdown code{font-size:14px;padding:5px 7px;border-radius:4px;background:rgba(0,0,0,.05);font-family:monospace;font-weight:600;color:#9012fe}.swagger-ui .markdown pre>code,.swagger-ui .renderedMarkdown pre>code{display:block} - -/*# sourceMappingURL=swagger-ui.css.map*/ \ No newline at end of file diff --git a/design/best-practices/index.html b/design/best-practices/index.html index 6b90ec8f8e..53b6b3cc3e 100644 --- a/design/best-practices/index.html +++ b/design/best-practices/index.html @@ -1,9 +1,9 @@ -Configuration Best Practices | Community Health Toolkit +Configuration Best Practices | Community Health Toolkit

    Configuration Best Practices

    This document covers the configuration best practices of forms, tasks, targets, and contact profiles when building your own community health app.

    Forms

    We use forms to build the Tasks, Care Guides, and Reports that take health workers through care protocols and provide decision support for their interactions with patients.

    In this context, a form is any document with questions and blank spaces or selectable options for answers. Forms can be found in many parts of your app including the Tasks, People, and Reports tabs

    While both Tasks and Reports are built with forms, there are key differences. Tasks are blank forms that need to be completed, while Reports are forms that have already been submitted. When a Task is completed and submitted, it automatically becomes a Report.

    Note: The icons and titles that we choose for Tasks remain the same when they become Reports.


    Anatomy of a Task

    The Task tab shows a list of upcoming visits, follow-ups, or other tasks that need to be completed. When a task is finished, it will automatically clear from the Tasks list and move to Reports.


    Anatomy of a Task


    Each Task has an icon on the left side which indicates what type of Task it is.
    The first bold line of text is the name of the person or family that the Task is about.
    The second line of text is the name of the Task.
    The days left for the Task to be done is located in the upper right-hand corner.
    If a Task is overdue, the due date will be red. Tasks are listed in order of due date.


    Anatomy of a Report


    Anatomy of a Report


    The first line of bold text is the name of the person whom the Report is about.
    The second line of text is the title of the Report.
    The third line of text is the hierarchy of place to which that person belongs.
    In the upper right corner, a timestamp displays when the Report was submitted.

    Reports are sorted by submission date, with the most recently submitted Reports at the top. If a Report is unread, the timestamp will be bold blue and there will be a horizontal blue line above it.


    Titles

    The patient’s name should not be included in the form title.

    ❌ Don't do this: "Beatrice Bass Delivery Follow-Up"
    + Create project issue

    Configuration Best Practices

    This document covers the configuration best practices of forms, tasks, targets, and contact profiles when building your own community health app.

    Forms

    We use forms to build the Tasks, Care Guides, and Reports that take health workers through care protocols and provide decision support for their interactions with patients.

    In this context, a form is any document with questions and blank spaces or selectable options for answers. Forms can be found in many parts of your app including the Tasks, People, and Reports tabs

    While both Tasks and Reports are built with forms, there are key differences. Tasks are blank forms that need to be completed, while Reports are forms that have already been submitted. When a Task is completed and submitted, it automatically becomes a Report.

    Note: The icons and titles that we choose for Tasks remain the same when they become Reports.


    Anatomy of a Task

    The Task tab shows a list of upcoming visits, follow-ups, or other tasks that need to be completed. When a task is finished, it will automatically clear from the Tasks list and move to Reports.


    Anatomy of a Task


    Each Task has an icon on the left side which indicates what type of Task it is.
    The first bold line of text is the name of the person or family that the Task is about.
    The second line of text is the name of the Task.
    The days left for the Task to be done is located in the upper right-hand corner.
    If a Task is overdue, the due date will be red. Tasks are listed in order of due date.


    Anatomy of a Report


    Anatomy of a Report


    The first line of bold text is the name of the person whom the Report is about.
    The second line of text is the title of the Report.
    The third line of text is the hierarchy of place to which that person belongs.
    In the upper right corner, a timestamp displays when the Report was submitted.

    Reports are sorted by submission date, with the most recently submitted Reports at the top. If a Report is unread, the timestamp will be bold blue and there will be a horizontal blue line above it.


    Titles

    The patient’s name should not be included in the form title.

    ❌ Don't do this: "Beatrice Bass Delivery Follow-Up"
     ✅ Do this instead: "Delivery Follow-Up"
     

    Avoid generic words like “Visit” or “Report”. Every form can be a Report and often involves a visit, so including these words in the title doesn’t help differentiate it from other forms.

    ❌ Don't do this: "Delivery Follow-up Visit"
     ✅ Do this instead: "Delivery Follow-Up"
    @@ -349,7 +349,8 @@
             - On-time + Late = true, Didn’t happen = false
     
    • It’s not possible to total up forms from all household members or check to see if any family member had a form submitted this week and count that as the household being visited. If you want to count the households visited this month, you need a form submitted at the household level like a household visit form or a household survey form.
    • Denominator must be the same for both sides (e.g. kids in our immunization program). Can’t split it into kids <2 yrs on one side and kids <5 years on the other side. Bars must progress positively (ex. Visits completed, not visits missed)

    Unique Considerations for Personas

    CHWs:

    • Will be viewing on a mobile device; keep in mind the number of widgets you display
    • Information is related to their own individual achievement, not comparisons with peers or aggregates for their facility
    • May have a difficult time understanding percentages (however certain partners like LG still focus on % based targets and therefore it still needs to be included). Percentages are good for evaluating performance, but difficult to “act on” because depending on how many actions you have already done / left to do, it may not always be clear or possible to “achieve” a percentage goal.
    • Targets data is always up-to-date even when offline, but important to know when they’ve last synced and sent data to managers

    Managers:

    • Could be viewing on desktop or mobile device; may be seeing widgets side-by-side
    • Want to view overall summary of CHW goals, may also have their own personal goals
    • Can only see widgets for which they have Report access
    • Understand, and need, percentage information in order to evaluate performance
    • Important to know how up-to-date the data is (will be addressed in a different feature for CHW last sync date)
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/design/best-practices/index.xml b/design/best-practices/index.xml index 284b39486c..f584e520e7 100644 --- a/design/best-practices/index.xml +++ b/design/best-practices/index.xml @@ -1 +1 @@ -Community Health Toolkit – Configuration Best Practiceshttps://docs.communityhealthtoolkit.org/design/best-practices/Recent content in Configuration Best Practices on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file +Configuration Best Practices on Community Health Toolkithttps://docs.communityhealthtoolkit.org/design/best-practices/Recent content in Configuration Best Practices on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file diff --git a/design/components/index.html b/design/components/index.html index 7b8dee93a2..726521921b 100644 --- a/design/components/index.html +++ b/design/components/index.html @@ -1,9 +1,9 @@ -Component Library | Community Health Toolkit +Component Library | Community Health Toolkit

    Component Library

    The standard aspects of our core framework for reuse to encourage consistency when building new pages or components

    The purpose of this guide is to document the standard aspects of our core framework and allow designers and developers to focus on solving challenges, rather than reinventing interface elements that are already in use. Changes to the styles in this document should go through product design and be implemented throughout the app before this document is updated.

    Colors

    Color helps users interpret and interact with app content by establishing a hierarchy of information, highlighting actions, indicating states, and conveying meaning.

    Primary colors

    These are the primary colors of the navigation tabs. When necessary, use white #FFFFFF text over these colors.

    #63A2C6 Blue#7193EE Periwinkle#F47B63 Pink#76B0B0 Teal#E9AA22 Yellow
    #63A2C6#7193EE#F47B63#76B0B0#E9AA22
    rgb(99, 162, 198)rgb(113, 147, 238)rgb(244, 123, 99)rgb(118, 176, 176)rgb(233, 170, 34)
    MessagesTasksPeopleTargetsReports

    Secondary colors

    These are the secondary (highlight) colors of the navigation tabs.

    #EEF5F9 Blue Highlight#F0F4FD Periwinkle Highlight#FDF1EF Pink Highlight#DFEAEA Teal Highlight#FCF6E7 Yellow Highlight
    #EEF5F9#F0F4FD#FDF1EF#DFEAEA#FCF6E7
    rgb(238, 245, 249)rgb(240, 244, 253)rgb(253, 241, 239)rgb(223, 234, 234)rgb(252, 246, 231)

    Status colors

    These are the status indication colors of the system. When necessary, use white #FFFFFF text over these colors.

    #218E7F Teal Dark#007AC0 Blue Dark#C78330 Yellow Dark#E33030 Red
    #218E7F#007AC0#C78330#E33030
    rgb(33, 142, 127)rgb(0, 122, 192)rgb(199, 131, 48)rgb(227, 48, 48)
    Completed, verified, sent actionsPrimary button, link, infoDelayed, incomplete actionsOverdue, unmet, error, delete, failed, denied actions

    Backgrounds

    #777777 Gray Dark#E0E0E0 Gray Light#A0A0A0 Gray Medium#333333 Gray Ultra Dark#F2F2F2 Gray Ultra Light#FFFFFF White
    #777777#E0E0E0#A0A0A0#333333#F2F2F2#FFFFFF
    rgb(119, 119, 119)rgb(224, 224, 224)rgb(160, 160, 160)rgb(51, 51, 51)rgb(242, 242, 242)rgb(255, 255, 255)
    Disabled statuses, secondary body text1px line borders, action bar iconsMuted or deceased contacts, cleared messagesOverdue, unmet, error, delete, failed, denied actionsApp background, list and dropdown highlightsForm background

    For more information on how these colors are applied in the app, see our color variables file.

    Typography

    The default app font is Noto Sans. It is free, ocodepen source, supports 800 languages and was specifically designed for good web legibility. It is bundled with the app so that all users see the same font regardless of their particular device, language, browser, etc. This ensures a consistent experience for all users.

    Most text in the app should be the @text-normal-color: @gray-ultra-dark color. + Create project issue

    Component Library

    The standard aspects of our core framework for reuse to encourage consistency when building new pages or components

    The purpose of this guide is to document the standard aspects of our core framework and allow designers and developers to focus on solving challenges, rather than reinventing interface elements that are already in use. Changes to the styles in this document should go through product design and be implemented throughout the app before this document is updated.

    Colors

    Color helps users interpret and interact with app content by establishing a hierarchy of information, highlighting actions, indicating states, and conveying meaning.

    Primary colors

    These are the primary colors of the navigation tabs. When necessary, use white #FFFFFF text over these colors.

    #63A2C6 Blue#7193EE Periwinkle#F47B63 Pink#76B0B0 Teal#E9AA22 Yellow
    #63A2C6#7193EE#F47B63#76B0B0#E9AA22
    rgb(99, 162, 198)rgb(113, 147, 238)rgb(244, 123, 99)rgb(118, 176, 176)rgb(233, 170, 34)
    MessagesTasksPeopleTargetsReports

    Secondary colors

    These are the secondary (highlight) colors of the navigation tabs.

    #EEF5F9 Blue Highlight#F0F4FD Periwinkle Highlight#FDF1EF Pink Highlight#DFEAEA Teal Highlight#FCF6E7 Yellow Highlight
    #EEF5F9#F0F4FD#FDF1EF#DFEAEA#FCF6E7
    rgb(238, 245, 249)rgb(240, 244, 253)rgb(253, 241, 239)rgb(223, 234, 234)rgb(252, 246, 231)

    Status colors

    These are the status indication colors of the system. When necessary, use white #FFFFFF text over these colors.

    #218E7F Teal Dark#007AC0 Blue Dark#C78330 Yellow Dark#E33030 Red
    #218E7F#007AC0#C78330#E33030
    rgb(33, 142, 127)rgb(0, 122, 192)rgb(199, 131, 48)rgb(227, 48, 48)
    Completed, verified, sent actionsPrimary button, link, infoDelayed, incomplete actionsOverdue, unmet, error, delete, failed, denied actions

    Backgrounds

    #777777 Gray Dark#E0E0E0 Gray Light#A0A0A0 Gray Medium#333333 Gray Ultra Dark#F2F2F2 Gray Ultra Light#FFFFFF White
    #777777#E0E0E0#A0A0A0#333333#F2F2F2#FFFFFF
    rgb(119, 119, 119)rgb(224, 224, 224)rgb(160, 160, 160)rgb(51, 51, 51)rgb(242, 242, 242)rgb(255, 255, 255)
    Disabled statuses, secondary body text1px line borders, action bar iconsMuted or deceased contacts, cleared messagesOverdue, unmet, error, delete, failed, denied actionsApp background, list and dropdown highlightsForm background

    For more information on how these colors are applied in the app, see our color variables file.

    Typography

    The default app font is Noto Sans. It is free, ocodepen source, supports 800 languages and was specifically designed for good web legibility. It is bundled with the app so that all users see the same font regardless of their particular device, language, browser, etc. This ensures a consistent experience for all users.

    Most text in the app should be the @text-normal-color: @gray-ultra-dark color. The lighter text color @text-secondary-color: @gray-dark) is used for labels and condition card filters. Hyperlinked text color is @text-hyperlink-color: @blue-dark).

    H1 is the highest hierarchical level of text, and should be used sparingly. It is used for the large text underneath percentage bars.

    H2 is used as a header style for main content sections on the right-hand side, such as a task title, the name of a person/place on their profile, or the title of a targets widget.

    H3 is used for titles of condition cards and section titles on the form summary page.

    H4 is the default type size, and should be used for all normal body text throughout the app. Most text should be H4 in size. When in doubt, use H4.

    H5 is a smaller body text size that we use sparingly in places where space is tight, such as timestamps in the upper right of content rows, condition card filter text, “belongs to” breadcrumbs, and targets goal labels.

        <script
             data-slug-hash="PoZObmY"
    @@ -413,7 +413,8 @@
             src="//codepen.io/assets/embed/ei.js"
         ></script>
     
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/design/components/index.xml b/design/components/index.xml index 41ef951f87..b67649d35f 100644 --- a/design/components/index.xml +++ b/design/components/index.xml @@ -1 +1 @@ -Community Health Toolkit – Component Libraryhttps://docs.communityhealthtoolkit.org/design/components/Recent content in Component Library on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file +Component Library on Community Health Toolkithttps://docs.communityhealthtoolkit.org/design/components/Recent content in Component Library on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file diff --git a/design/guides/designing-interviews/index.html b/design/guides/designing-interviews/index.html index 0ecaa79c89..5431b4c557 100644 --- a/design/guides/designing-interviews/index.html +++ b/design/guides/designing-interviews/index.html @@ -1,9 +1,9 @@ -Designing User Interviews | Community Health Toolkit +Designing User Interviews | Community Health Toolkit

    Designing User Interviews

    Designing user interviews

    Purpose Of the Guide

    Understanding the people you are designing for is a paramount step in the design process, and so one way to achieve that is through conducting user interviews. This guide will help design thinkers to understand users’ experiences from their own point of view in terms of:

    • Why they use products in a certain way
    • How they feel about something
    • How they perform various actions

    Background (Current Situation, Problem, and/or Opportunity)

    Interviews can be a great way to empathize with your users because interviews can give you an in-depth understanding of the users’ values, perceptions, and experiences. They allow you to ask specific questions, while remaining open to exploring your participants’ points of view. They are also often combined with other user research methods, such as usability tests or surveys, so as to gain deeper insights into objective results by asking a user about them and to elicit the user’s subjective opinion on products or interactions.

    Brief Overview Of Key Concepts

    Semi-structured interviews are somewhat structured in that you prepare a set of topics you would like to cover during the interview, but still open enough that you can follow leads in the conversation and change the order of topics.

    This means you have an interview guide with the questions or themes that you want to talk to the user about, but you are free to change the order of questions or to explore different topics that may arise during the interview.

    User interviews can be very informative and helpful, but only if they are used correctly and for the right things. It is important to know what you can expect to get out of interviews and what you should not expect to get out of interviews.

    User interviews can be conducted for the following reasons:

    • for exploration before a clear concept has been defined or before a major redesign,
    • in combination with user tests and formal experiments, and
    • in combination with observations.

    User Interview Requirements

    A good interview requires preparation and careful consideration on the part of the interviewer. + Create project issue

    Designing User Interviews

    Designing user interviews

    Purpose Of the Guide

    Understanding the people you are designing for is a paramount step in the design process, and so one way to achieve that is through conducting user interviews. This guide will help design thinkers to understand users’ experiences from their own point of view in terms of:

    • Why they use products in a certain way
    • How they feel about something
    • How they perform various actions

    Background (Current Situation, Problem, and/or Opportunity)

    Interviews can be a great way to empathize with your users because interviews can give you an in-depth understanding of the users’ values, perceptions, and experiences. They allow you to ask specific questions, while remaining open to exploring your participants’ points of view. They are also often combined with other user research methods, such as usability tests or surveys, so as to gain deeper insights into objective results by asking a user about them and to elicit the user’s subjective opinion on products or interactions.

    Brief Overview Of Key Concepts

    Semi-structured interviews are somewhat structured in that you prepare a set of topics you would like to cover during the interview, but still open enough that you can follow leads in the conversation and change the order of topics.

    This means you have an interview guide with the questions or themes that you want to talk to the user about, but you are free to change the order of questions or to explore different topics that may arise during the interview.

    User interviews can be very informative and helpful, but only if they are used correctly and for the right things. It is important to know what you can expect to get out of interviews and what you should not expect to get out of interviews.

    User interviews can be conducted for the following reasons:

    • for exploration before a clear concept has been defined or before a major redesign,
    • in combination with user tests and formal experiments, and
    • in combination with observations.

    User Interview Requirements

    A good interview requires preparation and careful consideration on the part of the interviewer. It is important to be aware of how to ask questions and how to listen in order to gain valid insights into your participant’s life and experiences.

    How to Structure User Interviews

    When you conduct a user interview, consider the best way of ordering the questions. Below are some tips on what to ask in the beginning, in the middle, and at the end of a user interview.

    Beginning

    At the beginning of the interview, ask opening questions to set people at ease and build rapport. Do not ask about sensitive topics. Instead, focus on setting the stage and bringing your interviewees on board so they are comfortable enough to be, and remain open with you. Your questions at this stage can include:

    • briefing participants on topics that will be covered,
    • briefing participants on how their data will be used,
    • asking safe questions such as what their role is in their organization, and
    • asking concrete questions that are easy to answer.


    Middle

    In the middle of the interview, you are hitting high gear. Having established a direct channel with your interviewees (the users), you move on and ask the bulk of your questions. The predetermined order of the questions may change based on the direction the conversation is taking. Some of the tips here include:

    • keeping the flow of the conversation as natural as possible, but cover the topics you want to cover,
    • picking up on what participants have said earlier and get full replies to questions they have only partially answered,
    • steering participants back on track if they go too far off topic, and
    • showing that you have been listening.


    End

    At the end of the interview, you wrap up in a way that makes participants feel as though they have said what they wanted to say and that their answers are valuable.

    Things you should do include:

    • asking if there is anything participants would like to add,
    • telling your participants what you are going to do with their data and what the value is for them, and
    • thanking them for taking the time to help with your research.


    Example Interview Guide

    Introduction

    You have been identified as someone with significant expertise and experience, and we have asked you to participate in this interview. Your responses are confidential. We may need to record the session for use later on as we compile notes from this interview. The conversation will take 30 to 45 minutes of your time.

    CHW Supervisors
    Questions on supervision
    1. What does your typical day look like?
    2. How many CHWs have you been assigned to supervise?
    3. What are your main supervisory responsibilities?
    4. Which tools do you use during supervision of CHWs?
    5. Which are the health areas that CHWs are expected to report?
    6. How often do you interact/meet with CHWs for official work assignments?
    7. Do you shadow CHWs during their visits to households or other field activities? How often?
    8. What informs the mode or approach of supervision?
    9. What supervision activities are involved?
    Questions on reporting
    1. How often do you collect data from CHW?
    2. What data is usually or typically hard to collect accurately for CHWs?
    3. What are commonest challenges observed or reported by CHWs during meetings?
    4. How often do CHWs submit their reports?
    5. What challenges have you experienced while compiling and reporting the data from CHWs?
    6. What improvements do you think would help improve the reporting process?

    Ending the interview

    We have come to the end of the interview. Do you have anything to add that you feel we have not covered?

    Thank you so much for taking the time to speak with me today. Learning about how you conduct supervision and reporting activities will really help us understand how to create solutions to support your work. If you think of anything else or you have any questions, you are more than welcome to get in touch. I also want to ask if we can contact you again if we think of other questions or if something is unclear. Is that alright?

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/design/guides/empathy-map/index.html b/design/guides/empathy-map/index.html index e350cdaa56..1c1694e999 100644 --- a/design/guides/empathy-map/index.html +++ b/design/guides/empathy-map/index.html @@ -1,9 +1,9 @@ -Creating an Empathy Map | Community Health Toolkit +Creating an Empathy Map | Community Health Toolkit

    Creating an Empathy Map

    Creating an empathy map

    Purpose Of the Guide

    This guide will take you through the process of creating an empathy map as a way to synthesize the insights gathered during the discovery phase.

    Background

    An empathy map is a visualization tool which helps you sum up what you learned from design research to help you better understand your users and articulate what you know to colleagues and stakeholders.

    The map provides four major areas in which to focus your attention on, thus providing an overview of a person’s experience. Empathy maps are also great as a background for the construction of the personas that you would often want to create later.

    The map consists of four quadrants. The four quadrants reflect four key traits which the user demonstrated/possessed during the discovery stage. The four quadrants refer to what the user: said, did, thought, and felt.

    It is fairly easy to determine what the user said and did. However, determining what they thought and felt should be based on careful observation and analysis of how they behaved and responded to certain activities, suggestions, conversations, and so on.

    Brief Overview Of Key Concepts

    Insight refers to a remarkable realization that can help you to solve the current design challenge you are facing.

    Steps

    1. Fill Out the Empathy Map

    Lay the four quadrants out on a table, draw them on paper or on a whiteboard.

    Review your notes, pictures, audio, and video from your research/fieldwork and fill out each of the four quadrants while defining and synthesising:


    • What did the user SAY? Write down significant quotes and keywords that the user said.
    • What did the user DO? Describe which actions and behaviours you noticed or insert pictures or drawing.
    • What did the user THINK? Dig deeper. What do you think that your user might be thinking? What are their motivations, their goals, their needs, their desires? What does this tell you about her beliefs?
    • How did the user FEEL? What emotions might your user be feeling? Take subtle cues like body language and their choice of words and tone of voice into account.


    2. Synthesize Needs

    Look for the following within your data to identify your users’ needs:

    • Verbs, that is, activities and desires. Mark and analyze any pieces of data that start with a verb as these are likely to contain or point towards a user need.
    • The user traits you noted. Mark specific user traits as these will lead towards the true needs of your users.
    • Contradictions and inconsistencies. Once you have picked out the users’ traits, you should look for contradictions and inconsistencies between them. For example, there may be a disconnect between what a user says and does, or they might show a positive action but portray a negative emotion through a quote.

    Write down user’s needs.



    3. Synthesize Insights

    Look to synthesize major insights, especially from contradictions between two user attributes. It can be found within one quadrant or in two different quadrants.

    You can also synthesize insights by asking yourself ‘why?’ when you notice strange, tense, or surprising behaviour.

    Write down your insights.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/design/guides/index.html b/design/guides/index.html index 3a69590ff4..0edb667aef 100644 --- a/design/guides/index.html +++ b/design/guides/index.html @@ -1,9 +1,9 @@ -Quick Guides for Designers | Community Health Toolkit +Quick Guides for Designers | Community Health Toolkit

    Quick Guides for Designers

    These quick guides address focused CHT service design topics

    Designing User Interviews

    Designing user interviews

    Creating an Empathy Map

    Creating an empathy map

    Creating an Actionable Problem Statement

    Creating an actionable problem statement

    Mapping User on the CHT Hierarchy

    Mapping users on the CHT hierarchy

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/design/guides/index.xml b/design/guides/index.xml index 0552305df8..4e99111822 100644 --- a/design/guides/index.xml +++ b/design/guides/index.xml @@ -1,217 +1,9 @@ -Community Health Toolkit – Quick Guides for Designershttps://docs.communityhealthtoolkit.org/design/guides/Recent content in Quick Guides for Designers on Community Health ToolkitHugo -- gohugo.ioenDesign: Designing User Interviewshttps://docs.communityhealthtoolkit.org/design/guides/designing-interviews/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/guides/designing-interviews/ -<h2 id="purpose-of-the-guide">Purpose Of the Guide</h2> -<p>Understanding the people you are designing for is a paramount step in the design process, and so one way to achieve that is through conducting user interviews. This guide will help design thinkers to understand users’ experiences from their own point of view in terms of:</p> -<ul> -<li>Why they use products in a certain way</li> -<li>How they feel about something</li> -<li>How they perform various actions</li> -</ul> -<h2 id="background-current-situation-problem-andor-opportunity">Background (Current Situation, Problem, and/or Opportunity)</h2> -<p>Interviews can be a great way to empathize with your users because interviews can give you an in-depth understanding of the users’ values, perceptions, and experiences. They allow you to ask specific questions, while remaining open to exploring your participants’ points of view. They are also often combined with other user research methods, such as usability tests or surveys, so as to gain deeper insights into objective results by asking a user about them and to elicit the user’s subjective opinion on products or interactions.</p> -<h2 id="brief-overview-of-key-concepts">Brief Overview Of Key Concepts</h2> -<p>Semi-structured interviews are somewhat structured in that you prepare a set of topics you would like to cover during the interview, but still open enough that you can follow leads in the conversation and change the order of topics.</p> -<p>This means you have an interview guide with the questions or themes that you want to talk to the user about, but you are free to change the order of questions or to explore different topics that may arise during the interview.</p> -<p>User interviews can be very informative and helpful, but only if they are used correctly and for the right things. It is important to know what you can expect to get out of interviews and what you should not expect to get out of interviews.</p> -<p>User interviews can be conducted for the following reasons:</p> -<ul> -<li>for exploration before a clear concept has been defined or before a major redesign,</li> -<li>in combination with user tests and formal experiments, and</li> -<li>in combination with observations.</li> -</ul> -<h2 id="user-interview-requirements">User Interview Requirements</h2> -<p>A good interview requires preparation and careful consideration on the part of the interviewer. -It is important to be aware of how to ask questions and how to listen in order to gain valid insights into your participant’s life and experiences.</p> -<h2 id="how-to-structure-user-interviews">How to Structure User Interviews</h2> -<p>When you conduct a user interview, consider the best way of ordering the questions. Below are some tips on what to ask in the beginning, in the middle, and at the end of a user interview.</p> -<h3 id="beginning">Beginning</h3> -<p>At the beginning of the interview, ask opening questions to set people at ease and build rapport. Do not ask about sensitive topics. Instead, focus on setting the stage and bringing your interviewees on board so they are comfortable enough to be, and remain open with you. Your questions at this stage can include:</p> -<ul> -<li>briefing participants on topics that will be covered,</li> -<li>briefing participants on how their data will be used,</li> -<li>asking safe questions such as what their role is in their organization, and</li> -<li>asking concrete questions that are easy to answer.</li> -</ul> -<br clear="all"> -<hr> -<h3 id="middle">Middle</h3> -<p>In the middle of the interview, you are hitting high gear. Having established a direct channel with your interviewees (the users), you move on and ask the bulk of your questions. The predetermined order of the questions may change based on the direction the conversation is taking. Some of the tips here include:</p> -<ul> -<li>keeping the flow of the conversation as natural as possible, but cover the topics you want to cover,</li> -<li>picking up on what participants have said earlier and get full replies to questions they have only partially answered,</li> -<li>steering participants back on track if they go too far off topic, and</li> -<li>showing that you have been listening.</li> -</ul> -<br clear="all"> -<hr> -<h3 id="end">End</h3> -<p>At the end of the interview, you wrap up in a way that makes participants feel as though they have said what they wanted to say and that their answers are valuable.</p> -<p>Things you should do include:</p> -<ul> -<li>asking if there is anything participants would like to add,</li> -<li>telling your participants what you are going to do with their data and what the value is for them, and</li> -<li>thanking them for taking the time to help with your research.</li> -</ul> -<br clear="all"> -<hr> -<h3 id="example-interview-guide">Example Interview Guide</h3> -<h4 id="introduction">Introduction</h4> -<p><em>You have been identified as someone with significant expertise and experience, and we have asked you to participate in this interview. Your responses are confidential. We may need to record the session for use later on as we compile notes from this interview. The conversation will take 30 to 45 minutes of your time.</em></p> -<h5 id="chw-supervisors">CHW Supervisors</h5> -<h6 id="questions-on-supervision">Questions on supervision</h6> -<ol> -<li><em>What does your typical day look like?</em></li> -<li><em>How many CHWs have you been assigned to supervise?</em></li> -<li><em>What are your main supervisory responsibilities?</em></li> -<li><em>Which tools do you use during supervision of CHWs?</em></li> -<li><em>Which are the health areas that CHWs are expected to report?</em></li> -<li><em>How often do you interact/meet with CHWs for official work assignments?</em></li> -<li><em>Do you shadow CHWs during their visits to households or other field activities? How often?</em></li> -<li><em>What informs the mode or approach of supervision?</em></li> -<li><em>What supervision activities are involved?</em></li> -</ol> -<h6 id="questions-on-reporting">Questions on reporting</h6> -<ol> -<li><em>How often do you collect data from CHW?</em></li> -<li><em>What data is usually or typically hard to collect accurately for CHWs?</em></li> -<li><em>What are commonest challenges observed or reported by CHWs during meetings?</em></li> -<li><em>How often do CHWs submit their reports?</em></li> -<li><em>What challenges have you experienced while compiling and reporting the data from CHWs?</em></li> -<li><em>What improvements do you think would help improve the reporting process?</em></li> -</ol> -<h4 id="ending-the-interview">Ending the interview</h4> -<p><em>We have come to the end of the interview. Do you have anything to add that you feel we have not covered?</em></p> -<p><em>Thank you so much for taking the time to speak with me today. Learning about how you conduct supervision and reporting activities will really help us understand how to create solutions to support your work. If you think of anything else or you have any questions, you are more than welcome to get in touch. I also want to ask if we can contact you again if we think of other questions or if something is unclear. Is that alright?</em></p>Design: Creating an Empathy Maphttps://docs.communityhealthtoolkit.org/design/guides/empathy-map/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/guides/empathy-map/ -<h2 id="purpose-of-the-guide">Purpose Of the Guide</h2> -<p>This guide will take you through the process of creating an empathy map as a way to synthesize the insights gathered during the discovery phase.</p> -<h2 id="background">Background</h2> -<p>An empathy map is a visualization tool which helps you sum up what you learned from design research to help you better understand your users and articulate what you know to colleagues and stakeholders.</p> -<p>The map provides four major areas in which to focus your attention on, thus providing an overview of a person’s experience. Empathy maps are also great as a background for the construction of the personas that you would often want to create later.</p> -<p>The map consists of four quadrants. The four quadrants reflect four key traits which the user demonstrated/possessed during the discovery stage. The four quadrants refer to what the user: said, did, thought, and felt.</p> -<p>It is fairly easy to determine what the user said and did. However, determining what they thought and felt should be based on careful observation and analysis of how they behaved and responded to certain activities, suggestions, conversations, and so on.</p> -<h2 id="brief-overview-of-key-concepts">Brief Overview Of Key Concepts</h2> -<p><em>Insight</em> refers to a remarkable realization that can help you to solve the current design challenge you are facing.</p> -<h2 id="steps">Steps</h2> -<h3 id="1-fill-out-the-empathy-map">1. Fill Out the Empathy Map</h3> -<figure class="right col-6 col-lg-8"><a href="empathy-map.png"> -<img src="empathy-map.png"/> </a> -</figure> -<p>Lay the four quadrants out on a table, draw them on paper or on a whiteboard.</p> -<p>Review your notes, pictures, audio, and video from your research/fieldwork and fill out each of the four quadrants while defining and synthesising:</p> -<br clear="all"> -<ul> -<li>What did the user SAY? Write down significant quotes and keywords that the user said.</li> -<li>What did the user DO? Describe which actions and behaviours you noticed or insert pictures or drawing.</li> -<li>What did the user THINK? Dig deeper. What do you think that your user might be thinking? What are their motivations, their goals, their needs, their desires? What does this tell you about her beliefs?</li> -<li>How did the user FEEL? What emotions might your user be feeling? Take subtle cues like body language and their choice of words and tone of voice into account.</li> -</ul> -<br clear="all"> -<hr> -<h3 id="2-synthesize-needs">2. Synthesize Needs</h3> -<p>Look for the following within your data to identify your users’ needs:</p> -<ul> -<li>Verbs, that is, activities and desires. Mark and analyze any pieces of data that start with a verb as these are likely to contain or point towards a user need.</li> -<li>The user traits you noted. Mark specific user traits as these will lead towards the true needs of your users.</li> -<li>Contradictions and inconsistencies. Once you have picked out the users’ traits, you should look for contradictions and inconsistencies between them. For example, there may be a disconnect between what a user says and does, or they might show a positive action but portray a negative emotion through a quote.</li> -</ul> -<p>Write down user’s needs.</p> -<br clear="all"> -<hr> -<h3 id="3-synthesize-insights">3. Synthesize Insights</h3> -<p>Look to synthesize major insights, especially from contradictions between two user attributes. It can be found within one quadrant or in two different quadrants.</p> -<p>You can also synthesize insights by asking yourself &lsquo;why?&rsquo; when you notice strange, tense, or surprising behaviour.</p> -<p>Write down your insights.</p>Design: Creating an Actionable Problem Statementhttps://docs.communityhealthtoolkit.org/design/guides/problem-statement/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/guides/problem-statement/ -<h2 id="purpose-of-the-guide">Purpose Of the Guide</h2> -<p>This guide will take you through how to create an actionable problem statement in order to generate a greater quantity and higher quality solutions when you start generating ideas during ideation sessions.</p> -<h2 id="background">Background</h2> -<p>Defining your design challenge is probably one of the most important steps in the design thinking process as it sets the tone and guides all of the activities that follow.</p> -<p>In the define stage, you should end up creating an actionable problem statement which is commonly known as the point of view (POV) in design thinking. Base your point Of view on a deeper understanding of your specific users, their needs, and your most essential insights about them.</p> -<p>Your POV should never contain any specific solution, nor should it contain any indication as to how to fulfill your users’ needs in the service, experience, or product you are designing. Instead, your POV should provide a wide enough scope for you and your team to start thinking about solutions which go beyond status quo.</p> -<p>Here, you will also learn to frame and open up your point of view, which is the axis that design thinking revolves around – a challenge well-framed is half solved.</p> -<h2 id="steps">Steps</h2> -<h3 id="1-define-the-type-of-person-you-are-designing-for--your-user">1. Define the Type of Person You Are Designing For – Your User</h3> -<p>Select the most essential needs which are the most important to fulfill.</p> -<p>Work to express the insights developed through the synthesis of your gathered information.</p> -<br clear="all"> -<hr> -<h3 id="2-write-your-definitions-into-a-point-of-view-template">2. Write Your Definitions Into a Point of View Template</h3> -<figure><a href="pov-statement.png"> -<img src="pov-statement.png"/> </a> -</figure> -<p>Example</p> -<table> -<thead> -<tr> -<th>User</th> -<th>Needs</th> -<th>Insights</th> -</tr> -</thead> -<tbody> -<tr> -<td>An adult person who lives in a city</td> -<td>To use a car for 10-60 minutes 1-4 trips per week</td> -<td>The user would not want to own a car as it would be too expensive compared to his needs. He would like to share a car with others who have similar needs. However, there are no easy and affordable solutions for him. It is important for the user to think and live green and to not own more than he truly needs.</td> -</tr> -</tbody> -</table> -<br clear="all"> -<hr> -<h3 id="3-create-a-point-of-view-mad-lib">3. Create a Point of View Mad Lib</h3> -<p>You can articulate a POV by combining these three elements – user, need, and insight – as an actionable problem statement that will drive the rest of your design work.</p> -<p>It is surprisingly easy when you insert your findings in the POV Madlib below.</p> -<p>You can articulate your POV by inserting information about your user, the needs, and your insights in the following sentence:</p> -<p>[User . . . (descriptive)] needs [Need . . . (verb)] because [Insight . . . (compelling)]</p> -<p>Example</p> -<p>An adult person who lives in the city… needs access to a shared car 1-4 times for 10-60 minutes per week … because he would rather share a car with more people as this is cheaper, more environmentally friendly, however it should still be easy for more people to share.</p> -<br clear="all"> -<hr> -<h3 id="4-make-sure-that-your-point-of-view-meets-the-requirements">4. Make Sure That Your Point of View Meets the Requirements</h3> -<p>Your POV should:</p> -<ul> -<li>provide a narrow focus,</li> -<li>frame the problem as a problem statement,</li> -<li>inspire your team,</li> -<li>guide your innovation efforts,</li> -<li>inform criteria for evaluating competing ideas,</li> -<li>capture people’s attention, and</li> -<li>be valid, insightful, actionable, unique, narrow, meaningful, and exciting</li> -</ul>Design: Mapping User on the CHT Hierarchyhttps://docs.communityhealthtoolkit.org/design/guides/mapping-hierarchy/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/guides/mapping-hierarchy/ -<h2 id="purpose-of-the-guide">Purpose Of the Guide</h2> -<p>This guide will take you through mapping of users on CHT hierarchy, including:</p> -<ul> -<li>defining the hierarchy (reporting structure)</li> -<li>defining user roles</li> -<li>mapping user personas to the CHT hierarchy</li> -</ul> -<h2 id="brief-overview-of-key-concepts">Brief Overview of Key Concepts</h2> -<p>A <em><a href="https://docs.communityhealthtoolkit.org/design/personas/">user persona</a></em> is a generalized character that embodies a particular type of user.</p> -<p><em>User roles</em> are the activities that the user personas are expected to carry out.</p> -<p>A <em>hierarchy</em> represents the reporting structure.</p> -<h2 id="prerequisitesrequired-resources">Prerequisites/Required Resources</h2> -<p><a href="https://docs.communityhealthtoolkit.org/design/personas/">Personas.</a></p> -<h2 id="steps">Steps</h2> -<h3 id="1-define-the-hierarchy">1. Define the Hierarchy</h3> -<p>Identify the reporting structure of the user personas in order to have a flawless flow of data from one level to another.</p> -<figure class="right col-6 col-lg-8"><a href="app-hierarchy.jpg"> -<img src="app-hierarchy.jpg"/> </a> -</figure> -<p>For instance, family members belong to a household. Households and CHWs belong to a CHW area. CHW areas and nurses belong a health facility. Additional levels may be added as needed. The administrator level operates outside of the hierarchy and gives access to all levels and people.</p> -<br clear="all"> -<br clear="all"> -<figure class="right col-6 col-lg-8"><a href="user-hierarchy.png"> -<img src="user-hierarchy.png"/> </a> -</figure> -<p>The app hierarchy is often modeled after the health system, health program or community structure. All people who are registered in the app must be associated with a place. These places are located in a hierarchy to other places.</p> -<br clear="all"> -<hr> -<h3 id="2-define-the-user-roles">2. Define the User Roles</h3> -<p>Determine all the actors within the system in terms of who will be doing what.</p> -<figure><a href="user-roles.png"> -<img src="user-roles.png"/> </a> -</figure> -<br clear="all"> -<hr> -<h3 id="3-map-user-personas-to-the-hierarchy">3. Map User Personas to the Hierarchy</h3> -<figure><a href="mapped-personas.png"> -<img src="mapped-personas.png"/> </a> -</figure> \ No newline at end of file +Quick Guides for Designers on Community Health Toolkithttps://docs.communityhealthtoolkit.org/design/guides/Recent content in Quick Guides for Designers on Community Health ToolkitHugo -- gohugo.ioenDesigning User Interviewshttps://docs.communityhealthtoolkit.org/design/guides/designing-interviews/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/guides/designing-interviews/Purpose Of the Guide Understanding the people you are designing for is a paramount step in the design process, and so one way to achieve that is through conducting user interviews. This guide will help design thinkers to understand users’ experiences from their own point of view in terms of: +Why they use products in a certain way How they feel about something How they perform various actions Background (Current Situation, Problem, and/or Opportunity) Interviews can be a great way to empathize with your users because interviews can give you an in-depth understanding of the users’ values, perceptions, and experiences.Creating an Empathy Maphttps://docs.communityhealthtoolkit.org/design/guides/empathy-map/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/guides/empathy-map/Purpose Of the Guide This guide will take you through the process of creating an empathy map as a way to synthesize the insights gathered during the discovery phase. +Background An empathy map is a visualization tool which helps you sum up what you learned from design research to help you better understand your users and articulate what you know to colleagues and stakeholders. +The map provides four major areas in which to focus your attention on, thus providing an overview of a person’s experience.Creating an Actionable Problem Statementhttps://docs.communityhealthtoolkit.org/design/guides/problem-statement/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/guides/problem-statement/Purpose Of the Guide This guide will take you through how to create an actionable problem statement in order to generate a greater quantity and higher quality solutions when you start generating ideas during ideation sessions. +Background Defining your design challenge is probably one of the most important steps in the design thinking process as it sets the tone and guides all of the activities that follow. +In the define stage, you should end up creating an actionable problem statement which is commonly known as the point of view (POV) in design thinking.Mapping User on the CHT Hierarchyhttps://docs.communityhealthtoolkit.org/design/guides/mapping-hierarchy/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/guides/mapping-hierarchy/Purpose Of the Guide This guide will take you through mapping of users on CHT hierarchy, including: +defining the hierarchy (reporting structure) defining user roles mapping user personas to the CHT hierarchy Brief Overview of Key Concepts A user persona is a generalized character that embodies a particular type of user. +User roles are the activities that the user personas are expected to carry out. +A hierarchy represents the reporting structure. \ No newline at end of file diff --git a/design/guides/mapping-hierarchy/index.html b/design/guides/mapping-hierarchy/index.html index 628d4f92ac..cb489bf7b4 100644 --- a/design/guides/mapping-hierarchy/index.html +++ b/design/guides/mapping-hierarchy/index.html @@ -1,9 +1,9 @@ -Mapping User on the CHT Hierarchy | Community Health Toolkit +Mapping User on the CHT Hierarchy | Community Health Toolkit

    Mapping User on the CHT Hierarchy

    Mapping users on the CHT hierarchy

    Purpose Of the Guide

    This guide will take you through mapping of users on CHT hierarchy, including:

    • defining the hierarchy (reporting structure)
    • defining user roles
    • mapping user personas to the CHT hierarchy

    Brief Overview of Key Concepts

    A user persona is a generalized character that embodies a particular type of user.

    User roles are the activities that the user personas are expected to carry out.

    A hierarchy represents the reporting structure.

    Prerequisites/Required Resources

    Personas.

    Steps

    1. Define the Hierarchy

    Identify the reporting structure of the user personas in order to have a flawless flow of data from one level to another.

    For instance, family members belong to a household. Households and CHWs belong to a CHW area. CHW areas and nurses belong a health facility. Additional levels may be added as needed. The administrator level operates outside of the hierarchy and gives access to all levels and people.



    The app hierarchy is often modeled after the health system, health program or community structure. All people who are registered in the app must be associated with a place. These places are located in a hierarchy to other places.



    2. Define the User Roles

    Determine all the actors within the system in terms of who will be doing what.



    3. Map User Personas to the Hierarchy

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/design/guides/problem-statement/index.html b/design/guides/problem-statement/index.html index f7b02b6ac9..fa1e03560b 100644 --- a/design/guides/problem-statement/index.html +++ b/design/guides/problem-statement/index.html @@ -1,9 +1,9 @@ -Creating an Actionable Problem Statement | Community Health Toolkit +Creating an Actionable Problem Statement | Community Health Toolkit

    Creating an Actionable Problem Statement

    Creating an actionable problem statement

    Purpose Of the Guide

    This guide will take you through how to create an actionable problem statement in order to generate a greater quantity and higher quality solutions when you start generating ideas during ideation sessions.

    Background

    Defining your design challenge is probably one of the most important steps in the design thinking process as it sets the tone and guides all of the activities that follow.

    In the define stage, you should end up creating an actionable problem statement which is commonly known as the point of view (POV) in design thinking. Base your point Of view on a deeper understanding of your specific users, their needs, and your most essential insights about them.

    Your POV should never contain any specific solution, nor should it contain any indication as to how to fulfill your users’ needs in the service, experience, or product you are designing. Instead, your POV should provide a wide enough scope for you and your team to start thinking about solutions which go beyond status quo.

    Here, you will also learn to frame and open up your point of view, which is the axis that design thinking revolves around – a challenge well-framed is half solved.

    Steps

    1. Define the Type of Person You Are Designing For – Your User

    Select the most essential needs which are the most important to fulfill.

    Work to express the insights developed through the synthesis of your gathered information.



    2. Write Your Definitions Into a Point of View Template

    Example

    UserNeedsInsights
    An adult person who lives in a cityTo use a car for 10-60 minutes 1-4 trips per weekThe user would not want to own a car as it would be too expensive compared to his needs. He would like to share a car with others who have similar needs. However, there are no easy and affordable solutions for him. It is important for the user to think and live green and to not own more than he truly needs.


    3. Create a Point of View Mad Lib

    You can articulate a POV by combining these three elements – user, need, and insight – as an actionable problem statement that will drive the rest of your design work.

    It is surprisingly easy when you insert your findings in the POV Madlib below.

    You can articulate your POV by inserting information about your user, the needs, and your insights in the following sentence:

    [User . . . (descriptive)] needs [Need . . . (verb)] because [Insight . . . (compelling)]

    Example

    An adult person who lives in the city… needs access to a shared car 1-4 times for 10-60 minutes per week … because he would rather share a car with more people as this is cheaper, more environmentally friendly, however it should still be easy for more people to share.



    4. Make Sure That Your Point of View Meets the Requirements

    Your POV should:

    • provide a narrow focus,
    • frame the problem as a problem statement,
    • inspire your team,
    • guide your innovation efforts,
    • inform criteria for evaluating competing ideas,
    • capture people’s attention, and
    • be valid, insightful, actionable, unique, narrow, meaningful, and exciting
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/design/icons/forms_tasks_targets/index.html b/design/icons/forms_tasks_targets/index.html index 35faad2056..8ed47b0d55 100644 --- a/design/icons/forms_tasks_targets/index.html +++ b/design/icons/forms_tasks_targets/index.html @@ -1,9 +1,9 @@ -Icons for Forms, Tasks, Targets | Community Health Toolkit +Icons for Forms, Tasks, Targets | Community Health Toolkit

    Icons for Forms, Tasks, Targets

    Recommended icons for Forms, Tasks, Targets of CHT apps

    Full list of files and alternate formats available in GitHub.

    Icons for Forms, Tasks, Targets

    Recommended icons for Forms, Tasks, Targets of CHT apps

    Full list of files and alternate formats available in GitHub.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/design/icons/forms_tasks_targets/index.xml b/design/icons/forms_tasks_targets/index.xml index 5e93125852..4ecee05206 100644 --- a/design/icons/forms_tasks_targets/index.xml +++ b/design/icons/forms_tasks_targets/index.xml @@ -1 +1 @@ -Community Health Toolkit – Icons for Forms, Tasks, Targetshttps://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/Recent content in Icons for Forms, Tasks, Targets on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file +Icons for Forms, Tasks, Targets on Community Health Toolkithttps://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/Recent content in Icons for Forms, Tasks, Targets on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file diff --git a/design/icons/index.html b/design/icons/index.html index 71592763a1..cef4bc61ed 100644 --- a/design/icons/index.html +++ b/design/icons/index.html @@ -1,9 +1,9 @@ -Icon Library | Community Health Toolkit +Icon Library | Community Health Toolkit

    Icon Library

    Icons for use in CHT application based on our human centered design principles

    We believe in making our simple but strong visual iconography open and accessible to all. We have assembled and designed these icons for use with the Community Health Toolkit based on our human centered design principles.

    About the Icon Library

    Example Icons

    This collection of over 60 beautiful icons was made for use in CHT applications. We will continually update this library as new icons are developed for different workflows.

    Use the forms_tasks_targets folder to find PNG and SVG icons for application code. All instances on 2.15.0 and later should be using SVG icons because they are smaller files. If your project has families or households as the lowest level of the hierarchy, use the people_and_places folder to swap out icons as needed so your hierarchy has the correct icons.

    NOTE: For those using cht-conf to upload resources, make sure you add the correct icons for people and places to your resources.json file. Otherwise, uploading resources will inadvertently remove your people and place icons. An issue is being raised in cht-conf to solve this. You’ll need to define medic-clinic, medic-health-center, medic-district-hospital and medic-person in your resources.json and set them to the appropriate SVG files.

    The design team has put together a resource to help with best practices when selecting and using icons in configuration. Check out the best practices to learn more about which icons can be used for which use cases and workflows.

    Creative Commons License
    This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.


    Icons for Forms, Tasks, Targets

    Recommended icons for Forms, Tasks, Targets of CHT apps

    Icons for People and Places

    Recommended icons for people and places within the hierarchy of CHT apps

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/design/icons/index.xml b/design/icons/index.xml index 3a0edd2016..cc76178eef 100644 --- a/design/icons/index.xml +++ b/design/icons/index.xml @@ -1,499 +1 @@ -Community Health Toolkit – Icon Libraryhttps://docs.communityhealthtoolkit.org/design/icons/Recent content in Icon Library on Community Health ToolkitHugo -- gohugo.ioenDesign: Icons for Forms, Tasks, Targetshttps://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/ -<p>Full list of files and alternate formats <a href=" -https://github.com/medic/cht-docs/tree/main/content/en/design/icons/forms_tasks_targets/. -">available in GitHub.</a></p> -<style> -div.gallery { -display: flex; -flex-wrap: wrap; -} -div.gallery a { -display: block; -width: 200px; -margin: 1em; -flex-wrap: wrap; -text-align: center; -overflow: hidden -} -div.gallery a img { -display: block; -width: 200px; -clear: both; -float: left; -padding: 1em; -border: 2px #cccccc solid; -} -div.gallery a span { -display: block; -clear: left; -float: left; -margin-top: 0.4em; -} -</style> -<div class="gallery"> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/ICCM-immunization.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/ICCM-immunization.svg"> -<span>ICCM-immunization.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/faq.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/faq.svg"> -<span>faq.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/home-delivery.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/home-delivery.svg"> -<span>home-delivery.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-ANC-danger-sign.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-ANC-danger-sign.svg"> -<span>icon-ANC-danger-sign.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-ICCM-danger-sign.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-ICCM-danger-sign.svg"> -<span>icon-ICCM-danger-sign.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-PNC-baby-danger-sign.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-PNC-baby-danger-sign.svg"> -<span>icon-PNC-baby-danger-sign.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-PNC-danger-sign.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-PNC-danger-sign.svg"> -<span>icon-PNC-danger-sign.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-cash-donation.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-cash-donation.svg"> -<span>icon-cash-donation.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-cash-individual-contribution.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-cash-individual-contribution.svg"> -<span>icon-cash-individual-contribution.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-cash-interest.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-cash-interest.svg"> -<span>icon-cash-interest.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-cash-loan-payment-reminder.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-cash-loan-payment-reminder.svg"> -<span>icon-cash-loan-payment-reminder.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-cash-loan-payment.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-cash-loan-payment.svg"> -<span>icon-cash-loan-payment.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-child-cognition.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-child-cognition.svg"> -<span>icon-child-cognition.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-child-growth.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-child-growth.svg"> -<span>icon-child-growth.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-child-nutrition.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-child-nutrition.svg"> -<span>icon-child-nutrition.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-community-meeting-tree.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-community-meeting-tree.svg"> -<span>icon-community-meeting-tree.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-community.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-community.svg"> -<span>icon-community.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-condition-cough.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-condition-cough.svg"> -<span>icon-condition-cough.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-condition-diarrhea.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-condition-diarrhea.svg"> -<span>icon-condition-diarrhea.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-condition-eye.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-condition-eye.svg"> -<span>icon-condition-eye.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-condition-fever.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-condition-fever.svg"> -<span>icon-condition-fever.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-condition-rash.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-condition-rash.svg"> -<span>icon-condition-rash.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-condition-worms.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-condition-worms.svg"> -<span>icon-condition-worms.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-death-coffin-maternal.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-death-coffin-maternal.svg"> -<span>icon-death-coffin-maternal.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-death-coffin-neonatal.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-death-coffin-neonatal.svg"> -<span>icon-death-coffin-neonatal.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-death-coffin.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-death-coffin.svg"> -<span>icon-death-coffin.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-death-general.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-death-general.svg"> -<span>icon-death-general.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-delivery.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-delivery.svg"> -<span>icon-delivery.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-disease-HIV-AIDS.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-disease-HIV-AIDS.svg"> -<span>icon-disease-HIV-AIDS.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-disease-cancer.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-disease-cancer.svg"> -<span>icon-disease-cancer.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-disease-cardiovascular.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-disease-cardiovascular.svg"> -<span>icon-disease-cardiovascular.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-disease-diabetes.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-disease-diabetes.svg"> -<span>icon-disease-diabetes.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-disease-malaria.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-disease-malaria.svg"> -<span>icon-disease-malaria.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-disease-outbreak.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-disease-outbreak.svg"> -<span>icon-disease-outbreak.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-disease-pneumonia.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-disease-pneumonia.svg"> -<span>icon-disease-pneumonia.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-disease-respiratory.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-disease-respiratory.svg"> -<span>icon-disease-respiratory.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-family-planning.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-family-planning.svg"> -<span>icon-family-planning.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-followup-general.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-followup-general.svg"> -<span>icon-followup-general.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-form-general.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-form-general.svg"> -<span>icon-form-general.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-healthcare-assessment.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-healthcare-assessment.svg"> -<span>icon-healthcare-assessment.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-healthcare-campaign.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-healthcare-campaign.svg"> -<span>icon-healthcare-campaign.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-healthcare-diagnosis.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-healthcare-diagnosis.svg"> -<span>icon-healthcare-diagnosis.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-healthcare-generic-2.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-healthcare-generic-2.svg"> -<span>icon-healthcare-generic-2.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-healthcare-generic.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-healthcare-generic.svg"> -<span>icon-healthcare-generic.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-healthcare-immunization.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-healthcare-immunization.svg"> -<span>icon-healthcare-immunization.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-healthcare-medicine.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-healthcare-medicine.svg"> -<span>icon-healthcare-medicine.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-healthcare-mrdt-negative.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-healthcare-mrdt-negative.svg"> -<span>icon-healthcare-mrdt-negative.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-healthcare-mrdt-positive.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-healthcare-mrdt-positive.svg"> -<span>icon-healthcare-mrdt-positive.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-healthcare-mrdt.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-healthcare-mrdt.svg"> -<span>icon-healthcare-mrdt.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-healthcare-refuse.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-healthcare-refuse.svg"> -<span>icon-healthcare-refuse.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-healthcare-shield.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-healthcare-shield.svg"> -<span>icon-healthcare-shield.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-healthcare-warning.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-healthcare-warning.svg"> -<span>icon-healthcare-warning.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-household-bednet.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-household-bednet.svg"> -<span>icon-household-bednet.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-household-education.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-household-education.svg"> -<span>icon-household-education.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-household-equity.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-household-equity.svg"> -<span>icon-household-equity.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-household-foodsecurity.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-household-foodsecurity.svg"> -<span>icon-household-foodsecurity.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-messages-off.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-messages-off.svg"> -<span>icon-messages-off.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-messages-on.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-messages-on.svg"> -<span>icon-messages-on.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-misc-hand-wash.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-misc-hand-wash.svg"> -<span>icon-misc-hand-wash.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-CHW-crop.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-CHW-crop.svg"> -<span>icon-people-CHW-crop.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-CHW-female.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-CHW-female.svg"> -<span>icon-people-CHW-female.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-CHW-male.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-CHW-male.svg"> -<span>icon-people-CHW-male.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-baby.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-baby.svg"> -<span>icon-people-baby.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-child-clinic.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-child-clinic.svg"> -<span>icon-people-child-clinic.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-child-ltf.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-child-ltf.svg"> -<span>icon-people-child-ltf.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-child.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-child.svg"> -<span>icon-people-child.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-children.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-children.svg"> -<span>icon-people-children.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-family.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-family.svg"> -<span>icon-people-family.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-man.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-man.svg"> -<span>icon-people-man.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-manager-crop.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-manager-crop.svg"> -<span>icon-people-manager-crop.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-manager.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-manager.svg"> -<span>icon-people-manager.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-nurse-crop.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-nurse-crop.svg"> -<span>icon-people-nurse-crop.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-nurse.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-nurse.svg"> -<span>icon-people-nurse.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-person-general.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-person-general.svg"> -<span>icon-people-person-general.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-pregnant-clinic.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-pregnant-clinic.svg"> -<span>icon-people-pregnant-clinic.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-pregnant-ltf.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-pregnant-ltf.svg"> -<span>icon-people-pregnant-ltf.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-wheelchair.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-wheelchair.svg"> -<span>icon-people-wheelchair.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-woman-baby-crop.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-woman-baby-crop.svg"> -<span>icon-people-woman-baby-crop.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-woman-baby.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-woman-baby.svg"> -<span>icon-people-woman-baby.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-woman-pregnant.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-woman-pregnant.svg"> -<span>icon-people-woman-pregnant.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-woman.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-people-woman.svg"> -<span>icon-people-woman.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-places-CHW-area.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-places-CHW-area.svg"> -<span>icon-places-CHW-area.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-places-MOH.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-places-MOH.svg"> -<span>icon-places-MOH.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-places-clinic.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-places-clinic.svg"> -<span>icon-places-clinic.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-places-hospital.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-places-hospital.svg"> -<span>icon-places-hospital.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-places-household.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-places-household.svg"> -<span>icon-places-household.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-service-rating.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-service-rating.svg"> -<span>icon-service-rating.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-warning-amber.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-warning-amber.svg"> -<span>icon-warning-amber.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-warning-black.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-warning-black.svg"> -<span>icon-warning-black.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-warning-red.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-warning-red.svg"> -<span>icon-warning-red.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-wash-gender-men.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-wash-gender-men.svg"> -<span>icon-wash-gender-men.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-wash-gender-women.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-wash-gender-women.svg"> -<span>icon-wash-gender-women.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-wash-home-visits-men.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-wash-home-visits-men.svg"> -<span>icon-wash-home-visits-men.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-wash-home-visits-women.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-wash-home-visits-women.svg"> -<span>icon-wash-home-visits-women.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-wash-improved-latrine.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-wash-improved-latrine.svg"> -<span>icon-wash-improved-latrine.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-wash-talks-men.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-wash-talks-men.svg"> -<span>icon-wash-talks-men.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-wash-talks-women.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-wash-talks-women.svg"> -<span>icon-wash-talks-women.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-wash-talks.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/icon-wash-talks.svg"> -<span>icon-wash-talks.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/messages-review.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/messages-review.svg"> -<span>messages-review.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/people-woman-pregnant-1.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/people-woman-pregnant-1.svg"> -<span>people-woman-pregnant-1.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/people-woman-pregnant-2.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/people-woman-pregnant-2.svg"> -<span>people-woman-pregnant-2.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/people-woman-pregnant-3.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/people-woman-pregnant-3.svg"> -<span>people-woman-pregnant-3.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/people-woman-pregnant-4.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/SVGs/people-woman-pregnant-4.svg"> -<span>people-woman-pregnant-4.svg</span> -</a> -</div>Design: Icons for People and Placeshttps://docs.communityhealthtoolkit.org/design/icons/people_and_places/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/icons/people_and_places/ -<p>Full list of files and alternate formats <a href=" -https://github.com/medic/cht-docs/tree/main/content/en/design/icons/people_and_places/. -">available in GitHub.</a></p> -<style> -div.gallery { -display: flex; -flex-wrap: wrap; -} -div.gallery a { -display: block; -width: 200px; -margin: 1em; -flex-wrap: wrap; -text-align: center; -overflow: hidden -} -div.gallery a img { -display: block; -width: 200px; -clear: both; -float: left; -padding: 1em; -border: 2px #cccccc solid; -} -div.gallery a span { -display: block; -clear: left; -float: left; -margin-top: 0.4em; -} -</style> -<div class="gallery"> -<a href="https://docs.communityhealthtoolkit.org/design/icons/people_and_places/SVGs/hierarchies-chw-area.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/people_and_places/SVGs/hierarchies-chw-area.svg"> -<span>hierarchies-chw-area.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/people_and_places/SVGs/hierarchies-district-hospital.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/people_and_places/SVGs/hierarchies-district-hospital.svg"> -<span>hierarchies-district-hospital.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/people_and_places/SVGs/hierarchies-family.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/people_and_places/SVGs/hierarchies-family.svg"> -<span>hierarchies-family.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/people_and_places/SVGs/hierarchies-health-center.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/people_and_places/SVGs/hierarchies-health-center.svg"> -<span>hierarchies-health-center.svg</span> -</a> -<a href="https://docs.communityhealthtoolkit.org/design/icons/people_and_places/SVGs/hierarchies-person.svg"> -<img src="https://docs.communityhealthtoolkit.org/design/icons/people_and_places/SVGs/hierarchies-person.svg"> -<span>hierarchies-person.svg</span> -</a> -</div> \ No newline at end of file +Icon Library on Community Health Toolkithttps://docs.communityhealthtoolkit.org/design/icons/Recent content in Icon Library on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file diff --git a/design/icons/people_and_places/index.html b/design/icons/people_and_places/index.html index 440b9bcea3..19adc2af73 100644 --- a/design/icons/people_and_places/index.html +++ b/design/icons/people_and_places/index.html @@ -1,9 +1,9 @@ -Icons for People and Places | Community Health Toolkit +Icons for People and Places | Community Health Toolkit

    Icons for People and Places

    Recommended icons for people and places within the hierarchy of CHT apps

    Full list of files and alternate formats available in GitHub.

    Icons for People and Places

    Recommended icons for people and places within the hierarchy of CHT apps

    Full list of files and alternate formats available in GitHub.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/design/icons/people_and_places/index.xml b/design/icons/people_and_places/index.xml index 16f040d21b..32d9081374 100644 --- a/design/icons/people_and_places/index.xml +++ b/design/icons/people_and_places/index.xml @@ -1 +1 @@ -Community Health Toolkit – Icons for People and Placeshttps://docs.communityhealthtoolkit.org/design/icons/people_and_places/Recent content in Icons for People and Places on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file +Icons for People and Places on Community Health Toolkithttps://docs.communityhealthtoolkit.org/design/icons/people_and_places/Recent content in Icons for People and Places on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file diff --git a/design/index.html b/design/index.html index 99f50f4ac5..df45047c8d 100644 --- a/design/index.html +++ b/design/index.html @@ -1,9 +1,9 @@ -Design System | Community Health Toolkit +Design System | Community Health Toolkit
    \ No newline at end of file diff --git a/design/index.xml b/design/index.xml index ec6711a87b..edcf9ee000 100644 --- a/design/index.xml +++ b/design/index.xml @@ -1 +1 @@ -Community Health Toolkit – Design Systemhttps://docs.communityhealthtoolkit.org/design/Recent content in Design System on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file +Design System on Community Health Toolkithttps://docs.communityhealthtoolkit.org/design/Recent content in Design System on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file diff --git a/design/mockups/index.html b/design/mockups/index.html index 24593c835b..077a8c2fd5 100644 --- a/design/mockups/index.html +++ b/design/mockups/index.html @@ -1,9 +1,9 @@ -Editable Mockups | Community Health Toolkit +Editable Mockups | Community Health Toolkit

    Editable Mockups

    Slide decks with components for testing and validating concepts

    Use these sample CHT screens and components as needed to test and validate workflows. Make a copy of the slides to edit and use with your content.

    Mobile

    Mockups of the CHT on mobile devices can be easily made using the components in this Google Slide deck.

    Desktop

    Mockups of the CHT in a desktop view are possible using this Google Slide deck.

    -

    Last modified 10.03.2023: Adds text (#943) (66a78e63)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/design/mockups/index.xml b/design/mockups/index.xml index 2a4cc3c399..3a52208105 100644 --- a/design/mockups/index.xml +++ b/design/mockups/index.xml @@ -1 +1 @@ -Community Health Toolkit – Editable Mockupshttps://docs.communityhealthtoolkit.org/design/mockups/Recent content in Editable Mockups on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file +Editable Mockups on Community Health Toolkithttps://docs.communityhealthtoolkit.org/design/mockups/Recent content in Editable Mockups on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file diff --git a/design/personas/app-builder/index.html b/design/personas/app-builder/index.html index 9e16f496e1..da6c2a6226 100644 --- a/design/personas/app-builder/index.html +++ b/design/personas/app-builder/index.html @@ -1,9 +1,9 @@ -App Builders | Community Health Toolkit +App Builders | Community Health Toolkit

    App Builders

    App builders and technical organizations develop and deliver world-class software contextualized to meet their clients’ needs and deliver value to their stakeholders.

    About

    App builders and technical organizations have in-house or contracted software developers. They deploy health technology solutions for implementing partners such an iNGOs, Governments, UN agencies etc in the community health space. They have limited familiarity with end-users such as CHWs and HCD.

    Values

    • Aligned with CHT principles and willing to contribute back to the CHT in the future
    • Believe in building open-source technologies
    • Integrity
    • Likeable personality
    • Open-mindedness
    • Strong work ethic

    Responsibilities

    • Build and deliver user experiences centered on the CHT
    • Steward the CHT app developer’s experience
    • CHT Capacity Builders
    • Technical support
    • Speak the language of the business

    Needs

    • Capacity and expertise with the CHT
    • Flexible technology (and tooling) to help build holistic digital health interventions

    Motivations

    • Build awesome and bug-free user experiences using the CHT
    • Adoption of the CHT as the technology of choice for building community health apps
    • Delivering effective interventions
    • Novel projects or interventions
    • Developing skills

    Strengths and Assets

    • Diversity in skills sets: HCD, software development, database management, dashboard development
    • Resourcefulness: bend the CHT’s capabilities to meet a given need
    • Passion for problem solving
    • An inquisitive mind
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/design/personas/chw-janet/index.html b/design/personas/chw-janet/index.html index 9c19efe0bc..e7f9cf61e6 100644 --- a/design/personas/chw-janet/index.html +++ b/design/personas/chw-janet/index.html @@ -1,9 +1,9 @@ -The Professionalized Community Health Worker (CHW), Janet | Community Health Toolkit +The Professionalized Community Health Worker (CHW), Janet | Community Health Toolkit

    The Professionalized Community Health Worker (CHW), Janet

    CHWs are the central users of apps built with the Core Framework. CHWs conduct household visits and are responsible for the health of their community. CHWs are known and trusted locally and typically live in and are chosen by their community. Their degree of health training, responsibilities, and support depends upon their country and program. The majority of CHWs are women, ranging from 25-60 years old.

    CHW


    “It took a while to be trusted. We had to prove our worth, but now we are well appreciated.”

    About

    Janet is a mother and a farmer. She takes care of her daily BRAC work after finishing her farm work. She has poor eyesight. + Create project issue

    The Professionalized Community Health Worker (CHW), Janet

    CHWs are the central users of apps built with the Core Framework. CHWs conduct household visits and are responsible for the health of their community. CHWs are known and trusted locally and typically live in and are chosen by their community. Their degree of health training, responsibilities, and support depends upon their country and program. The majority of CHWs are women, ranging from 25-60 years old.

    CHW


    “It took a while to be trusted. We had to prove our worth, but now we are well appreciated.”

    About

    Janet is a mother and a farmer. She takes care of her daily BRAC work after finishing her farm work. She has poor eyesight. Janet has a feature phone. She has seen smartphones but never used one. She has no electricity at her home. She charges her phone at her friend’s house. Janet has a trusted boda driver who she uses to help her deliver drugs.

    Values

    • Life: believes in the sanctity of life and strives to protect it by extending health care services
    • Family
    • Education
    • Honesty
    • Faith/Religion

    Responsibilities

    • Registering new people and families
    • Conducting guided health assessments
    • Screening for and tracking specific conditions
    • Providing basic medicines and health supplies
    • Reporting danger signs & referring to clinics
    • Following up about clinic visits and care

    Needs

    • Accurate information about the health status of her patients
    • Close monitoring of the pregnant women to ensure that all deliveries take place at the health facilities
    • All children to be immunized according to the expanded immunization program
    • Children’s growth to be monitored
    • Fast transfer of community members who need emergency health care to the health facilities- ambulance transfer

    Motivations

    • Recognition from managers, colleagues and the broader community
    • Financial rewards such as monthly recharges for SMS, voice calls and data, rewards based on performance metrics, reimbursements or allowances for travel costs between communities being served and facilities
    • Opportunities for continued education, training and skill improvement via refreshers, computer education etc.
    • Career development, especially in younger cohorts: added responsibilities for high-performing CHWs such as training new CHWs or mentoring low-performing CHWs.

    A Day in the Life

    Janet is a CHW who has been working for BRAC for 2 years. She lives in Bembe Central, a periurban town. She visits 10 and 15 homes daily where many of the families live in brick/clay huts reinforced with straw with no electricity, running water or internet connection.

    Janet visits homes based off a list after she finishes her day job. There is no particular order, although she follows the register. While she may get some specific calls, most days she just visits homes and records her visits in the book. Some visits can be as quick as 2 minutes. She walks along a dirt road with her BRAC outfit making her stand out. She often calls out to her neighbors as she walks alon the road. In the rainy season — when diarrhea is often worse — these roads flood. Janet does not visit homes on these days.

    During her visits Janet carries her register, an education booklet, drugs and other sales material in her bag which is very heavy. She readjusts the weight of her bag several times and sometimes drops her drugs. Bodas play an important role in the health system here. Janet has a trusted body driver, whom she asks to deliver drugs.

    On days with her Supervisor, Janet might visit 20-25 homes. The Supervisor joins her at least once a month where they also conduct “health forums” together. Supervisors conduct technical procedures,such as taking BP, marking columns in the CHWs register and later copies this into their own during refresher trainings.

    A normal day lasts about 2 hours.

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/design/personas/chw-supervisor-ann/index.html b/design/personas/chw-supervisor-ann/index.html index 317d41f292..010f3fb824 100644 --- a/design/personas/chw-supervisor-ann/index.html +++ b/design/personas/chw-supervisor-ann/index.html @@ -1,9 +1,9 @@ -CHW Supervisor, Ann | Community Health Toolkit +CHW Supervisor, Ann | Community Health Toolkit

    CHW Supervisor, Ann

    The CHW supervisor is the person who trains and supports CHWs and helps them meet their monthly goals. Supervisors usually split their time between administrative duties at the local health facility and accompanying CHWs on their community visits.

    CHW Supervisor


    “During refresher trainings, when I go there, it takes me 3 hours because I copy from one book to another.”

    About

    Married with two school-aged children + Create project issue

    CHW Supervisor, Ann

    The CHW supervisor is the person who trains and supports CHWs and helps them meet their monthly goals. Supervisors usually split their time between administrative duties at the local health facility and accompanying CHWs on their community visits.

    CHW Supervisor


    “During refresher trainings, when I go there, it takes me 3 hours because I copy from one book to another.”

    About

    Married with two school-aged children Has one year of training in a health-related field Not originally from the community, but communicates effectively with the CHWs Salaried MOH employee Has a personal mobile phone that ranges from a feature phone to a smart phone, used for communication Has an email account primarily accessed via mobile phone

    Responsibilities

    • Overall responsible of the health of the community
    • Manages a group or groups of CHWs
    • Communicates frequently with CHWs and other supervisors
    • Liaises with the facility-based health workers especially the nurses and clinical doctors
    • Mobilizes & leads CHWs to educate community on health promotion & disease prevention
    • Ensures that the CHWs send in data
    • Aggregates data from CHWs and sends to the data records person
    • Attends meetings with MOH officials at the district health center
    • Sits on the community development committee with chiefs and government officers
    • Training CHWs and reinforcing health knowledge and protocols
    • Following up with CHWs on high-priority cases
    • Liaising with the facility-based staff on the needs at the community level
    • Mobilizing CHWs to educate community on health promotion campaigns
    • Tracking progress towards key impact metrics and helping CHWs reach their targets
    • Aggregating CHW data and reporting on activities to health system officials

    Needs

    • Accurate data from the CHWs
    • Ability to send reports to supervisors promptly
    • Mobilize and lead CHWs on community events for health promotion & disease prevention
    • Closely monitor the progress of each individual CHWs and provide supportive supervision
    • Motivate CHWs consistently so that they continue doing their volunteer community service

    Motivations

    • Healthy community
    • Increased utilization of available health services provided at the health center
    • Recognition from supervisor for sending timely and accurate data

    Strengths and Assets

    • Literate
    • Background training in healthcare
    • Salaried (consistent source of income)
    • Familiar with socio-cultural norms of the community & committed to community’s health
    • Mobile phone
    • Earned community respect

    A Day in the Life

    Ann wakes up early in the morning to prepare her son for school and husband for work. She takes her son to school which is on her way to health center where she is based.

    She needs to be at the health center by 9 am so she takes a bus for part of the journey and a motorbike for the rest of the journey. The health center is located about 10 km from her home.

    She attends to her public health officer’s duties based at the health center. She is primarily in charge of the community and market sanitation, so she facilitates the small scale traders who run business at the nearby market to get health check-ups that are mandatory before she issues them a certified health certificate that allows them to run food related businesses in the market.

    In the course of her day, she collaborates with a wide range of stakeholders in order to perform her chores. She liaises with the clinicians and nurses to identify defaulters of care in order to organize for the follows up through the community health workers. The nurse would give her a list of children who have missed their vaccine appointments, the pregnant women who have missed their antenatal care clinics, the Tuberculosis and HIV/AIDs clinicians also have a list of defaulters of care that should be followed to ensure prompt care. She must liaises with the CHWs to be trace these patients and reintroduce them to care. This is one of her greatest challenge as it is hard to trace the children in the wide facility catchment area. You know it is her responsibility to ensure that the community health workers are creating demand for the health services at the community level and ensuring that the community members access the quality care at the health facilities. She assesses the performance of her CHWs by looking at how many community members they refer to the health facilities for care, and the adherence to care of the patients referred by each CHW. This is no mean easy task as CHWs are not paid and the villages they cover are wide and a very hilly terrain which becomes impossible during the rainy weather. Over half of the CHWs that she trained during the initiation of the CHW program in her area three years have dropped, and the new ones she has replaced need to be trained on the whole package of their role. She would gladly do this but “Oh, when will an non governmental organization come by to support us in this capacity building activity?” She exclaims. Meanwhile she must support the new CHWs by accompanying them at least once a month when they are doing their household visits in the villages.

    Since there is an upcoming community based polio and tetanus campaign targeted for under-fives and women of reproductive health, she must select and contact several CHWs from each village that will help them administer these vaccines in their villages and therefore reach the most possible target population.

    She must also liaise with the area administrative chief who provide the go ahead of the community based activities to mobilize the community for health education and dialogue days and community health related activities. She and her CHWs lead the community to discuss on various disease preventive and health promotive activities that are tailored to meet health needs/gaps they have observed within the community. They use these regular forums to reinforce important health messages like the importance pregnant women going for ANC and children for immunization. She leads the community health workers and community members during community action days.

    She also works hand in hand with the schools heads for deworming and Vitamin A campaign targeted at children, a regular activity which she carries out with the CHWs.

    Ann’s 80% of her time is spent interacting with the community and the community health workers. This month’s monthly meeting with the CHWs is early next week. She must make some time to prepare some notes on how to make drinking water safe for use in the community, during a visit in the community, she observed that the community was using unsafe water for drinking and the CHW did not know what to advise her households to make the water safe. The CHEW continues to offer need based training to the CHWs depending on assessment of the knowledge deficits of the CHWs after the initial training. He guides the CHWs, on how to effectively communicate well with the community. She intents to guide the CHWs on the data items to collect as most of them have not been sending all the reports as required. If transport within the community was cheap or easier she would visit the CHWs who did not send reports and or those who send inaccurate data or incomplete reports and walk with them as they collect the data.. As a CHEW, she is charged by the MOH to aggregate data about the community’s state of health that is received from the CHWs on a monthly basis. The primary data items for her reflect the utilization of health services by the community.

    Ann has a regular end of month meeting with her community health supervisor who is based in the Sub district hospital that is 150km away from her station early in the morning on Wednesday. She must prepare and present her monthly written reports and give a verbal report of the state of the community and performance of the CHWs under her supervision.

    Ann gets back to her home at 6 pm, she picks her son from school where he had been playing since 4pm when the other pupils leave school for home. She prepares supper for her family, and carries out the other family chores. She retires to bed at about 10pm tired but happy to have accomplished her day’s duties.

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/design/personas/data-manager-paul/index.html b/design/personas/data-manager-paul/index.html index ec3e93b30f..89b40a6512 100644 --- a/design/personas/data-manager-paul/index.html +++ b/design/personas/data-manager-paul/index.html @@ -1,9 +1,9 @@ -Data Manager, Paul | Community Health Toolkit +Data Manager, Paul | Community Health Toolkit

    Data Manager, Paul

    Data Managers are often based at a regional health facility or a program or administrative unit and serve many local facilities. They are responsible for collating and reporting on community and health system data. Their work often involves following up with supervisors and nurses to verify data and retrieve missing information.

    HRIO


    “It’s all about training them. The Community Health Workers are just villagers and farmers, so it’s training and more training.”

    About

    Paul has worked at BRAC for two years. He’s very focused on his career and has lots of IT projects running. He is responsible for training the branches on how to use the mobile phones. Right now Paul is looking for the right quality of phones. He also wants to understand how the database will ensure reliable syncing.

    Values

    • Critical thinker
    • Strong communication skills
    • Integrity
    • Creativity
    • Accuracy and attention to detail
    • Problem solving skills
    • Team player

    Responsibilities

    • Collating health system data from the field
    • Verifying data for accuracy and completeness
    • Aggregating data and providing reports to nurses, supervisors, and health system officials with raw numbers and trends on key metrics
    • Designing and maintaining data systems and databases
    • Using statistical tools to interpret data sets
    • Preparing reports that effectively communicate trends, patterns and interesting observations

    Needs

    • Accurate data reported by CHWs and verified by CHW Supervisors
    • Systems that check the completeness of data
    • Access to statistical tools
    • Robust systems that can handle large data sets

    Motivations

    • Helping the organisation to make data driven decisions
    • Employing new skills learnt from colleagues

    Strengths and Assets

    • Statistical expertise
    • Presentation skills
    • Data visualization techniques
    • Methodological
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/design/personas/index.html b/design/personas/index.html index bdc9e80711..c578cabac1 100644 --- a/design/personas/index.html +++ b/design/personas/index.html @@ -1,9 +1,9 @@ -User Personas | Community Health Toolkit +User Personas | Community Health Toolkit

    User Personas

    The “typical” users of CHT Apps across diverse contexts

    User personas give us a common understanding of who we are serving, particularly when working across diverse contexts. Our global personas are based on “typical” users, knowing that some variation is present in different settings.

    Being explicit about who are we designing with and for, and understanding what’s important to them helps us prioritize features, make better design decisions, and optimize impact.


    The Professionalized Community Health Worker (CHW), Janet

    CHWs are the central users of apps built with the Core Framework. CHWs conduct household visits and are responsible for the health of their community. CHWs are known and trusted locally and typically live in and are chosen by their community. Their degree of health training, responsibilities, and support depends upon their country and program. The majority of CHWs are women, ranging from 25-60 years old.

    CHW Supervisor, Ann

    The CHW supervisor is the person who trains and supports CHWs and helps them meet their monthly goals. Supervisors usually split their time between administrative duties at the local health facility and accompanying CHWs on their community visits.

    Regional Manager, Christina

    Regional managers provide overall oversight of activities in the region they are assigned. They are employed by technical and implementing partners to oversee and provide general guidance for two or more branches in a region. They troubleshoot and provide support to branch managers to optimise operations. They have limited interactions with end users but provide the link between field operations and the head office.

    Nurse, Mary

    Nurses are stationed at the health facility and spend their days seeing patients. They are typically very busy and may see 50 or more patients a day. At the clinic, they sometimes deal with staff shortages, stock-outs, and poor internet connectivity. They help train and manage CHWs, particularly during monthly meetings at the facility. They are interested in seeing improvements in health metrics for the areas their facility serves.

    Data Manager, Paul

    Data Managers are often based at a regional health facility or a program or administrative unit and serve many local facilities. They are responsible for collating and reporting on community and health system data. Their work often involves following up with supervisors and nurses to verify data and retrieve missing information.

    App Builders

    App builders and technical organizations develop and deliver world-class software contextualized to meet their clients’ needs and deliver value to their stakeholders.

    Partner Personas

    An overview of CHT Partner Personas

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/design/personas/index.xml b/design/personas/index.xml index 73d4890963..16ac6b5e60 100644 --- a/design/personas/index.xml +++ b/design/personas/index.xml @@ -1,305 +1,9 @@ -Community Health Toolkit – User Personashttps://docs.communityhealthtoolkit.org/design/personas/Recent content in User Personas on Community Health ToolkitHugo -- gohugo.ioenDesign: The Professionalized Community Health Worker (CHW), Janethttps://docs.communityhealthtoolkit.org/design/personas/chw-janet/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/chw-janet/ -<p><img src="chw.png" alt="CHW"></p> -<br> -<blockquote> -<p>&ldquo;It took a while to be trusted. We had to prove our worth, but now we are well appreciated.&rdquo;</p> -</blockquote> -<h2 id="about">About</h2> -<p>Janet is a mother and a farmer. She takes care of her daily BRAC work after finishing her farm work. She has poor eyesight. -Janet has a feature phone. She has seen smartphones but never used one. She has no electricity at her home. She charges her phone at her friend’s house. -Janet has a trusted boda driver who she uses to help her deliver drugs.</p> -<h2 id="values">Values</h2> -<ul> -<li>Life: believes in the sanctity of life and strives to protect it by extending health care services</li> -<li>Family</li> -<li>Education</li> -<li>Honesty</li> -<li>Faith/Religion</li> -</ul> -<h2 id="responsibilities">Responsibilities</h2> -<ul> -<li>Registering new people and families</li> -<li>Conducting guided health assessments</li> -<li>Screening for and tracking specific conditions</li> -<li>Providing basic medicines and health supplies</li> -<li>Reporting danger signs &amp; referring to clinics</li> -<li>Following up about clinic visits and care</li> -</ul> -<h2 id="needs">Needs</h2> -<ul> -<li>Accurate information about the health status of her patients</li> -<li>Close monitoring of the pregnant women to ensure that all deliveries take place at the health facilities</li> -<li>All children to be immunized according to the expanded immunization program</li> -<li>Children&rsquo;s growth to be monitored</li> -<li>Fast transfer of community members who need emergency health care to the health facilities- ambulance transfer</li> -</ul> -<h2 id="motivations">Motivations</h2> -<ul> -<li>Recognition from managers, colleagues and the broader community</li> -<li>Financial rewards such as monthly recharges for SMS, voice calls and data, rewards based on performance metrics, reimbursements or allowances for travel costs between communities being served and facilities</li> -<li>Opportunities for continued education, training and skill improvement via refreshers, computer education etc.</li> -<li>Career development, especially in younger cohorts: added responsibilities for high-performing CHWs such as training new CHWs or mentoring low-performing CHWs.</li> -</ul> -<h2 id="a-day-in-the-life">A Day in the Life</h2> -<p>Janet is a CHW who has been working for BRAC for 2 years. She lives in Bembe Central, a periurban town. She visits 10 and 15 homes daily where many of the families live in brick/clay huts reinforced with straw with no electricity, running water or internet connection.</p> -<p>Janet visits homes based off a list after she finishes her day job. There is no particular order, although she follows the register. While she may get some specific calls, most days she just visits homes and records her visits in the book. Some visits can be as quick as 2 minutes. She walks along a dirt road with her BRAC outfit making her stand out. She often calls out to her neighbors as she walks alon the road. In the rainy season — when diarrhea is often worse — these roads flood. Janet does not visit homes on these days.</p> -<p>During her visits Janet carries her register, an education booklet, drugs and other sales material in her bag which is very heavy. She readjusts the weight of her bag several times and sometimes drops her drugs. Bodas play an important role in the health system here. Janet has a trusted body driver, whom she asks to deliver drugs.</p> -<p>On days with her Supervisor, Janet might visit 20-25 homes. The Supervisor joins her at least once a month where they also conduct “health forums” together. Supervisors conduct technical procedures,such as taking BP, marking columns in the CHWs register and later copies this into their own during refresher trainings.</p> -<p>A normal day lasts about 2 hours.</p>Design: CHW Supervisor, Annhttps://docs.communityhealthtoolkit.org/design/personas/chw-supervisor-ann/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/chw-supervisor-ann/ -<p><img src="supervisor.png" alt="CHW Supervisor"></p> -<br> -<blockquote> -<p>“During refresher trainings, when I go there, it takes me 3 hours because I copy from one book to another.”</p> -</blockquote> -<h2 id="about">About</h2> -<p>Married with two school-aged children -Has one year of training in a health-related field -Not originally from the community, but communicates effectively with the CHWs -Salaried MOH employee -Has a personal mobile phone that ranges from a feature phone to a smart phone, used for communication -Has an email account primarily accessed via mobile phone</p> -<h2 id="responsibilities">Responsibilities</h2> -<ul> -<li>Overall responsible of the health of the community</li> -<li>Manages a group or groups of CHWs</li> -<li>Communicates frequently with CHWs and other supervisors</li> -<li>Liaises with the facility-based health workers especially the nurses and clinical doctors</li> -<li>Mobilizes &amp; leads CHWs to educate community on health promotion &amp; disease prevention</li> -<li>Ensures that the CHWs send in data</li> -<li>Aggregates data from CHWs and sends to the data records person</li> -<li>Attends meetings with MOH officials at the district health center</li> -<li>Sits on the community development committee with chiefs and government officers</li> -<li>Training CHWs and reinforcing health knowledge and protocols</li> -<li>Following up with CHWs on high-priority cases</li> -<li>Liaising with the facility-based staff on the needs at the community level</li> -<li>Mobilizing CHWs to educate community on health promotion campaigns</li> -<li>Tracking progress towards key impact metrics and helping CHWs reach their targets</li> -<li>Aggregating CHW data and reporting on activities to health system officials</li> -</ul> -<h2 id="needs">Needs</h2> -<ul> -<li>Accurate data from the CHWs</li> -<li>Ability to send reports to supervisors promptly</li> -<li>Mobilize and lead CHWs on community events for health promotion &amp; disease prevention</li> -<li>Closely monitor the progress of each individual CHWs and provide supportive supervision</li> -<li>Motivate CHWs consistently so that they continue doing their volunteer community service</li> -</ul> -<h2 id="motivations">Motivations</h2> -<ul> -<li>Healthy community</li> -<li>Increased utilization of available health services provided at the health center</li> -<li>Recognition from supervisor for sending timely and accurate data</li> -</ul> -<h2 id="strengths-and-assets">Strengths and Assets</h2> -<ul> -<li>Literate</li> -<li>Background training in healthcare</li> -<li>Salaried (consistent source of income)</li> -<li>Familiar with socio-cultural norms of the community &amp; committed to community’s health</li> -<li>Mobile phone</li> -<li>Earned community respect</li> -</ul> -<h2 id="a-day-in-the-life">A Day in the Life</h2> -<p>Ann wakes up early in the morning to prepare her son for school and husband for work. She takes her son to school which is on her way to health center where she is based.</p> -<p>She needs to be at the health center by 9 am so she takes a bus for part of the journey and a motorbike for the rest of the journey. The health center is located about 10 km from her home.</p> -<p>She attends to her public health officer’s duties based at the health center. She is primarily in charge of the community and market sanitation, so she facilitates the small scale traders who run business at the nearby market to get health check-ups that are mandatory before she issues them a certified health certificate that allows them to run food related businesses in the market.</p> -<p>In the course of her day, she collaborates with a wide range of stakeholders in order to perform her chores. She liaises with the clinicians and nurses to identify defaulters of care in order to organize for the follows up through the community health workers. The nurse would give her a list of children who have missed their vaccine appointments, the pregnant women who have missed their antenatal care clinics, the Tuberculosis and HIV/AIDs clinicians also have a list of defaulters of care that should be followed to ensure prompt care. She must liaises with the CHWs to be trace these patients and reintroduce them to care. This is one of her greatest challenge as it is hard to trace the children in the wide facility catchment area. You know it is her responsibility to ensure that the community health workers are creating demand for the health services at the community level and ensuring that the community members access the quality care at the health facilities. She assesses the performance of her CHWs by looking at how many community members they refer to the health facilities for care, and the adherence to care of the patients referred by each CHW. This is no mean easy task as CHWs are not paid and the villages they cover are wide and a very hilly terrain which becomes impossible during the rainy weather. Over half of the CHWs that she trained during the initiation of the CHW program in her area three years have dropped, and the new ones she has replaced need to be trained on the whole package of their role. She would gladly do this but “Oh, when will an non governmental organization come by to support us in this capacity building activity?” She exclaims. Meanwhile she must support the new CHWs by accompanying them at least once a month when they are doing their household visits in the villages.</p> -<p>Since there is an upcoming community based polio and tetanus campaign targeted for under-fives and women of reproductive health, she must select and contact several CHWs from each village that will help them administer these vaccines in their villages and therefore reach the most possible target population.</p> -<p>She must also liaise with the area administrative chief who provide the go ahead of the community based activities to mobilize the community for health education and dialogue days and community health related activities. She and her CHWs lead the community to discuss on various disease preventive and health promotive activities that are tailored to meet health needs/gaps they have observed within the community. They use these regular forums to reinforce important health messages like the importance pregnant women going for ANC and children for immunization. She leads the community health workers and community members during community action days.</p> -<p>She also works hand in hand with the schools heads for deworming and Vitamin A campaign targeted at children, a regular activity which she carries out with the CHWs.</p> -<p>Ann’s 80% of her time is spent interacting with the community and the community health workers. This month’s monthly meeting with the CHWs is early next week. She must make some time to prepare some notes on how to make drinking water safe for use in the community, during a visit in the community, she observed that the community was using unsafe water for drinking and the CHW did not know what to advise her households to make the water safe. The CHEW continues to offer need based training to the CHWs depending on assessment of the knowledge deficits of the CHWs after the initial training. He guides the CHWs, on how to effectively communicate well with the community. She intents to guide the CHWs on the data items to collect as most of them have not been sending all the reports as required. If transport within the community was cheap or easier she would visit the CHWs who did not send reports and or those who send inaccurate data or incomplete reports and walk with them as they collect the data.. As a CHEW, she is charged by the MOH to aggregate data about the community’s state of health that is received from the CHWs on a monthly basis. The primary data items for her reflect the utilization of health services by the community.</p> -<p>Ann has a regular end of month meeting with her community health supervisor who is based in the Sub district hospital that is 150km away from her station early in the morning on Wednesday. She must prepare and present her monthly written reports and give a verbal report of the state of the community and performance of the CHWs under her supervision.</p> -<p>Ann gets back to her home at 6 pm, she picks her son from school where he had been playing since 4pm when the other pupils leave school for home. She prepares supper for her family, and carries out the other family chores. She retires to bed at about 10pm tired but happy to have accomplished her day’s duties.</p>Design: Regional Manager, Christinahttps://docs.communityhealthtoolkit.org/design/personas/regional-manager-christina/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/regional-manager-christina/ -<p><img src="regional-manager.png" alt="Regional Manager"></p> -<br> -<blockquote> -<p>“I heard that some branches have already had challenges with mobile phones.”</p> -</blockquote> -<h2 id="about">About</h2> -<p>Christina been working at BRAC for six years. She manages four branches and works closely with the CHW Managers there. She makes sure CHW Managers are monitoring CHWs. She occasionally does random checks, driving out to villages and observing Managers with CHWs.</p> -<h2 id="values">Values</h2> -<ul> -<li>Visionary</li> -<li>Integrity</li> -<li>Good communication</li> -<li>Respect</li> -<li>Responsibility</li> -<li>Wisdom</li> -<li>Empathy</li> -</ul> -<h2 id="responsibilities">Responsibilities</h2> -<ul> -<li>Overall responsibility of the Region’s activities</li> -<li>Oversees implementation of organisational goals</li> -<li>Communicates organisational goals and strategies to CHW managers</li> -<li>Liaise and ensure compliance with Ministries, NGO boards and regulatory bodies</li> -<li>Promote advocacy efforts of the program</li> -<li>Supervises and supports staff to attain their career goals</li> -<li>Approval of quarterly logistic plans for CHW managers</li> -<li>Reviews and approves CHW’s continuous education programs</li> -<li>Evaluates programs impact and advises on key priority areas of focus</li> -<li>Realignment of programs to changing ecosystem</li> -<li>Budgetary planning for region’s activities</li> -<li>Tracking branches’ and CHW manager’s performance</li> -<li>Monthly indicators tracking to ensure realization of organisational goals</li> -</ul> -<h2 id="needs">Needs</h2> -<ul> -<li>Real time access of data on supervision activities ongoing in their region</li> -<li>Access to summary statistics on project indicators</li> -<li>Timely submission of monthly reports on retention and turnover rates for CHWs</li> -<li>Escalation of CHW retraining needs, challenges and proposed solutions</li> -<li>Opportunities to conduct random checks to assess the CHWs perceptions of their managers support</li> -</ul> -<h2 id="motivations">Motivations</h2> -<ul> -<li>Desire to improve lives of communities</li> -<li>Desire to practice community health strategies learnt at school</li> -<li>Organisational goals align well with personal goals</li> -</ul> -<h2 id="strengths-and-assets">Strengths and Assets</h2> -<ul> -<li>Provided with a company car and computer</li> -<li>Has a welcoming heart and accessible to employees</li> -<li>Team player</li> -</ul>Design: Nurse, Maryhttps://docs.communityhealthtoolkit.org/design/personas/nurse-mary/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/nurse-mary/ -<p><img src="nurse.png" alt="Nurse"></p> -<br> -<h2 id="about">About</h2> -<p>Mary has formal clinical training but a limited ability to conduct complex surgical procedures. She sees +/- 50 patients a day, sometimes in their homes. She is literate in English and understands local dialects. She is sometimes stationed far from her rural town/family in spurts and makes regular trips to districts to hand-deliver reports.</p> -<p>Mary lives on $5/day and depends mostly on solar power at the clinic as she may not have electricity at home. She is enthusiastic about technology but has had little exposure. She has an email address that she does not check regularly and has a basic understanding of Windows OS. She uses Facebook and M-PESA on a low-end phone.</p> -<h2 id="values">Values</h2> -<ul> -<li>Good health. Anyone is only able to work and therefore take care of ones and other people’s needs if and when in really good health</li> -<li>Family is the essence of one’s existence and that each child is entitled to love that can be best provided and experienced in the family setting</li> -<li>Education is the ticket for better, healthier and happier life for everyone</li> -</ul> -<h2 id="responsibilities">Responsibilities</h2> -<ul> -<li>Assessing patients and providing primary care</li> -<li>Reporting service delivery statistics to health system officials</li> -<li>Coordinating care for high-priority patients through CHWs and supervisors</li> -<li>Initiating events to promote healthy practices in the community</li> -</ul> -<h2 id="needs">Needs</h2> -<ul> -<li>Nurse Mary wants nil (zero) maternal and child mortalities in the community served by the health facility</li> -<li>Nurse Mary needs all the community to be aware of the health care services that they can access at the health facility</li> -<li>Nurse Mary needs everyone in the community to have access and adhere to continuous quality care provided at the health facility</li> -<li>Nurse Mary needs all defaulters to health care available at the health facility that are in the community to be traced and re-introduced to care</li> -<li>Nurse Mary needs to keep and get accurate data of the target population that she provides health care for</li> -<li>Nurse Mary needs to keep abreast with new editions of nursing procedures through regular trainings/seminars</li> -</ul> -<h2 id="motivations">Motivations</h2> -<ul> -<li>Community enjoying services of skilled health professionals for deliveries which translates to no or very few maternal and child mortalities. More money for her to run the facility as finances for performance is a policy that is in use now in Kenya.</li> -<li>Healthier children, with fewer neonatal, infant or child mortalities, and 100% full immunization of all under-fives.</li> -<li>Progress within the nursing profession with continues and recognized certifications.</li> -<li>Model health facility where other health facilities are encouraged to emulate and adopt those standards.</li> -<li>Prompt report submissions to supervisors and the recognition by the supervisors for that.</li> -</ul> -<h2 id="strengths-and-assets">Strengths and Assets</h2> -<ul> -<li>Literate</li> -<li>Strong clinical knowledge</li> -<li>Organizational skills</li> -<li>Constant source of income-employment by the government</li> -<li>Mobile phones: smart phones, facebook, Whatsapp and access to email</li> -<li>Computer literacy: access email, basic understanding of the windows OS</li> -<li>Access to either solar or electricity power at the health center</li> -</ul> -<h2 id="a-day-in-the-life">A Day in the Life</h2> -<p>Her day at work starts early as she must be on duty at 7 am, to relieve the nurse who has been on cover of the health facility at night if she is in a health facility that as at least 2 of them. In the health facility, where she is the only nurse she comes to pick up from where she left last evening, if she was “lucky” not to be woken up to attend to a patient at night.</p> -<p>She gets a report from the “night nurse” of all the in-patients-mainly women in labor, newborns born at night, children less than 5 years brought in at night for care and injuries resulting from accidents.</p> -<p>8am going to 9am, the outpatient is swollen with patients: mothers with under five children bringing them to the child welfare clinic, pregnant women some with under five children coming also for antenatal care, and elderly women and men, all weak, sick looking and would pass for nutritional support-if only there were supplements for the elderly. The ones in store are for the under 5 malnourished and pregnant women.</p> -<p>Anyway, she notes the need to do a health education session on how to eat a well-nourished meal using the locally available food stuffs, but now, she must do one on how to prevent contracting cholera as cholera as for the last 2 months being reported in many parts of the country. It is a regular practice to carry out health education sessions to the out patients. She will also talk to the CHEW today for a health education session with the CHWs during their monthly meeting on cholera prevention that the CHWs must disseminate to the community. She and the field CHEW must liaise consistently as they work together with the CHWs to promote the health of the community. Her role is to oversee the field CHEW and to help her meet the knowledge gap of the CHWs by consistently teaching CHWs on common local diseases and how to prevent them, identifying them, giving first aid and referring patients for specialized health care.</p> -<p>The CHWs are critical to her as they are the link between the community and the health facility that is very important. The CHWs help her trace the children under five lost to follow up, malnourished children in the community who need follow up, chronically ill patients in the community that need home based care and refer and support orphans and vulnerable children in the community. The field CHEW co-ordinates this activity, after giving her the list of the patients to be followed.</p> -<p>Her next in line activity is to see the ANC mothers, she co-ordinates their care, sends them for laboratory work, prescribe treatments and supplements based to the general health assessments of the woman and the lab results. She records care given in the maternal child booklet, and advises her on when to come for her next follow up. She briefly counsels on how to take care of herself during pregnancy, feed well and the exercises to take, maintain good hygiene and to plan to deliver her baby in the health facility: the individual birth plan, must be reinforced by the CHWs who can afford some more time for a one to one talk with the woman. She keeps a record of all the pregnant women she sees, both the new and those that come for subsequent visit. She will write a report based on this at the end of the month showing the new pregnancies, those that have it 4+ANC visits, new PMTC cases among other details.</p> -<p>She will also attend to the under-fives, both those that have come for the routine immunization and growth monitoring and the sick ones who were brought for treatment. She will record all the care given in the mother child booklet and give brief advice on nutrition of the child while charting the weight on the growth monitoring chart. She will attend to the other sickly patients as well, give family planning counseling and services, and monitor the mothers in labor in the ward. Among other duties that she must attend to are emergency cases referred from the community, accompanying complicated cases to the higher level hospital for more specialized care, and assisting the doctors and clinicians in handling special cases when in facility like a hospital where these services are offered.</p> -<p>The day has not started or ended if she has not read the temperature of the thermometers of the vaccine boxes to ensure that it is in the required temperature ranges and therefore the boxes are in good order.</p> -<p>Her monthly reports range from daily vaccine monitoring chart, to the ANC, immunization related records to the vaccine usage rates for the month. She places monthly based order for vaccine supplies based on the month’s usage, and must collect the vaccines from the sub-county headquarters before the end of the first week of the month. She attends a monthly visit organized by her supervisors at the sub-county district, at least monthly and frequently meets her colleagues during the irregular continuous education seminars organized by their supervisors.</p> -<p>Nurse Mary faces a lot of challenges, but the most pressing is the workload coupled with the intensity of the nature of the work that she faces day in, day out. She is entitled to work a maximum of 45 hours a week, but is that really what happens?</p>Design: Data Manager, Paulhttps://docs.communityhealthtoolkit.org/design/personas/data-manager-paul/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/data-manager-paul/ -<p><img src="hrio.png" alt="HRIO"></p> -<br> -<blockquote> -<p>“It’s all about training them. The Community Health Workers are just villagers and farmers, so it’s training and more training.”</p> -</blockquote> -<h2 id="about">About</h2> -<p>Paul has worked at BRAC for two years. He’s very focused on his career and has lots of IT projects running. He is responsible for training the branches on how to use the mobile phones. Right now Paul is looking for the right quality of phones. He also wants to understand how the database will ensure reliable syncing.</p> -<h2 id="values">Values</h2> -<ul> -<li>Critical thinker</li> -<li>Strong communication skills</li> -<li>Integrity</li> -<li>Creativity</li> -<li>Accuracy and attention to detail</li> -<li>Problem solving skills</li> -<li>Team player</li> -</ul> -<h2 id="responsibilities">Responsibilities</h2> -<ul> -<li>Collating health system data from the field</li> -<li>Verifying data for accuracy and completeness</li> -<li>Aggregating data and providing reports to nurses, supervisors, and health system officials with raw numbers and trends on key metrics</li> -<li>Designing and maintaining data systems and databases</li> -<li>Using statistical tools to interpret data sets</li> -<li>Preparing reports that effectively communicate trends, patterns and interesting observations</li> -</ul> -<h2 id="needs">Needs</h2> -<ul> -<li>Accurate data reported by CHWs and verified by CHW Supervisors</li> -<li>Systems that check the completeness of data</li> -<li>Access to statistical tools</li> -<li>Robust systems that can handle large data sets</li> -</ul> -<h2 id="motivations">Motivations</h2> -<ul> -<li>Helping the organisation to make data driven decisions</li> -<li>Employing new skills learnt from colleagues</li> -</ul> -<h2 id="strengths-and-assets">Strengths and Assets</h2> -<ul> -<li>Statistical expertise</li> -<li>Presentation skills</li> -<li>Data visualization techniques</li> -<li>Methodological</li> -</ul>Design: App Buildershttps://docs.communityhealthtoolkit.org/design/personas/app-builder/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/app-builder/ -<h2 id="about">About</h2> -<p>App builders and technical organizations have in-house or contracted software developers. They deploy health technology solutions for implementing partners such an iNGOs, Governments, UN agencies etc in the community health space. They have limited familiarity with end-users such as CHWs and HCD.</p> -<h2 id="values">Values</h2> -<ul> -<li>Aligned with CHT principles and willing to contribute back to the CHT in the future</li> -<li>Believe in building open-source technologies</li> -<li>Integrity</li> -<li>Likeable personality</li> -<li>Open-mindedness</li> -<li>Strong work ethic</li> -</ul> -<h2 id="responsibilities">Responsibilities</h2> -<ul> -<li>Build and deliver user experiences centered on the CHT</li> -<li>Steward the CHT app developer’s experience</li> -<li>CHT Capacity Builders</li> -<li>Technical support</li> -<li>Speak the language of the business</li> -</ul> -<h2 id="needs">Needs</h2> -<ul> -<li>Capacity and expertise with the CHT</li> -<li>Flexible technology (and tooling) to help build holistic digital health interventions</li> -</ul> -<h2 id="motivations">Motivations</h2> -<ul> -<li>Build awesome and bug-free user experiences using the CHT</li> -<li>Adoption of the CHT as the technology of choice for building community health apps</li> -<li>Delivering effective interventions</li> -<li>Novel projects or interventions</li> -<li>Developing skills</li> -</ul> -<h2 id="strengths-and-assets">Strengths and Assets</h2> -<ul> -<li>Diversity in skills sets: HCD, software development, database management, dashboard development</li> -<li>Resourcefulness: bend the CHT&rsquo;s capabilities to meet a given need</li> -<li>Passion for problem solving</li> -<li>An inquisitive mind</li> -</ul>Design: Partner Personashttps://docs.communityhealthtoolkit.org/design/personas/partners/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/partners/ \ No newline at end of file +User Personas on Community Health Toolkithttps://docs.communityhealthtoolkit.org/design/personas/Recent content in User Personas on Community Health ToolkitHugo -- gohugo.ioenThe Professionalized Community Health Worker (CHW), Janethttps://docs.communityhealthtoolkit.org/design/personas/chw-janet/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/chw-janet/&ldquo;It took a while to be trusted. We had to prove our worth, but now we are well appreciated.&rdquo; +About Janet is a mother and a farmer. She takes care of her daily BRAC work after finishing her farm work. She has poor eyesight. Janet has a feature phone. She has seen smartphones but never used one. She has no electricity at her home. She charges her phone at her friend’s house.CHW Supervisor, Annhttps://docs.communityhealthtoolkit.org/design/personas/chw-supervisor-ann/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/chw-supervisor-ann/“During refresher trainings, when I go there, it takes me 3 hours because I copy from one book to another.” +About Married with two school-aged children Has one year of training in a health-related field Not originally from the community, but communicates effectively with the CHWs Salaried MOH employee Has a personal mobile phone that ranges from a feature phone to a smart phone, used for communication Has an email account primarily accessed via mobile phoneRegional Manager, Christinahttps://docs.communityhealthtoolkit.org/design/personas/regional-manager-christina/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/regional-manager-christina/ +“I heard that some branches have already had challenges with mobile phones.” +About Christina been working at BRAC for six years. She manages four branches and works closely with the CHW Managers there. She makes sure CHW Managers are monitoring CHWs. She occasionally does random checks, driving out to villages and observing Managers with CHWs. +Values Visionary Integrity Good communication Respect Responsibility Wisdom Empathy Responsibilities Overall responsibility of the Region’s activities Oversees implementation of organisational goals Communicates organisational goals and strategies to CHW managers Liaise and ensure compliance with Ministries, NGO boards and regulatory bodies Promote advocacy efforts of the program Supervises and supports staff to attain their career goals Approval of quarterly logistic plans for CHW managers Reviews and approves CHW’s continuous education programs Evaluates programs impact and advises on key priority areas of focus Realignment of programs to changing ecosystem Budgetary planning for region’s activities Tracking branches’ and CHW manager’s performance Monthly indicators tracking to ensure realization of organisational goals Needs Real time access of data on supervision activities ongoing in their region Access to summary statistics on project indicators Timely submission of monthly reports on retention and turnover rates for CHWs Escalation of CHW retraining needs, challenges and proposed solutions Opportunities to conduct random checks to assess the CHWs perceptions of their managers support Motivations Desire to improve lives of communities Desire to practice community health strategies learnt at school Organisational goals align well with personal goals Strengths and Assets Provided with a company car and computer Has a welcoming heart and accessible to employees Team playerNurse, Maryhttps://docs.communityhealthtoolkit.org/design/personas/nurse-mary/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/nurse-mary/About Mary has formal clinical training but a limited ability to conduct complex surgical procedures. She sees +/- 50 patients a day, sometimes in their homes. She is literate in English and understands local dialects. She is sometimes stationed far from her rural town/family in spurts and makes regular trips to districts to hand-deliver reports. +Mary lives on $5/day and depends mostly on solar power at the clinic as she may not have electricity at home.Data Manager, Paulhttps://docs.communityhealthtoolkit.org/design/personas/data-manager-paul/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/data-manager-paul/“It’s all about training them. The Community Health Workers are just villagers and farmers, so it’s training and more training.” +About Paul has worked at BRAC for two years. He’s very focused on his career and has lots of IT projects running. He is responsible for training the branches on how to use the mobile phones. Right now Paul is looking for the right quality of phones. He also wants to understand how the database will ensure reliable syncing.App Buildershttps://docs.communityhealthtoolkit.org/design/personas/app-builder/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/app-builder/About App builders and technical organizations have in-house or contracted software developers. They deploy health technology solutions for implementing partners such an iNGOs, Governments, UN agencies etc in the community health space. They have limited familiarity with end-users such as CHWs and HCD. +Values Aligned with CHT principles and willing to contribute back to the CHT in the future Believe in building open-source technologies Integrity Likeable personality Open-mindedness Strong work ethic Responsibilities Build and deliver user experiences centered on the CHT Steward the CHT app developer’s experience CHT Capacity Builders Technical support Speak the language of the business Needs Capacity and expertise with the CHT Flexible technology (and tooling) to help build holistic digital health interventions Motivations Build awesome and bug-free user experiences using the CHT Adoption of the CHT as the technology of choice for building community health apps Delivering effective interventions Novel projects or interventions Developing skills Strengths and Assets Diversity in skills sets: HCD, software development, database management, dashboard development Resourcefulness: bend the CHT&rsquo;s capabilities to meet a given need Passion for problem solving An inquisitive mind \ No newline at end of file diff --git a/design/personas/nurse-mary/index.html b/design/personas/nurse-mary/index.html index 50ca3ce7d8..95fc60dff0 100644 --- a/design/personas/nurse-mary/index.html +++ b/design/personas/nurse-mary/index.html @@ -1,9 +1,9 @@ -Nurse, Mary | Community Health Toolkit +Nurse, Mary | Community Health Toolkit

    Nurse, Mary

    Nurses are stationed at the health facility and spend their days seeing patients. They are typically very busy and may see 50 or more patients a day. At the clinic, they sometimes deal with staff shortages, stock-outs, and poor internet connectivity. They help train and manage CHWs, particularly during monthly meetings at the facility. They are interested in seeing improvements in health metrics for the areas their facility serves.

    Nurse


    About

    Mary has formal clinical training but a limited ability to conduct complex surgical procedures. She sees +/- 50 patients a day, sometimes in their homes. She is literate in English and understands local dialects. She is sometimes stationed far from her rural town/family in spurts and makes regular trips to districts to hand-deliver reports.

    Mary lives on $5/day and depends mostly on solar power at the clinic as she may not have electricity at home. She is enthusiastic about technology but has had little exposure. She has an email address that she does not check regularly and has a basic understanding of Windows OS. She uses Facebook and M-PESA on a low-end phone.

    Values

    • Good health. Anyone is only able to work and therefore take care of ones and other people’s needs if and when in really good health
    • Family is the essence of one’s existence and that each child is entitled to love that can be best provided and experienced in the family setting
    • Education is the ticket for better, healthier and happier life for everyone

    Responsibilities

    • Assessing patients and providing primary care
    • Reporting service delivery statistics to health system officials
    • Coordinating care for high-priority patients through CHWs and supervisors
    • Initiating events to promote healthy practices in the community

    Needs

    • Nurse Mary wants nil (zero) maternal and child mortalities in the community served by the health facility
    • Nurse Mary needs all the community to be aware of the health care services that they can access at the health facility
    • Nurse Mary needs everyone in the community to have access and adhere to continuous quality care provided at the health facility
    • Nurse Mary needs all defaulters to health care available at the health facility that are in the community to be traced and re-introduced to care
    • Nurse Mary needs to keep and get accurate data of the target population that she provides health care for
    • Nurse Mary needs to keep abreast with new editions of nursing procedures through regular trainings/seminars

    Motivations

    • Community enjoying services of skilled health professionals for deliveries which translates to no or very few maternal and child mortalities. More money for her to run the facility as finances for performance is a policy that is in use now in Kenya.
    • Healthier children, with fewer neonatal, infant or child mortalities, and 100% full immunization of all under-fives.
    • Progress within the nursing profession with continues and recognized certifications.
    • Model health facility where other health facilities are encouraged to emulate and adopt those standards.
    • Prompt report submissions to supervisors and the recognition by the supervisors for that.

    Strengths and Assets

    • Literate
    • Strong clinical knowledge
    • Organizational skills
    • Constant source of income-employment by the government
    • Mobile phones: smart phones, facebook, Whatsapp and access to email
    • Computer literacy: access email, basic understanding of the windows OS
    • Access to either solar or electricity power at the health center

    A Day in the Life

    Her day at work starts early as she must be on duty at 7 am, to relieve the nurse who has been on cover of the health facility at night if she is in a health facility that as at least 2 of them. In the health facility, where she is the only nurse she comes to pick up from where she left last evening, if she was “lucky” not to be woken up to attend to a patient at night.

    She gets a report from the “night nurse” of all the in-patients-mainly women in labor, newborns born at night, children less than 5 years brought in at night for care and injuries resulting from accidents.

    8am going to 9am, the outpatient is swollen with patients: mothers with under five children bringing them to the child welfare clinic, pregnant women some with under five children coming also for antenatal care, and elderly women and men, all weak, sick looking and would pass for nutritional support-if only there were supplements for the elderly. The ones in store are for the under 5 malnourished and pregnant women.

    Anyway, she notes the need to do a health education session on how to eat a well-nourished meal using the locally available food stuffs, but now, she must do one on how to prevent contracting cholera as cholera as for the last 2 months being reported in many parts of the country. It is a regular practice to carry out health education sessions to the out patients. She will also talk to the CHEW today for a health education session with the CHWs during their monthly meeting on cholera prevention that the CHWs must disseminate to the community. She and the field CHEW must liaise consistently as they work together with the CHWs to promote the health of the community. Her role is to oversee the field CHEW and to help her meet the knowledge gap of the CHWs by consistently teaching CHWs on common local diseases and how to prevent them, identifying them, giving first aid and referring patients for specialized health care.

    The CHWs are critical to her as they are the link between the community and the health facility that is very important. The CHWs help her trace the children under five lost to follow up, malnourished children in the community who need follow up, chronically ill patients in the community that need home based care and refer and support orphans and vulnerable children in the community. The field CHEW co-ordinates this activity, after giving her the list of the patients to be followed.

    Her next in line activity is to see the ANC mothers, she co-ordinates their care, sends them for laboratory work, prescribe treatments and supplements based to the general health assessments of the woman and the lab results. She records care given in the maternal child booklet, and advises her on when to come for her next follow up. She briefly counsels on how to take care of herself during pregnancy, feed well and the exercises to take, maintain good hygiene and to plan to deliver her baby in the health facility: the individual birth plan, must be reinforced by the CHWs who can afford some more time for a one to one talk with the woman. She keeps a record of all the pregnant women she sees, both the new and those that come for subsequent visit. She will write a report based on this at the end of the month showing the new pregnancies, those that have it 4+ANC visits, new PMTC cases among other details.

    She will also attend to the under-fives, both those that have come for the routine immunization and growth monitoring and the sick ones who were brought for treatment. She will record all the care given in the mother child booklet and give brief advice on nutrition of the child while charting the weight on the growth monitoring chart. She will attend to the other sickly patients as well, give family planning counseling and services, and monitor the mothers in labor in the ward. Among other duties that she must attend to are emergency cases referred from the community, accompanying complicated cases to the higher level hospital for more specialized care, and assisting the doctors and clinicians in handling special cases when in facility like a hospital where these services are offered.

    The day has not started or ended if she has not read the temperature of the thermometers of the vaccine boxes to ensure that it is in the required temperature ranges and therefore the boxes are in good order.

    Her monthly reports range from daily vaccine monitoring chart, to the ANC, immunization related records to the vaccine usage rates for the month. She places monthly based order for vaccine supplies based on the month’s usage, and must collect the vaccines from the sub-county headquarters before the end of the first week of the month. She attends a monthly visit organized by her supervisors at the sub-county district, at least monthly and frequently meets her colleagues during the irregular continuous education seminars organized by their supervisors.

    Nurse Mary faces a lot of challenges, but the most pressing is the workload coupled with the intensity of the nature of the work that she faces day in, day out. She is entitled to work a maximum of 45 hours a week, but is that really what happens?

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/design/personas/partners/implementers/index.html b/design/personas/partners/implementers/index.html index d208f15524..da759a4d36 100644 --- a/design/personas/partners/implementers/index.html +++ b/design/personas/partners/implementers/index.html @@ -1,9 +1,9 @@ -Implementing Partners | Community Health Toolkit +Implementing Partners | Community Health Toolkit

    Implementing Partners

    Implementing partners aim to build a new model of care with demonstrated evidence for impact and scalability. They are interested in pioneering data-driven approaches to achieve the desired community health outcomes.

    Characteristics and Strengths

    • May have influence and reach with the government to adopt and scale-up the new model of care
    • Have a good understanding of the health needs of communities
    • Have an appreciation for the role of digital technology in healthcare
    • May have deployed, or are planning to deploy, digital health tools in the community health space
    • Support government’s CHW networks, and may have fielded their own cadre of CHWs
    • Are leveraging data and data science to innovate on new approaches to achieving desired health outcomes
    • Have access to health systems data from the deployment of a new model of care
    • May possess existing capacity, or are looking to build, the in-house capacity to deploy and implement digital health tools
    • May have already invested significantly in proprietary locked-in software
    • Keen to attract funding from large funders
    • May have a presence in multiple countries

    Values

    • Equitable access to quality health care
    • Developing national and local capacities
    • Cost-effectiveness
    • Government and donor relations
    • Harmonization and alignment to the national health strategies
    • Integration with national digital health platforms
    • Government adoption and ownership of their model of care

    Needs

    • Government support and buy-in
    • Funder backing and resources to design and deploy digital health programs
    • HCD and data science capabilities
    • May need further understanding and expertise in working with CHT and opensource tools
    -

    Last modified 01.06.2020: update partner personas (86e88ac5)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/design/personas/partners/index.html b/design/personas/partners/index.html index 94720f7b56..1a040c1af2 100644 --- a/design/personas/partners/index.html +++ b/design/personas/partners/index.html @@ -1,9 +1,9 @@ -Partner Personas | Community Health Toolkit +Partner Personas | Community Health Toolkit

    Partner Personas

    An overview of CHT Partner Personas

    Implementing Partners

    Implementing partners aim to build a new model of care with demonstrated evidence for impact and scalability. They are interested in pioneering data-driven approaches to achieve the desired community health outcomes.

    Local and Sub-National Governments

    Local government partners aim to increase access to quality, affordable care and maximize impact from resource investments in health.

    Ministry of Health and National Governments

    MoH and national government partners set forward-looking national health policies, strengthen health systems, roll-out national public health programs, and deliver on international health commitments. They aim to increase access to quality, affordable care and achieve Universal Health Coverage

    -

    Last modified 01.06.2020: description changes (768c27c7)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/design/personas/partners/index.xml b/design/personas/partners/index.xml index eb787a949b..1997be43ce 100644 --- a/design/personas/partners/index.xml +++ b/design/personas/partners/index.xml @@ -1,104 +1 @@ -Community Health Toolkit – Partner Personashttps://docs.communityhealthtoolkit.org/design/personas/partners/Recent content in Partner Personas on Community Health ToolkitHugo -- gohugo.ioenDesign: Implementing Partnershttps://docs.communityhealthtoolkit.org/design/personas/partners/implementers/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/partners/implementers/ -<h2 id="characteristics-and-strengths">Characteristics and Strengths</h2> -<ul> -<li>May have influence and reach with the government to adopt and scale-up the new model of care</li> -<li>Have a good understanding of the health needs of communities</li> -<li>Have an appreciation for the role of digital technology in healthcare</li> -<li>May have deployed, or are planning to deploy, digital health tools in the community health space</li> -<li>Support government’s CHW networks, and may have fielded their own cadre of CHWs</li> -<li>Are leveraging data and data science to innovate on new approaches to achieving desired health outcomes</li> -<li>Have access to health systems data from the deployment of a new model of care</li> -<li>May possess existing capacity, or are looking to build, the in-house capacity to deploy and implement digital health tools</li> -<li>May have already invested significantly in proprietary locked-in software</li> -<li>Keen to attract funding from large funders</li> -<li>May have a presence in multiple countries</li> -</ul> -<h2 id="values">Values</h2> -<ul> -<li>Equitable access to quality health care</li> -<li>Developing national and local capacities</li> -<li>Cost-effectiveness</li> -<li>Government and donor relations</li> -<li>Harmonization and alignment to the national health strategies</li> -<li>Integration with national digital health platforms</li> -<li>Government adoption and ownership of their model of care</li> -</ul> -<h2 id="needs">Needs</h2> -<ul> -<li>Government support and buy-in</li> -<li>Funder backing and resources to design and deploy digital health programs</li> -<li>HCD and data science capabilities</li> -<li>May need further understanding and expertise in working with CHT and opensource tools</li> -</ul>Design: Local and Sub-National Governmentshttps://docs.communityhealthtoolkit.org/design/personas/partners/local-governments/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/partners/local-governments/ -<h2 id="characteristics-and-strengths">Characteristics and Strengths</h2> -<ul> -<li>They are often led by locally elected political leaders and are motivated to demonstrate health impacts for continued political support</li> -<li>Tend to be more accountable to local communities</li> -<li>Have a thorough understanding of the local context and health needs of local communities</li> -<li>They often have the legal and administrative mandate to set local health priorities, plan, allocate and mobilize resources, and deliver primary health care to their respective communities</li> -<li>They are guided by national health strategy and community health strategy</li> -<li>They are responsible for managing CHWs</li> -<li>They are often motivated to adopt digital health technologies</li> -<li>May have deployed, or are planning to deploy, digital health tools in the community health space</li> -<li>Comparatively, they have less cumbersome bureaucratic processes and red-tape in building partnerships</li> -</ul> -<h2 id="values">Values</h2> -<ul> -<li>Health and well-being of their citizens</li> -<li>Equitable access to quality health care services</li> -<li>Social health protection of their constituencies</li> -<li>Local leadership and decentralization in health</li> -<li>Innovation and digital technologies in community health space</li> -<li>High impact door-step health care</li> -<li>Community participation</li> -<li>Partnerships with non-state actors</li> -<li>Cost-effectiveness</li> -<li>Sustainability</li> -</ul> -<h2 id="needs">Needs</h2> -<ul> -<li>Health systems strengthening support to keep up with growing population needs and tackle emerging public health challenges, e.g. NCDs</li> -<li>Better institutional capacities to manage community health programs</li> -<li>Regular guidance, coordination and support from the state/provincial and national governments</li> -<li>Better data systems for evidence-based planning and monitoring</li> -<li>Technical partnership and support to manage digital health technologies</li> -</ul>Design: Ministry of Health and National Governmentshttps://docs.communityhealthtoolkit.org/design/personas/partners/national-governments/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/partners/national-governments/ -<h2 id="characteristics-and-strengths">Characteristics and Strengths</h2> -<ul> -<li>Set national health priorities and design large-scale national public health programs</li> -<li>Lead health sector reform agenda</li> -<li>Guide the production, recruitment, and deployment of human resource for health, including community health workforce</li> -<li>Regulate both public and private health service providers</li> -<li>Set development cooperation policies and priorities in the health sector</li> -<li>Provide financial resources to sub-national governments</li> -<li>Work closely with other ministries and sub-national governments to execute national health policies</li> -<li>Compete with other ministries and government line agencies for resources</li> -<li>Have access to large-scale funding from multilateral and bilateral international agencies</li> -<li>Have an appreciation for the role of digital technology in healthcare</li> -<li>May have developed and rolled-out national digital health strategy</li> -<li>Responsible for setting digital technology and data standards</li> -<li>Often have an institutional home to anchor national digital health programs</li> -<li>May have some in-house capacity to deploy and implement digital health tools</li> -<li>Are often looking to establish partnerships for leveraging digital health technologies</li> -<li>Are leveraging data and data science to innovate on new approaches to achieving desired health outcomes</li> -<li>Have access to health systems data from the deployment of a new model of care</li> -<li>May have already invested significantly in proprietary locked-in software</li> -</ul> -<h2 id="values">Values</h2> -<ul> -<li>Safeguarding the health rights of the citizens</li> -<li>International commitments in health, e.g. UHC, SDG, etc.</li> -<li>National ownership of public health programs</li> -<li>National capacities to govern and manage community health programs</li> -<li>Cost-effectiveness</li> -<li>Government adoption and sustainability</li> -</ul> -<h2 id="needs">Needs</h2> -<ul> -<li>Health systems strengthening support to keep up with growing population needs and tackle emerging public health challenges, e.g. NCDs</li> -<li>Regular political commitments for increased spending in public health</li> -<li>Integration and interoperability of digital health technologies</li> -<li>Better data systems for evidence-based planning and monitoring</li> -<li>Capacity and resources to govern and manage digital health programs</li> -<li>HCD and data science capabilities</li> -</ul> \ No newline at end of file +Partner Personas on Community Health Toolkithttps://docs.communityhealthtoolkit.org/design/personas/partners/Recent content in Partner Personas on Community Health ToolkitHugo -- gohugo.ioenImplementing Partnershttps://docs.communityhealthtoolkit.org/design/personas/partners/implementers/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/partners/implementers/Characteristics and Strengths May have influence and reach with the government to adopt and scale-up the new model of care Have a good understanding of the health needs of communities Have an appreciation for the role of digital technology in healthcare May have deployed, or are planning to deploy, digital health tools in the community health space Support government’s CHW networks, and may have fielded their own cadre of CHWs Are leveraging data and data science to innovate on new approaches to achieving desired health outcomes Have access to health systems data from the deployment of a new model of care May possess existing capacity, or are looking to build, the in-house capacity to deploy and implement digital health tools May have already invested significantly in proprietary locked-in software Keen to attract funding from large funders May have a presence in multiple countries Values Equitable access to quality health care Developing national and local capacities Cost-effectiveness Government and donor relations Harmonization and alignment to the national health strategies Integration with national digital health platforms Government adoption and ownership of their model of care Needs Government support and buy-in Funder backing and resources to design and deploy digital health programs HCD and data science capabilities May need further understanding and expertise in working with CHT and opensource toolsLocal and Sub-National Governmentshttps://docs.communityhealthtoolkit.org/design/personas/partners/local-governments/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/partners/local-governments/Characteristics and Strengths They are often led by locally elected political leaders and are motivated to demonstrate health impacts for continued political support Tend to be more accountable to local communities Have a thorough understanding of the local context and health needs of local communities They often have the legal and administrative mandate to set local health priorities, plan, allocate and mobilize resources, and deliver primary health care to their respective communities They are guided by national health strategy and community health strategy They are responsible for managing CHWs They are often motivated to adopt digital health technologies May have deployed, or are planning to deploy, digital health tools in the community health space Comparatively, they have less cumbersome bureaucratic processes and red-tape in building partnerships Values Health and well-being of their citizens Equitable access to quality health care services Social health protection of their constituencies Local leadership and decentralization in health Innovation and digital technologies in community health space High impact door-step health care Community participation Partnerships with non-state actors Cost-effectiveness Sustainability Needs Health systems strengthening support to keep up with growing population needs and tackle emerging public health challenges, e.Ministry of Health and National Governmentshttps://docs.communityhealthtoolkit.org/design/personas/partners/national-governments/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/partners/national-governments/Characteristics and Strengths Set national health priorities and design large-scale national public health programs Lead health sector reform agenda Guide the production, recruitment, and deployment of human resource for health, including community health workforce Regulate both public and private health service providers Set development cooperation policies and priorities in the health sector Provide financial resources to sub-national governments Work closely with other ministries and sub-national governments to execute national health policies Compete with other ministries and government line agencies for resources Have access to large-scale funding from multilateral and bilateral international agencies Have an appreciation for the role of digital technology in healthcare May have developed and rolled-out national digital health strategy Responsible for setting digital technology and data standards Often have an institutional home to anchor national digital health programs May have some in-house capacity to deploy and implement digital health tools Are often looking to establish partnerships for leveraging digital health technologies Are leveraging data and data science to innovate on new approaches to achieving desired health outcomes Have access to health systems data from the deployment of a new model of care May have already invested significantly in proprietary locked-in software Values Safeguarding the health rights of the citizens International commitments in health, e. \ No newline at end of file diff --git a/design/personas/partners/local-governments/index.html b/design/personas/partners/local-governments/index.html index c98dc4b971..4fac7ff7b9 100644 --- a/design/personas/partners/local-governments/index.html +++ b/design/personas/partners/local-governments/index.html @@ -1,9 +1,9 @@ -Local and Sub-National Governments | Community Health Toolkit +Local and Sub-National Governments | Community Health Toolkit

    Local and Sub-National Governments

    Local government partners aim to increase access to quality, affordable care and maximize impact from resource investments in health.

    Characteristics and Strengths

    • They are often led by locally elected political leaders and are motivated to demonstrate health impacts for continued political support
    • Tend to be more accountable to local communities
    • Have a thorough understanding of the local context and health needs of local communities
    • They often have the legal and administrative mandate to set local health priorities, plan, allocate and mobilize resources, and deliver primary health care to their respective communities
    • They are guided by national health strategy and community health strategy
    • They are responsible for managing CHWs
    • They are often motivated to adopt digital health technologies
    • May have deployed, or are planning to deploy, digital health tools in the community health space
    • Comparatively, they have less cumbersome bureaucratic processes and red-tape in building partnerships

    Values

    • Health and well-being of their citizens
    • Equitable access to quality health care services
    • Social health protection of their constituencies
    • Local leadership and decentralization in health
    • Innovation and digital technologies in community health space
    • High impact door-step health care
    • Community participation
    • Partnerships with non-state actors
    • Cost-effectiveness
    • Sustainability

    Needs

    • Health systems strengthening support to keep up with growing population needs and tackle emerging public health challenges, e.g. NCDs
    • Better institutional capacities to manage community health programs
    • Regular guidance, coordination and support from the state/provincial and national governments
    • Better data systems for evidence-based planning and monitoring
    • Technical partnership and support to manage digital health technologies
    -

    Last modified 01.06.2020: update partner personas (86e88ac5)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/design/personas/partners/national-governments/index.html b/design/personas/partners/national-governments/index.html index 8a8300e69b..9cef6063f8 100644 --- a/design/personas/partners/national-governments/index.html +++ b/design/personas/partners/national-governments/index.html @@ -1,9 +1,9 @@ -Ministry of Health and National Governments | Community Health Toolkit +Ministry of Health and National Governments | Community Health Toolkit

    Ministry of Health and National Governments

    MoH and national government partners set forward-looking national health policies, strengthen health systems, roll-out national public health programs, and deliver on international health commitments. They aim to increase access to quality, affordable care and achieve Universal Health Coverage

    Characteristics and Strengths

    • Set national health priorities and design large-scale national public health programs
    • Lead health sector reform agenda
    • Guide the production, recruitment, and deployment of human resource for health, including community health workforce
    • Regulate both public and private health service providers
    • Set development cooperation policies and priorities in the health sector
    • Provide financial resources to sub-national governments
    • Work closely with other ministries and sub-national governments to execute national health policies
    • Compete with other ministries and government line agencies for resources
    • Have access to large-scale funding from multilateral and bilateral international agencies
    • Have an appreciation for the role of digital technology in healthcare
    • May have developed and rolled-out national digital health strategy
    • Responsible for setting digital technology and data standards
    • Often have an institutional home to anchor national digital health programs
    • May have some in-house capacity to deploy and implement digital health tools
    • Are often looking to establish partnerships for leveraging digital health technologies
    • Are leveraging data and data science to innovate on new approaches to achieving desired health outcomes
    • Have access to health systems data from the deployment of a new model of care
    • May have already invested significantly in proprietary locked-in software

    Values

    • Safeguarding the health rights of the citizens
    • International commitments in health, e.g. UHC, SDG, etc.
    • National ownership of public health programs
    • National capacities to govern and manage community health programs
    • Cost-effectiveness
    • Government adoption and sustainability

    Needs

    • Health systems strengthening support to keep up with growing population needs and tackle emerging public health challenges, e.g. NCDs
    • Regular political commitments for increased spending in public health
    • Integration and interoperability of digital health technologies
    • Better data systems for evidence-based planning and monitoring
    • Capacity and resources to govern and manage digital health programs
    • HCD and data science capabilities
    -

    Last modified 01.06.2020: update partner personas (86e88ac5)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/design/personas/regional-manager-christina/index.html b/design/personas/regional-manager-christina/index.html index cff0189c9b..7252b274c6 100644 --- a/design/personas/regional-manager-christina/index.html +++ b/design/personas/regional-manager-christina/index.html @@ -1,9 +1,9 @@ -Regional Manager, Christina | Community Health Toolkit +Regional Manager, Christina | Community Health Toolkit

    Regional Manager, Christina

    Regional managers provide overall oversight of activities in the region they are assigned. They are employed by technical and implementing partners to oversee and provide general guidance for two or more branches in a region. They troubleshoot and provide support to branch managers to optimise operations. They have limited interactions with end users but provide the link between field operations and the head office.

    Regional Manager


    “I heard that some branches have already had challenges with mobile phones.”

    About

    Christina been working at BRAC for six years. She manages four branches and works closely with the CHW Managers there. She makes sure CHW Managers are monitoring CHWs. She occasionally does random checks, driving out to villages and observing Managers with CHWs.

    Values

    • Visionary
    • Integrity
    • Good communication
    • Respect
    • Responsibility
    • Wisdom
    • Empathy

    Responsibilities

    • Overall responsibility of the Region’s activities
    • Oversees implementation of organisational goals
    • Communicates organisational goals and strategies to CHW managers
    • Liaise and ensure compliance with Ministries, NGO boards and regulatory bodies
    • Promote advocacy efforts of the program
    • Supervises and supports staff to attain their career goals
    • Approval of quarterly logistic plans for CHW managers
    • Reviews and approves CHW’s continuous education programs
    • Evaluates programs impact and advises on key priority areas of focus
    • Realignment of programs to changing ecosystem
    • Budgetary planning for region’s activities
    • Tracking branches’ and CHW manager’s performance
    • Monthly indicators tracking to ensure realization of organisational goals

    Needs

    • Real time access of data on supervision activities ongoing in their region
    • Access to summary statistics on project indicators
    • Timely submission of monthly reports on retention and turnover rates for CHWs
    • Escalation of CHW retraining needs, challenges and proposed solutions
    • Opportunities to conduct random checks to assess the CHWs perceptions of their managers support

    Motivations

    • Desire to improve lives of communities
    • Desire to practice community health strategies learnt at school
    • Organisational goals align well with personal goals

    Strengths and Assets

    • Provided with a company car and computer
    • Has a welcoming heart and accessible to employees
    • Team player
    -

    Last modified 19.02.2023: fix: small grammatical error (768e3128)
    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/glossary/index.html b/glossary/index.html index 4420148fb3..af73454457 100644 --- a/glossary/index.html +++ b/glossary/index.html @@ -1,9 +1,9 @@ -Glossary and Definitions | Community Health Toolkit +Glossary and Definitions | Community Health Toolkit
    \ No newline at end of file diff --git a/hosting/3.x/app-developer/index.html b/hosting/3.x/app-developer/index.html index ab18c81c4a..f3b8925e4f 100644 --- a/hosting/3.x/app-developer/index.html +++ b/hosting/3.x/app-developer/index.html @@ -1,9 +1,9 @@ -App Developer Hosting in CHT 3.x | Community Health Toolkit +App Developer Hosting in CHT 3.x | Community Health Toolkit

    App Developer Hosting in CHT 3.x

    Hosting the CHT when developing apps

    This guide assumes you are a CHT app developer wanting to either run concurrent instances of the CHT, or easily be able to switch between different instances without loosing any data while doing so. To do development on the CHT core itself, see the development guide.

    To deploy the CHT in production, see either AWS hosting or Self hosting

    Getting started

    Be sure to meet the CHT hosting requirements first. As well, if any other medic-os instances using the main docker-compose-developer-3.x-only.yml file are running locally, stop them otherwise port, storage volume and container name conflicts may occur.

    After meeting these requirements, download the developer YAML file in the directory you want to store them:

    curl -o docker-compose-developer-3.x-only.yml https://raw.githubusercontent.com/medic/cht-core/master/scripts/docker-helper/docker-compose-developer-3.x-only.yml
    + Create project issue

    App Developer Hosting in CHT 3.x

    Hosting the CHT when developing apps

    This guide assumes you are a CHT app developer wanting to either run concurrent instances of the CHT, or easily be able to switch between different instances without loosing any data while doing so. To do development on the CHT core itself, see the development guide.

    To deploy the CHT in production, see either AWS hosting or Self hosting

    Getting started

    Be sure to meet the CHT hosting requirements first. As well, if any other medic-os instances using the main docker-compose-developer-3.x-only.yml file are running locally, stop them otherwise port, storage volume and container name conflicts may occur.

    After meeting these requirements, download the developer YAML file in the directory you want to store them:

    curl -o docker-compose-developer-3.x-only.yml https://raw.githubusercontent.com/medic/cht-core/master/scripts/docker-helper/docker-compose-developer-3.x-only.yml
     

    To start the first developer CHT instance, run docker-compose and specify the file that was just download:

    docker-compose -f docker-compose-developer-3.x-only.yml up
     

    This may take some minutes to fully start depending on the speed of the Internet connection and speed of the bare-metal host. This is because docker needs to download all the storage layers for all the containers and the CHT needs to run the first run set up. After downloads and setup has completed, the CHT should be accessible on https://localhost.

    When connecting to a new dev CHT instance for the first time, an error will be shown, “Your connection is not private” (see screenshot). To get past this, click “Advanced” and then click “Proceed to localhost”.

    Running the Nth CHT instance

    After running the first instance of the CHT, it’s easy to run as many more as are needed. This is achieved by specifying different:

    • port for HTTP redirects (CHT_HTTP)
    • port for HTTPS traffic (CHT_HTTPS)
    • project to for the docker compose call (-p PROJECT)

    Assuming you want to start a new project called the_second and start the instance on HTTP port 8081 and HTTPS port 8443, this would be the command:

    CHT_HTTP=8081 CHT_HTTPS=8443 docker-compose -p the_second -f docker-compose-developer-3.x-only.yml up
     

    The second instance is now accessible at https://localhost:8443.

    The .env file

    Often times it’s convenient to use revision control, like GitHub, to store and publish changes in a CHT app. A nice compliment to this is to store the specifics on how to run the docker-compose command for each app. By using a shared docker-compose configuration for all developers on the same app, it avoids any port collisions and enables all developers to have a unified configuration.

    Using the above the_second sample project, we can create another directory to host this project’s configuration:

    mkdir ../the_second
    @@ -326,7 +326,8 @@
     Fri 15 Oct 2021 02:30:28 PM PDT pid="410066" count="2" item="docker_logs" container="helper_test_haproxy_1" processes="4" last_log="Oct 15 21:30:27 576ca039cd88 haproxy[25]: 172.20.0.3,200,GET,/medic/_design/medic,-,medic,'-',21703,5,21402,'curl/7.68.0'"
     Fri 15 Oct 2021 02:30:28 PM PDT pid="410066" count="2" item="status" CHT_count="2" port_stat="open" http_code="200" ssl_verify="yes" reboot_count="0" docker_call="up" last_msg=" :) " load_now="2.69" helper_test_haproxy_1="true" helper_test_medic-os_1="true"
     

    The CHT stores its cookies based on the domain. This means if you’re running two concurrent instances on https://192-168-68-40.local-ip.medicmobile.org:8443 and https://192-168-68-40.local-ip.medicmobile.org:8440 (note different ports), the CHT would write the cookie under the same 192-168-68-40.local-ip.medicmobile.org domain. When logging out of one instance, you would get logged out of both and other consistencies.

    To avoid this collision of cookies, you can use different IP addresses to access the instances. This works because of two reasons:

    1. the TLS certificate being used is valid for any subdomain of *.local-ip.medicmobile.org. Further, the URL always resolves to the IP passed in the * section, so you can use any IP
    2. the IPs that are available to reference your localhost are actually a /8 netmask, meaning there are 16 million addresses to choose from!

    Using the above two reasons, these URLs could work to avoid the cookie collision:

    • https://127-0-0-1.local-ip.medicmobile.org:8443
    • https://127-0-0-2.local-ip.medicmobile.org:8440

    This would result in the domains being 127.0.0.1 and 127.0.0.2 from the CHT’s perspective. When using a mobile device for testing, you’re limited to use the LAN ip output in the helper and can not use the 127.x.x.x IPs.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/hosting/3.x/ec2-setup-guide/index.html b/hosting/3.x/ec2-setup-guide/index.html index 4f2f97a168..c2011c60c0 100644 --- a/hosting/3.x/ec2-setup-guide/index.html +++ b/hosting/3.x/ec2-setup-guide/index.html @@ -1,9 +1,9 @@ -AWS Hosting in CHT 3.x | Community Health Toolkit +AWS Hosting in CHT 3.x | Community Health Toolkit

    AWS Hosting in CHT 3.x

    Hosting the CHT on Amazon EC2

    Most production CHT instances are deployed on AWS EC2. Leveraging Elastic Compute Cloud (EC2) and Elastic Block Store (EBS), CHT instances can easily be scaled up with larger EC2 instances and have easy increased disk space, backup and restores with EBS.

    This guide will walk you through the process of creating an EC2 instance, mounting an EBS volume and provisioning Docker containers.

    Create and Configure EC2 Instance

    1. Create EC2 (use security best practices)

      Review the CHT hardware requirements and start with an appropriately sized instance. After creating the instance and downloading the .pem file, change permissions to 0600 for it:

      sudo chmod 0600 ~/Downloads/name_of_file.pem
      + Create project issue

    AWS Hosting in CHT 3.x

    Hosting the CHT on Amazon EC2

    Most production CHT instances are deployed on AWS EC2. Leveraging Elastic Compute Cloud (EC2) and Elastic Block Store (EBS), CHT instances can easily be scaled up with larger EC2 instances and have easy increased disk space, backup and restores with EBS.

    This guide will walk you through the process of creating an EC2 instance, mounting an EBS volume and provisioning Docker containers.

    Create and Configure EC2 Instance

    1. Create EC2 (use security best practices)

      Review the CHT hardware requirements and start with an appropriately sized instance. After creating the instance and downloading the .pem file, change permissions to 0600 for it:

      sudo chmod 0600 ~/Downloads/name_of_file.pem
       

      Create an Elastic IP (EIP) and associate the EIP to your EC2 instance.

      You should now be able to SSH into the EC2 instance using the EIP and the .pem file.

      Goal: SSH into instance

    2. Create or Restore EBS Volume

      • Create or Restore your EBS Volume, tagging appropriately, so it can be found later.
      • Attach volume to EC2 instance
      • Increase disk size (Optional)
      • If you are using a newly created EBS Volume, you will have to format the disk appropriately:
        1. SSH into instance
        2. Follow the instructions here: Using EBS Volumes
        3. Use sudo mkfs -t ext4 <location> in step 4
        4. Mount disk to /srv

      Goal: Mount EBS volume to /srv

    3. Provision Docker server

      Follow README & Run scripts in cht-infrastructure repository.

      Goal: CHT Application bootstraps and comes online

    4. DNS configuration

      • Point DNS A record to EIP given to Docker server in the prior step.
    5. Review SSL certificates

      • Location of certs is /srv/settings/medic-core/nginx/private/
      • Name the key file is default.key and the certificate file is default.crt
      • See SSL Certificates to install new certificates
    6. Configure couch2pg See the couch2pg basic configuration in the cht-couch2pg repository.

    7. Setup postgres to work with couch2pg

      • Creating the database, setting up permissions, exploring the tables and what they store
    8. Debugging couch2pg/postgres

      • Understanding the log and what the entries mean

    Troubleshooting

    1. Restarting processes

      /boot/svc-<start/stop/restart> <service-name/medic-api/medic-sentinel/medic-core couchdb/medic-core nginx>
       
    2. Investigating logs inside Medic OS

      • To view logs, first run this to access a shell in the medic-os container: docker exec -it medic-os /bin/bash
      • View CouchDB logs: less /srv/storage/medic-core/couchdb/logs/startup.log
      • View medic-api logs: less /srv/storage/medic-api/logs/medic-api.log
      • View medic-sentinel logs: less /srv/storage/medic-sentinel/logs/medic-sentinel.log
    3. Investigating docker stderr/stdout logs

       sudo docker logs medic-os
      @@ -314,7 +314,8 @@
       Quick Guides >
       Database >
       Couch2pg Memory Errors

      Dealing with out-of-memory errors in couch2pg

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/hosting/3.x/index.html b/hosting/3.x/index.html index 4f74086553..e75953bfac 100644 --- a/hosting/3.x/index.html +++ b/hosting/3.x/index.html @@ -1,9 +1,9 @@ -3.x | Community Health Toolkit +3.x | Community Health Toolkit

    3.x

    Guides for hosting CHT 3.x applications

    To get an overview on how these hosting solutions use docker and docker-compose, as well as other key CHT concepts, be sure to read the guide on a Local Setup. Note that while this is for CHT 4.x, the concepts apply to 3.x.

    Before beginning any of these guides, be sure to meet all of the CHT hosting requirements first.

    To host a production instance of CHT, use either the AWS or Self hosting guides. To do app development, see our App Developer hosting guide.

    To view 4.x hosting options, see the 4.x hosting section


    AWS Hosting in CHT 3.x

    Hosting the CHT on Amazon EC2

    Self Hosting in CHT 3.x

    Hosting the CHT on self run infrastructure

    App Developer Hosting in CHT 3.x

    Hosting the CHT when developing apps

    SSL Cert Install in CHT 3.x

    SSL Cert Installation for Self-Hosting Setups using Medic OS/3.x

    Offline Hosting of CHT 3.x Server

    Deploying and hosting CHT Core server instances without Internet connectivity

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/hosting/3.x/index.xml b/hosting/3.x/index.xml index 8575a232f8..7fc39145d8 100644 --- a/hosting/3.x/index.xml +++ b/hosting/3.x/index.xml @@ -1,690 +1,9 @@ -Community Health Toolkit – 3.xhttps://docs.communityhealthtoolkit.org/hosting/3.x/Recent content in 3.x on Community Health ToolkitHugo -- gohugo.ioenHosting: AWS Hosting in CHT 3.xhttps://docs.communityhealthtoolkit.org/hosting/3.x/ec2-setup-guide/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/3.x/ec2-setup-guide/ -<p>Most production CHT instances are deployed on AWS EC2. Leveraging Elastic Compute Cloud (EC2) and Elastic Block Store (EBS), CHT instances can easily be scaled up with larger EC2 instances and have easy increased disk space, backup and restores with EBS.</p> -<p>This guide will walk you through the process of creating an EC2 instance, mounting an EBS volume and provisioning Docker containers.</p> -<h2 id="create-and-configure-ec2-instance">Create and Configure EC2 Instance</h2> -<ol> -<li> -<p>Create EC2 (use security best practices)</p> -<p>Review the <a href="https://docs.communityhealthtoolkit.org/hosting/requirements/#hardware-requirements">CHT hardware requirements</a> and start with an appropriately sized instance. After creating the instance and downloading the <code>.pem</code> file, change permissions to <code>0600</code> for it:</p> -<pre tabindex="0"><code>sudo chmod 0600 ~/Downloads/name_of_file.pem -</code></pre><p>Create an <a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html">Elastic IP (EIP) and associate the EIP to your EC2 instance</a>.</p> -<p>You should now be able to SSH into the EC2 instance using the EIP and the <code>.pem</code> file.</p> -<p><code>Goal</code>: SSH into instance</p> -</li> -<li> -<p>Create or Restore EBS Volume</p> -<ul> -<li>Create or <a href="https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/ebs-restoring-volume.html">Restore</a> your EBS Volume, tagging appropriately, so it can be found later.</li> -<li>Attach volume to EC2 instance</li> -<li><a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/recognize-expanded-volume-linux.html">Increase disk size</a> (Optional)</li> -<li>If you are using a newly created EBS Volume, you will have to format the disk appropriately: -<ol> -<li>SSH into instance</li> -<li>Follow the instructions here: <a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-using-volumes.html">Using EBS Volumes</a></li> -<li>Use <code>sudo mkfs -t ext4 &lt;location&gt;</code> in step 4</li> -<li>Mount disk to <code>/srv</code></li> -</ol> -</li> -</ul> -<p><code>Goal</code>: Mount EBS volume to <code>/srv</code></p> -</li> -<li> -<p>Provision Docker server</p> -<p>Follow README &amp; Run scripts in <a href="https://github.com/medic/cht-infrastructure/tree/master/self-hosting/prepare-system">cht-infrastructure repository</a>.</p> -<p><code>Goal</code>: CHT Application bootstraps and comes online</p> -</li> -<li> -<p>DNS configuration</p> -<ul> -<li>Point DNS <code>A</code> record to EIP given to Docker server in the prior step.</li> -</ul> -</li> -<li> -<p>Review SSL certificates</p> -<ul> -<li>Location of certs is <code>/srv/settings/medic-core/nginx/private/</code></li> -<li>Name the key file is <code>default.key</code> and the certificate file is <code>default.crt</code></li> -<li>See <a href="https://docs.communityhealthtoolkit.org/hosting/3.x/ssl-cert-install/">SSL Certificates</a> to install new certificates</li> -</ul> -</li> -<li> -<p>Configure couch2pg -See the <a href="https://github.com/medic/cht-couch2pg/blob/main/README.md">couch2pg basic configuration</a> in the <code>cht-couch2pg</code> repository.</p> -</li> -<li> -<p>Setup postgres to work with couch2pg</p> -<ul> -<li>Creating the database, setting up permissions, exploring the tables and what they store</li> -</ul> -</li> -<li> -<p>Debugging couch2pg/postgres</p> -<ul> -<li>Understanding the log and what the entries mean</li> -</ul> -</li> -</ol> -<h2 id="troubleshooting">Troubleshooting</h2> -<ol> -<li> -<p>Restarting processes</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>/boot/svc-&lt;start/stop/restart&gt; &lt;service-name/medic-api/medic-sentinel/medic-core couchdb/medic-core nginx&gt; -</span></span></code></pre></div><ul> -<li>Also see <a href="https://github.com/medic/medic-os#user-content-service-management-scripts">MedicOS service management scripts</a></li> -</ul> -</li> -<li> -<p>Investigating logs inside Medic OS</p> -<ul> -<li>To view logs, first run this to access a shell in the medic-os container: <code>docker exec -it medic-os /bin/bash</code></li> -<li>View CouchDB logs: <code>less /srv/storage/medic-core/couchdb/logs/startup.log</code></li> -<li>View medic-api logs: <code>less /srv/storage/medic-api/logs/medic-api.log</code></li> -<li>View medic-sentinel logs: <code>less /srv/storage/medic-sentinel/logs/medic-sentinel.log</code></li> -</ul> -</li> -<li> -<p>Investigating docker stderr/stdout logs</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span> sudo docker logs medic-os -</span></span><span style="display:flex;"><span> sudo docker logs haproxy -</span></span></code></pre></div></li> -<li> -<p>Upgrading the container</p> -<ul> -<li> -<p>Backup all data (EBS)</p> -</li> -<li> -<p>Log into container and stop all services</p> -</li> -<li> -<p>To prepare for the upgrade, delete all other files in <code>/srv</code> EXCEPT for <code>/srv/storage/medic-core/</code></p> -<p>The <code>medic-core</code> directory is where the CHT stores user data. Of key importance is <code>./couchdb/local.in</code> and <code>./medic-core/couchdb/local.d/</code> where custom CouchDB configuration is stored.</p> -</li> -<li> -<p>Change the image tag to the final Medic OS image release version (<code>cht-3.9.0-rc.2</code>) in the docker compose file:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">services</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">medic-os</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">image</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">medicmobile/medic-os:cht-3.9.0-rc.2</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span></code></pre></div></li> -<li> -<p>Launch new containers with appropriate <code>COUCHDB_ADMIN_PASSWORD</code> &amp; <code>HA_PASSWORD</code> environment variables</p> -</li> -</ul> -</li> -<li> -<p>Upgrading the webapp</p> -<ul> -<li>Use Admin GUI page</li> -<li><a href="https://docs.communityhealthtoolkit.org/hosting/3.x/self-hosting/#links-to-medic-documentation-for-horticulturalist-for-upgrades">CLI via horticulturalist</a></li> -</ul> -</li> -<li> -<p>RDS help</p> -<ul> -<li><a href="https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Welcome.html">Amazon user guide</a></li> -</ul> -</li> -</ol> -<h2 id="backups">Backups</h2> -<ol> -<li> -<p>Configure backups</p> -<ul> -<li><a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/snapshot-lifecycle.html">EBS Snapshot Lifecycle Manager</a></li> -</ul> -</li> -<li> -<p>Restoring from backup</p> -<ul> -<li>Create volume from snapshot</li> -<li>Tag appropriately for backups</li> -<li>Mount volume to docker server</li> -</ul> -</li> -</ol> -<h2 id="process-supervision">Process supervision</h2> -<ul> -<li><code>supvisorctl</code></li> -<li><code>/boot/supervisor-inspect</code></li> -</ul> -<h2 id="increasing-disk-size">Increasing disk size</h2> -<p>Monitor disk usage so alerts are sent before all disk spaces is used up. If free disk space falls below 40%, increase the disk space as follows:</p> -<ul> -<li>Stop medic: <code>sudo supervisorctl stop medic</code></li> -<li>Go to EBS in AWS and take a snapshot of the volume.</li> -<li>Modify the volume size (Increase it by 2x preferably). Wait until the modification succeeds.</li> -<li><a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/recognize-expanded-volume-linux.html">Make the instance recognize the additional space</a></li> -<li>Turn medic back on: <code>sudo supervisorctl start medic</code></li> -</ul> -<h2 id="monitoring--backup">Monitoring &amp; Backup</h2> -<ul> -<li>AWS CloudWatch and monitoring tab. Enable detailed monitoring (This costs more money)</li> -<li>Set up <a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/snapshot-lifecycle.html#snapshot-lifecycle-console">Lifecycle Management for EBS snapshots</a></li> -<li>Steps to mounting a backup snapshot to the instance and restarting the application</li> -<li>Please see the second-half of &ldquo;Increasing disk size&rdquo; reference above</li> -<li>Setup a TLS cert &amp; DNS registration</li> -</ul>Hosting: Self Hosting in CHT 3.xhttps://docs.communityhealthtoolkit.org/hosting/3.x/self-hosting/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/3.x/self-hosting/ -<p>Whether run on bare-metal or in a cloud provider, the Community Health Toolkit (CHT) core framework has been packaged into a docker container to make it portable and easy to install. It is available from <a href="https://hub.docker.com/r/medicmobile/medic-os">dockerhub</a>. To learn more how to work with docker you could follow the tutorial <a href="https://docker-curriculum.com/#getting-started">here</a> and the cheat sheet <a href="https://docs.docker.com/get-started/docker_cheatsheet.pdf">here</a>.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Before continuing, ensure all <a href="https://docs.communityhealthtoolkit.org/hosting/requirements/">requirements</a> are met. -</div> -<h2 id="installing-with-a-compose-file">Installing with a compose file</h2> -<p>The CHT containers are installed using <a href="https://docs.docker.com/compose/reference/overview/">docker compose</a> so that you can run multiple containers as a single service.</p> -<p>Start by choosing the location where you would like to save your compose configuration file. Then create the <code>docker-compose.yml</code> file by <code>cd</code>ing into the correct directory and running:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -s -o docker-compose.yml https://raw.githubusercontent.com/medic/cht-core/master/scripts/docker-helper/docker-compose-developer-3.x-only.yml -</span></span></code></pre></div><p>The install requires an admin password that it will configure in the database. You need to provide this externally as an environment variable. Before you run the compose file, you need to export this variable as shown below.</p> -<p><code>export DOCKER_COUCHDB_ADMIN_PASSWORD=myAwesomeCHTAdminPassword</code></p> -<p>You can then run <code>docker-compose</code> in the folder where you put your compose <code>docker-compose.yml</code> file. To start, run it interactively to see all the logs on screen and be able to stop the containers with <code>ctrl</code> + <code>c</code>:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo docker-compose up -</span></span></code></pre></div><p>If there are no errors, stop the containers with <code>ctrl</code> + <code>c</code> and then run it detached with <code>-d</code>:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo docker-compose up -d -</span></span></code></pre></div><p>Note In certain shells, <code>docker-compose</code> may not interpolate the admin password that was exported in <code>DOCKER_COUCHDB_ADMIN_PASSWORD</code>. Check if this is the case by searching the logs in the medic-os dockers instance. If the <code>docker logs medic-os</code> command below returns a user and password, then the export above failed, and you should use this user and password to complete the installation:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker logs medic-os <span style="color:#000;font-weight:bold">|</span>grep <span style="color:#4e9a06">&#39;New CouchDB Admin&#39;</span> -</span></span><span style="display:flex;"><span>Info: New CouchDB Administrative User: medic -</span></span><span style="display:flex;"><span>Info: New CouchDB Administrative Password: password -</span></span></code></pre></div><p>Monitor the logs until you get the <code>Setting up software (100% complete)</code> message. At this stage all containers are fully set up.</p> -<p>Once containers are setup, please run the following command from your host terminal:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo docker <span style="color:#204a87">exec</span> -it medic-os /bin/bash -c <span style="color:#4e9a06">&#34;sed -i &#39;s/--install=3.9.0/--complete-install/g&#39; /srv/scripts/horticulturalist/postrun/horticulturalist&#34;</span> -</span></span><span style="display:flex;"><span>sudo docker <span style="color:#204a87">exec</span> -it medic-os /bin/bash -c <span style="color:#4e9a06">&#34;/boot/svc-disable medic-core openssh &amp;&amp; /boot/svc-disable medic-rdbms &amp;&amp; /boot/svc-disable medic-couch2pg&#34;</span> -</span></span></code></pre></div><p>The first command fixes a postrun script for horticulturalist to prevent unique scenarios of re-install. The second command removes extra services that you will not need.</p> -<h3 id="visit-your-project">Visit your project</h3> -<p>If you&rsquo;re running this on your local machine, then open a browser to <a href="https://localhost">https://localhost</a>. Otherwise open a browser to the public IP of the host if it&rsquo;s running remotely.</p> -<p>You will have to click to through the SSL Security warning. Click &ldquo;Advanced&rdquo; -&gt; &ldquo;Continue to site&rdquo;.</p> -<h3 id="clean-up-and-re-install">Clean up and re-install</h3> -<p>If some instructions were missed and there&rsquo;s a broken CHT deployment, use the commands below to start afresh:</p> -<ol> -<li> -<p>Stop containers: <code>docker stop medic-os &amp;&amp; docker stop haproxy</code></p> -</li> -<li> -<p>Remove containers: <code>docker rm medic-os &amp;&amp; docker rm haproxy</code></p> -</li> -<li> -<p>Clean data volume:<code>docker volume rm medic-data</code></p> -<p>Note: Running <code>docker-compose down -v</code> would do all the above 3 steps</p> -</li> -<li> -<p>Prune system: <code>docker system prune</code></p> -</li> -</ol> -<p>After following the above commands, you can re-run docker-compose up and create a clean install: <code>docker-compose up -d</code></p> -<h3 id="port-conflicts">Port Conflicts</h3> -<p>In case you are already running services on HTTP(80) and HTTPS(443),you will have to either remap ports to the medic-os container or stop the services using those ports.</p> -<p>To find out which service is using a conflicting port: On Linux:</p> -<p><code>sudo netstat -plnt | grep ':&lt;port&gt;'</code></p> -<p>On Mac (10.10 and above):</p> -<p><code>sudo lsof -iTCP -sTCP:LISTEN -n -P | grep ':&lt;port&gt;'</code></p> -<p>You can either kill the service which is occupying HTTP/HTTPS ports, or run the container with forwarded ports that are free. In your compose file, change the ports under medic-os:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">services</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">medic-os</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">container_name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">medic-os</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">image</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">medicmobile/medic-os:cht-3.7.0-rc.1</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">volumes</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#000">medic-data:/srv</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">ports</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#0000cf;font-weight:bold">8080</span><span style="color:#000;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">80</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#0000cf;font-weight:bold">444</span><span style="color:#000;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">443</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span></code></pre></div><p>Turn off and remove all existing containers that were started:</p> -<p><code>sudo docker-compose down</code></p> -<p>Bring Up the containers in detached mode with the new forwarded ports.</p> -<p><code>sudo docker-compose up -d</code></p> -<p>Note: You can substitute 8080, 444 with whichever ports are free on your host. You would now visit https://localhost:444 to visit your project.</p> -<h2 id="data-storage--persistence">Data storage &amp; persistence</h2> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Containers that are already set up will lose all data when following the steps below to remap the <code>/srv</code> directory. -</div> -<p>Docker containers are <a href="https://www.redhat.com/en/topics/cloud-native-apps/stateful-vs-stateless">stateless</a> by design. In order to persist your data when a container restarts you need to specify the volumes that the container can use to store data. The CHT app stores all its data in the <code>/srv</code> folder. This is the folder that you need to map to your volume before you spin up your containers.</p> -<p>Ideally you should map this folder to a volume that is backed up regularly by your cloud hosting provider.</p> -<p>The example below shows how to map this folder in Ubuntu:</p> -<ol> -<li> -<p>Create the <code>/srv</code> folder: <code>sudo mkdir /srv</code></p> -</li> -<li> -<p>Mount your volume to this folder: <code>sudo mount /dev/xvdg /srv</code> . The attached volume number varies. Find your volume by running <code>lsblk</code>.</p> -</li> -<li> -<p>Update your compose file so that the containers store data to this folder</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">services</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">medic-os</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">container_name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">medic-os</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">image</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">medicmobile/medic-os:cht-3.9.0-rc.2</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">volumes</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#000">/srv:/srv</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>----<span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">haproxy</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">container_name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">haproxy</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">image</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">medicmobile/haproxy:rc-1.17</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">volumes</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#000">/srv:/srv </span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span></code></pre></div></li> -</ol> -<p>Alternatively, can create the <code>/srv</code> folder on any drive with enough space that is regularly backed up. Then map the path to the folder in the compose file like this.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">volumes</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#000">/path/to/srv:/srv</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span></code></pre></div><p>Be sure to check the available storage space regularly and expand your volume when needed</p> -<h2 id="backup">Backup</h2> -<p>Regular backups should be made of the <code>/srv</code> directory to have holistic and easy to restore copies of all important data and the current CHT version installed. To backup just the data and not the CHT, make copies of <code>/srv/storage/medic-core/</code>. This directory includes 4 key sub-directies:</p> -<ul> -<li>./couchdb</li> -<li>./openssh</li> -<li>./nginx</li> -<li>./passwd</li> -</ul> -<p>To make backups of just CouchDB data outside of the CHT docker infrastructure, please see <a href="https://docs.couchdb.org/en/2.3.1/maintenance/backups.html">CouchDB&rsquo;s Backup docs for 2.3.1</a>. Please note:</p> -<ul> -<li>CouchDB data files are in <code>/srv/storage/medic-core/couchdb/data</code> in the <code>medic-os</code> container.</li> -<li>Backing up via replication is discouraged as restored DBs can cause offline users to restart replication from zero. Use file backups instead.</li> -</ul>Hosting: App Developer Hosting in CHT 3.xhttps://docs.communityhealthtoolkit.org/hosting/3.x/app-developer/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/3.x/app-developer/ -<div class="pageinfo pageinfo-primary"> -<p>This guide assumes you are a CHT app developer wanting to either run concurrent instances of the CHT, or easily be able to switch between different instances without loosing any data while doing so. To do development on the CHT core itself, see the <a href="https://docs.communityhealthtoolkit.org/contribute/code/core/dev-environment/">development guide</a>.</p> -<p>To deploy the CHT in production, see either <a href="https://docs.communityhealthtoolkit.org/hosting/3.x/self-hosting/">AWS hosting</a> or <a href="https://docs.communityhealthtoolkit.org/hosting/3.x/ec2-setup-guide/">Self hosting</a></p> -</div> -<h2 id="getting-started">Getting started</h2> -<p>Be sure to meet the <a href="https://docs.communityhealthtoolkit.org/hosting/requirements/">CHT hosting requirements</a> first. As well, if any other <code>medic-os</code> instances using <a href="https://raw.githubusercontent.com/medic/cht-core/master/scripts/docker-helper/docker-compose-developer-3.x-only.yml">the main <code>docker-compose-developer-3.x-only.yml</code> file</a> are running locally, stop them otherwise port, storage volume and container name conflicts may occur.</p> -<p>After meeting these requirements, download the developer YAML file in the directory you want to store them:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>curl -o docker-compose-developer-3.x-only.yml https://raw.githubusercontent.com/medic/cht-core/master/scripts/docker-helper/docker-compose-developer-3.x-only.yml -</span></span></code></pre></div><p>To start the first developer CHT instance, run <code>docker-compose</code> and specify the file that was just download:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>docker-compose -f docker-compose-developer-3.x-only.yml up -</span></span></code></pre></div><p>This may take some minutes to fully start depending on the speed of the Internet connection and speed of the bare-metal host. This is because docker needs to download all the storage layers for all the containers and the CHT needs to run the first run set up. After downloads and setup has completed, the CHT should be accessible on <a href="https://localhost">https://localhost</a>.</p> -<p>When connecting to a new dev CHT instance for the first time, an error will be shown, &ldquo;Your connection is not private&rdquo; (see <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/privacy.error.png">screenshot</a>). To get past this, click &ldquo;Advanced&rdquo; and then click &ldquo;Proceed to localhost&rdquo;.</p> -<h2 id="running-the-nth-cht-instance">Running the Nth CHT instance</h2> -<p>After running the first instance of the CHT, it&rsquo;s easy to run as many more as are needed. This is achieved by specifying different:</p> -<ul> -<li>port for <code>HTTP</code> redirects (<code>CHT_HTTP</code>)</li> -<li>port for <code>HTTPS</code> traffic (<code>CHT_HTTPS</code>)</li> -<li>project to for the docker compose call (<code>-p PROJECT</code>)</li> -</ul> -<p>Assuming you want to start a new project called <code>the_second</code> and start the instance on <code>HTTP</code> port <code>8081</code> and <code>HTTPS</code> port <code>8443</code>, this would be the command:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#000">CHT_HTTP</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#0000cf;font-weight:bold">8081</span> <span style="color:#000">CHT_HTTPS</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#0000cf;font-weight:bold">8443</span> docker-compose -p the_second -f docker-compose-developer-3.x-only.yml up -</span></span></code></pre></div><p>The second instance is now accessible at <a href="https://localhost:8443">https://localhost:8443</a>.</p> -<h2 id="the-env-file">The <code>.env</code> file</h2> -<p>Often times it&rsquo;s convenient to use revision control, like GitHub, to store and publish changes in a CHT app. A nice compliment to this is to store the specifics on how to run the <code>docker-compose</code> command for each app. By using a shared <code>docker-compose</code> configuration for all developers on the same app, it avoids any port collisions and enables all developers to have a unified configuration.</p> -<p>Using the above <code>the_second</code> sample project, we can create another directory to host this project&rsquo;s configuration:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>mkdir ../the_second -</span></span></code></pre></div><p>Create a file <code>../the_second/.env-docker-compose</code> with this contents:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#000">COMPOSE_PROJECT_NAME</span><span style="color:#ce5c00;font-weight:bold">=</span>the_second -</span></span><span style="display:flex;"><span><span style="color:#000">CHT_HTTP</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#0000cf;font-weight:bold">8081</span> -</span></span><span style="display:flex;"><span><span style="color:#000">CHT_HTTPS</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#0000cf;font-weight:bold">8443</span> -</span></span></code></pre></div><p>Now it&rsquo;s easy to boot this environment by specifying which <code>.env</code> file to use:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>docker-compose --env-file ../the_second/.env-docker-compose -f docker-compose-developer-3.x-only.yml up -</span></span></code></pre></div><h2 id="switching--concurrent-projects">Switching &amp; concurrent projects</h2> -<p>The easiest way to switch between projects is to stop the first set of containers and start the second set. Cancel the first project running in the foreground with <code>ctrl + c</code>. Then start the second project using either the <code>.env</code> file or use the explicit command with ports and project name as shown above.</p> -<p>To run projects concurrently, instead of cancelling the first one, open a second terminal and start the second project.</p> -<h2 id="cht-docker-helper">CHT Docker Helper</h2> -<p>The <code>cht-docker-compose.sh</code> scripts builds on the <code>docker-compose-developer-3.x-only.yml</code> and <code>.env</code> files used above by helping start CHT instances with a simple text based GUI:</p> -<p><img src="cht-docker-helper.png" alt="The cht-docker-compose.sh script showing the URL and version of the CHT instance as well as number of containers launched, global container count, medic images downloaded count and OS load average. Finally a &ldquo;Successfully started my_first_project&rdquo; message is shown and denotes the login is &ldquo;medic&rdquo; and the password is &ldquo;password&rdquo;."></p> -<h3 id="prerequisites">Prerequisites</h3> -<h4 id="os">OS</h4> -<p>This script has been heavily tested on Ubuntu and should work very well there. It has been lightly tested on WSL2 on Windows 10 and macOS (x86*) - both should likely work as well.</p> -<p>* It will not work on macOS on Apple Silicon (M1/M2).</p> -<h4 id="software">Software</h4> -<p>The script will check and require these commands. All but <code>docker</code> and <code>docker-compose</code> should be installed by default on Ubuntu:</p> -<ul> -<li>awk</li> -<li>curl</li> -<li>cut</li> -<li>dirname</li> -<li>docker (At least version 20.x)</li> -<li>docker-compose (At least version 1.27)</li> -<li>file</li> -<li>grep</li> -<li>head</li> -<li>nc</li> -<li>tail</li> -<li>tr</li> -<li>wc</li> -</ul> -<p>Optionally you can install <code>jq</code> so that the script can parse JSON and tell you which version of the CHT is running.</p> -<h4 id="docker-compose-file-and-helper-scripts">Docker compose file and helper scripts</h4> -<p>An up-to-date clone of <a href="https://github.com/medic/cht-core/">cht-core</a> has everything you need including:</p> -<ul> -<li><code>docker-compose-developer-3.x-only.yml</code></li> -<li><code>cht-docker-compose.sh</code></li> -<li><code>docker-status.sh</code></li> -</ul> -<h3 id="using">Using</h3> -<h4 id="syntax">Syntax</h4> -<p>The helper script is run by calling <code>./cht-docker-compose.sh</code>. It accepts one required and one optional arguments:</p> -<ul> -<li><code>-e | --env-file</code> - path to the environment file. Required</li> -<li><code>-d | --docker_action</code> - docker action to run: <code>up</code>, <code>down</code> or <code>destroy</code>. Optional, defaults to <code>up</code></li> -</ul> -<h4 id="nomenclature">Nomenclature</h4> -<p>Docker containers, networks and volumes are always named after the project you&rsquo;re using. So if your project is called <code>my_first_project</code>, you will see:</p> -<ul> -<li>Two containers: <code>my_first_project_medic-os_1</code> and <code>my_first_project_haproxy_1</code></li> -<li>One storage volume: <code>my_first_project_medic-data</code></li> -<li>One network: <code>my_first_project_medic-net</code></li> -</ul> -<h4 id="first-run">First Run</h4> -<p>These steps assume:</p> -<ul> -<li>the first project is <code>my_first_project</code></li> -<li>one <code>.env_docker</code> environment file per project</li> -<li><code>my_first_project</code> and <code>cht-core</code> directories are next to each other in the same parent directory</li> -<li>you have Internet connectivity (<em>See <a href="#booting-with-no-connectivity">booting with no connectivity</a></em>)</li> -</ul> -<p>Follow these steps to create your first developer instance. You can create as many as you&rsquo;d like:</p> -<ol> -<li>create <code>./my_first_project/.env_docker</code> with the contents: -<pre tabindex="0"><code>COMPOSE_PROJECT_NAME=my_first_project -CHT_HTTP=8080 -CHT_HTTPS=8443 -</code></pre></li> -<li>Change into the <code>docker-help</code> directory: <code>cd ./cht-core/scripts/docker-helper/</code></li> -<li>Run the helper script and specify your <code>.env_docker</code> file: -<pre tabindex="0"><code>./cht-docker-compose.sh -e ../../../my_first_project/.env_docker -</code></pre></li> -<li>Your CHT instance will be started when you see the text: -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>Successfully started project my_first_project -</span></span></code></pre></div></li> -</ol> -<h4 id="second-run">Second Run</h4> -<p>When you&rsquo;re done with an instance, be sure to shut it down:</p> -<pre tabindex="0"><code>./cht-docker-compose.sh -e ../../../my_first_project/.env_docker -d down -</code></pre><p>All information will be saved, and it should be quick to start for the Nth time.</p> -<p>To start an existing instance again, just run the command from the &ldquo;First Run&rdquo; section:</p> -<pre tabindex="0"><code>./cht-docker-compose.sh -e ../../../my_first_project/.env_docker -</code></pre><p>This command is safe to run as many times as you&rsquo;d like if you forget the state of your project&rsquo;s Docker containers.</p> -<h4 id="last-run">Last Run</h4> -<p>When you&rsquo;re done with a project and want to completely destroy it, run <code>destroy</code>:</p> -<pre tabindex="0"><code>./cht-docker-compose.sh -e ../../../my_first_project/.env_docker -d destroy -</code></pre><p><em><strong>NOTE</strong></em> - Be sure you want to run <code>destroy</code>. The script will <em>not</em> prompt &ldquo;are you sure?&rdquo; and it will just delete all your project&rsquo;s data.</p> -<h3 id="troubleshooting">Troubleshooting</h3> -<p>The main issue you&rsquo;re likely to run into is that the CHT doesn&rsquo;t correctly start up, the very reason this script was created. If the script either hangs on one step, or fails to start and quits after 5 tries, try these steps:</p> -<ol> -<li>Ensure your Internet is working.</li> -<li>Destroy everything by using the <code>-d destroy</code> <a href="#syntax">option</a>. While this will delete any data, if you can&rsquo;t start the CHT instance, you won&rsquo;t be loosing any data you care about ;). This will delete the containers and volumes. Then run <code>-d up</code> and try again.</li> -<li>Quit apps that may be causing a high load on your computer. Possibly consider rebooting and running nothing else. In one instance this helped!</li> -</ol> -<p>If you still get stuck review the items below as possible issues you may find workarounds to. If none of these work, see the debug file which is always output in the same directory as your <code>env_file</code>. File a ticket in this repository and attach this log file. To read more about the contents of the <code>cht-docker-compose.log</code> see <a href="#cht-docker-composelog">the CHT Docker Compose Log section</a>.</p> -<h4 id="running-without-the-ip-utility">Running without the <code>ip</code> utility</h4> -<p>If you&rsquo;re on macOS, or other OS without the <code>ip</code> utility, your IP address will always show as <code>127.0.0.1</code>. You can not connect to this IP from a mobile client on your LAN because it always references the host it&rsquo;s on, not a foreign host.</p> -<p>To work around this, you can find out your IP on your LAN and just replace the <code>127-0-0-1</code> part of the <code>https://127-0-0-1.local-ip.medicmobile.org:8443</code> URL to be your IP address. So if your local IP was <code>192.168.0.22</code> your URL would be <code>https://192-168-0-22.local-ip.medicmobile.org:8443</code>.</p> -<h4 id="booting-with-no-connectivity">Booting with no connectivity</h4> -<p>This script can work without connectivity after the initial boot. However, it needs connectivity to do DNS lookups for the <code>*.local-ip.medicmobile.org</code> URLs. To work around this, when you have no connectivity, add an entry in your <code>/etc/hosts</code> for the URL showing up in the script. For example, if you&rsquo;re seeing <code>https://127-0-0-1.local-ip.medicmobile.org:8443</code> as your IP, add this line to the top of your <code>/etc/hosts</code> file.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>127.0.0.1 127-0-0-1.local-ip.medicmobile.org -</span></span></code></pre></div><p><em><strong>NOTE</strong></em> - You need connectivity on the initial boot of the VM to connect to <code>staging.dev.medicmobile.org</code> to download the base version of the CHT. As well, certificates for <code>*.local-ip.medicmobile.org</code> are downloaded. Subsequent boots do not require connectivity as long as you do not run <code>destroy</code>.</p> -<h4 id="port-conflicts">Port conflicts</h4> -<p>If you have two <code>.env_docker</code> files that have the same ports or re-use the same project name, bad things will happen. Don&rsquo;t do this.</p> -<p>Medic recommends setting up unique project names and unique ports for each project. Commit these <code>.env_docker</code> files to your app config&rsquo;s revision control so all app developers use the same <code>.env_docker</code> files.</p> -<h4 id="slow-downloads-and-wait-periods">Slow downloads and wait periods</h4> -<p>During testing on an Internet connection with high latency (&gt;1000ms) and packet loss, this script had trouble booting the CHT instance because it was taking too long to download the assets from <code>staging.dev.medicmobile.org</code>. Each version is about 38 MB.</p> -<p>To account for this, the wait time is multiplied times the boot iteration for each time it reboots. It starts at 100 seconds and then 200, 300, 400 up to the fifth time it will wait 500 seconds.</p> -<h4 id="too-many-containers">Too many containers</h4> -<p>If you&rsquo;re on a resource constrained computer, like a very old or very slow laptop, be sure to watch the total number of containers you&rsquo;re running. More than one or two projects (2 or 4 containers) and you may notice a slow-down. You can use the <code>./docker-status.sh</code> script if you forgot which projects you have running:</p> -<p><img src="docker-status.png" alt="The docker-status.sh script showing 4 sections. The top section lists the running CHT containers, their IP, their mapped ports and the state (running time). The second section found on the left is a list of docker networks. The third section is on the top right and lists downloaded docker images. The furth section on the bottom right, shows docker volumes"></p> -<p>A word of caution though - for now this script doesn&rsquo;t scale well if you have 10s of containers and volumes. Content <a href="https://user-images.githubusercontent.com/1608415/137566806-e13b765e-4f95-48be-82e7-c8e6351e14b7.mp4">can scroll off the screen</a> and seem confusing!</p> -<h4 id="output-on-macos-is-too-narrow">Output on macOS is too narrow</h4> -<p>There&rsquo;s a known bug with the bash library we&rsquo;re using that causes it to always render at 80 characters wide. <a href="https://github.com/metal3d/bashsimplecurses/issues/51#issuecomment-905914780">The fix is</a> to use <code>brew</code> to run a more recent version of <code>ncurses</code>.</p> -<h4 id="device--does-not-exist-and-curl-6-could-not-resolve-host-errors">&ldquo;Device &rsquo;&rsquo; does not exist&rsquo;&rdquo; and &ldquo;curl: (6) Could not resolve host&rdquo; errors</h4> -<p>If you see either of these errors, you&rsquo;re very likely off-line such that you effectively cannot reach the Internet. The script will not work as is. See the &ldquo;Booting with no connectivity&rdquo; section above for work-arounds.</p> -<h4 id="resetting-everything">Resetting everything</h4> -<p>If you REALLY get stuck and want to destroy <em><strong>ALL</strong></em> docker containers/volumes/networks, even those not started by this script, run this (but be <em><strong>extra</strong></em> sure that&rsquo;s what you want to do):</p> -<pre tabindex="0"><code>docker stop $(docker ps -q)&amp;&amp;docker system prune&amp;&amp;docker volume prune -</code></pre><h3 id="cht-docker-composelog">cht-docker-compose.log</h3> -<p>This log will be output every time you call the script. It will be created and appended to in the directory where you specified your environment file (<code>-e PATH/TO/env_file</code>).</p> -<p>There are three types of lines in this file. The first line will always be the Start line: <code>item=&quot;start&quot;</code>. Then there will be a Status line: <code>item=&quot;status&quot;</code>. Finally, there will be two lines, one for each docker container: <code>item=&quot;docker_logs&quot;</code>.</p> -<h4 id="shared-head">Shared head</h4> -<p>All lines start with a date, PID and count:</p> -<table> -<thead> -<tr> -<th>Name in log</th> -<th>Note</th> -<th>Example</th> -</tr> -</thead> -<tbody> -<tr> -<td>(none - first item)</td> -<td>value from <code>date</code> command with <code>DAY DATE MONTH YEAR TIME AM/PM</code></td> -<td><code>Fri 15 Oct 2021 02:19:30 PM</code></td> -</tr> -<tr> -<td><code>pid</code></td> -<td>process ID of the shell script. Will always be an integer</td> -<td><code>398399</code></td> -</tr> -<tr> -<td><code>count</code></td> -<td>how many times the shell script has looped internally</td> -<td><code>2</code></td> -</tr> -</tbody> -</table> -<h4 id="item--start">item = <code>start</code></h4> -<p>When you first call the script, a line is output with generic information about the project. This is only shown once per call:</p> -<table> -<thead> -<tr> -<th>Name in log</th> -<th>Note</th> -<th>Example(s)</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>item</code></td> -<td>which log item this is</td> -<td><code>start</code></td> -</tr> -<tr> -<td><code>URL</code></td> -<td>the full URL of the instance, with port</td> -<td><code>https://192-168-68-17.local-ip.medicmobile.org:443</code></td> -</tr> -<tr> -<td><code>IP</code></td> -<td>the IP address of the instance</td> -<td><code>192.168.68.17</code></td> -</tr> -<tr> -<td><code>port_https</code></td> -<td>port used for <code>https</code></td> -<td><code>443</code> or <code>8443</code></td> -</tr> -<tr> -<td><code>port_http</code></td> -<td>port used for <code>http</code></td> -<td><code>80</code> or <code>8080</code></td> -</tr> -<tr> -<td><code>project_name</code></td> -<td>The user specified project name. This will be used in docker container and volume names</td> -<td><code>helper_test</code></td> -</tr> -<tr> -<td><code>total_containers</code></td> -<td>Total docker containers running on the host. Useful for catching high load scenarios. Healthy is under <code>10</code>.</td> -<td><code>2</code></td> -</tr> -</tbody> -</table> -<h4 id="item--status">item = <code>status</code></h4> -<p>For each internal loop of the script, each one taking 1-5 seconds, a status line is output:</p> -<table> -<thead> -<tr> -<th>Name in log</th> -<th>Note</th> -<th>Example(s)</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>item</code></td> -<td>which log item this is</td> -<td><code>status</code></td> -</tr> -<tr> -<td><code>CHT_count</code></td> -<td>number of CHT containers running for this project. Healthy is <code>2</code></td> -<td><code>2</code></td> -</tr> -<tr> -<td><code>port_stat</code></td> -<td>Status of the <code>https</code> port. Healthy is <code>open</code></td> -<td><code>open</code> or <code>closed</code></td> -</tr> -<tr> -<td><code>http_code</code></td> -<td>If the <code>https</code> port is open by the web server, what <code>HTTP</code> response code is returned for a <code>GET</code>. Healthy is <code>200</code>. If you see <code>000</code>, <a href="#booting-with-no-connectivity">see workarounds</a>.</td> -<td><code>200</code> or <code>404</code></td> -</tr> -<tr> -<td><code>ssl_verify</code></td> -<td>If the <code>https</code> port is open by the web server, is the valid <code>local-ip.medicmobile.org</code> certificate installed. Healthy is <code>yes</code></td> -<td><code>yes</code> or <code>no</code></td> -</tr> -<tr> -<td><code>reboot_count</code></td> -<td>How many times <code>docker restart</code> has been called. Max is <code>5</code></td> -<td><code>3</code></td> -</tr> -<tr> -<td><code>docker_call</code></td> -<td>The docker action call to the script</td> -<td><code>up</code></td> -</tr> -<tr> -<td><code>last_msg</code></td> -<td>Last message the user was shown</td> -<td><code>Running &quot;down&quot; then &quot;up&quot;</code></td> -</tr> -<tr> -<td><code>load_now</code></td> -<td>Load average for the past minute. Healthy can vary, but should be &lt; <code>10</code></td> -<td><code>2.66</code></td> -</tr> -<tr> -<td><code>*_haproxy_1</code></td> -<td>Name of container based on <code>project_name</code>. Showing the &ldquo;has booted&rdquo; status, healthy is <code>true</code></td> -<td><code>false</code></td> -</tr> -<tr> -<td><code>*_medic-os_1</code></td> -<td>Name of container based on <code>project_name</code>. Showing the &ldquo;has booted&rdquo; status, healthy is <code>true</code></td> -<td><code>false</code></td> -</tr> -</tbody> -</table> -<h4 id="item--docker_logs">item = <code>docker_logs</code></h4> -<table> -<thead> -<tr> -<th>Name in log</th> -<th>Note</th> -<th>Example(s)</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>item</code></td> -<td>which log item this is</td> -<td><code>docker_logs</code></td> -</tr> -<tr> -<td><code>container</code></td> -<td>Container name based on <code>project_name</code></td> -<td><code>helper_test_medic-os_1</code></td> -</tr> -<tr> -<td><code>processes</code></td> -<td>Number of process running in the container. Medic OS should have 60-90, Nginx 4-10</td> -<td><code>64</code></td> -</tr> -<tr> -<td><code>last_log</code></td> -<td>Result from calling <code>docker logs</code> on the container. Will never have <code>&quot;</code> in them and date may differ from start of line date</td> -<td><code>[2021/10/15 19:53:39] Info: Horticulturalist has already bootstrapped</code></td> -</tr> -</tbody> -</table> -<h4 id="sample">Sample</h4> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>Fri <span style="color:#0000cf;font-weight:bold">15</span> Oct <span style="color:#0000cf;font-weight:bold">2021</span> 02:30:26 PM PDT <span style="color:#000">pid</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;410066&#34;</span> <span style="color:#000">count</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;1&#34;</span> <span style="color:#000">item</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;start&#34;</span> <span style="color:#000">URL</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;https://192-168-68-17.local-ip.medicmobile.org:443&#34;</span> <span style="color:#000">IP</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;192.168.68.17&#34;</span> <span style="color:#000">port_https</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;443&#34;</span> <span style="color:#000">port_http</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;80&#34;</span> <span style="color:#000">project_name</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;helper_test&#34;</span> <span style="color:#000">total_containers</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;2&#34;</span> -</span></span><span style="display:flex;"><span>Fri <span style="color:#0000cf;font-weight:bold">15</span> Oct <span style="color:#0000cf;font-weight:bold">2021</span> 02:30:26 PM PDT <span style="color:#000">pid</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;410066&#34;</span> <span style="color:#000">count</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;1&#34;</span> <span style="color:#000">item</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;docker_logs&#34;</span> <span style="color:#000">container</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;helper_test_medic-os_1&#34;</span> <span style="color:#000">processes</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;64&#34;</span> <span style="color:#000">last_log</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;[2021/10/15 19:53:39] Info: Horticulturalist has already bootstrapped&#34;</span> -</span></span><span style="display:flex;"><span>Fri <span style="color:#0000cf;font-weight:bold">15</span> Oct <span style="color:#0000cf;font-weight:bold">2021</span> 02:30:26 PM PDT <span style="color:#000">pid</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;410066&#34;</span> <span style="color:#000">count</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;1&#34;</span> <span style="color:#000">item</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;docker_logs&#34;</span> <span style="color:#000">container</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;helper_test_haproxy_1&#34;</span> <span style="color:#000">processes</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;4&#34;</span> <span style="color:#000">last_log</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;Oct 15 21:30:20 576ca039cd88 haproxy[25]: 172.20.0.3,200,GET,/medic/_design/medic,-,medic,&#39;-&#39;,21703,5,21402,&#39;curl/7.68.0&#39;&#34;</span> -</span></span><span style="display:flex;"><span>Fri <span style="color:#0000cf;font-weight:bold">15</span> Oct <span style="color:#0000cf;font-weight:bold">2021</span> 02:30:26 PM PDT <span style="color:#000">pid</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;410066&#34;</span> <span style="color:#000">count</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;1&#34;</span> <span style="color:#000">item</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;status&#34;</span> <span style="color:#000">CHT_count</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;2&#34;</span> <span style="color:#000">port_stat</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;open&#34;</span> <span style="color:#000">http_code</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;200&#34;</span> <span style="color:#000">ssl_verify</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;yes&#34;</span> <span style="color:#000">reboot_count</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;0&#34;</span> <span style="color:#000">docker_call</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;up&#34;</span> <span style="color:#000">last_msg</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;Initializing&#34;</span> <span style="color:#000">load_now</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;2.66&#34;</span> <span style="color:#000">helper_test_haproxy_1</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;true&#34;</span> helper_test_medic-os_1<span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;true&#34;</span> -</span></span><span style="display:flex;"><span>Fri <span style="color:#0000cf;font-weight:bold">15</span> Oct <span style="color:#0000cf;font-weight:bold">2021</span> 02:30:28 PM PDT <span style="color:#000">pid</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;410066&#34;</span> <span style="color:#000">count</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;2&#34;</span> <span style="color:#000">item</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;docker_logs&#34;</span> <span style="color:#000">container</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;helper_test_medic-os_1&#34;</span> <span style="color:#000">processes</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;67&#34;</span> <span style="color:#000">last_log</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;[2021/10/15 19:53:39] Info: Horticulturalist has already bootstrapped&#34;</span> -</span></span><span style="display:flex;"><span>Fri <span style="color:#0000cf;font-weight:bold">15</span> Oct <span style="color:#0000cf;font-weight:bold">2021</span> 02:30:28 PM PDT <span style="color:#000">pid</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;410066&#34;</span> <span style="color:#000">count</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;2&#34;</span> <span style="color:#000">item</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;docker_logs&#34;</span> <span style="color:#000">container</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;helper_test_haproxy_1&#34;</span> <span style="color:#000">processes</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;4&#34;</span> <span style="color:#000">last_log</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;Oct 15 21:30:27 576ca039cd88 haproxy[25]: 172.20.0.3,200,GET,/medic/_design/medic,-,medic,&#39;-&#39;,21703,5,21402,&#39;curl/7.68.0&#39;&#34;</span> -</span></span><span style="display:flex;"><span>Fri <span style="color:#0000cf;font-weight:bold">15</span> Oct <span style="color:#0000cf;font-weight:bold">2021</span> 02:30:28 PM PDT <span style="color:#000">pid</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;410066&#34;</span> <span style="color:#000">count</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;2&#34;</span> <span style="color:#000">item</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;status&#34;</span> <span style="color:#000">CHT_count</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;2&#34;</span> <span style="color:#000">port_stat</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;open&#34;</span> <span style="color:#000">http_code</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;200&#34;</span> <span style="color:#000">ssl_verify</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;yes&#34;</span> <span style="color:#000">reboot_count</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;0&#34;</span> <span style="color:#000">docker_call</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;up&#34;</span> <span style="color:#000">last_msg</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34; :) &#34;</span> <span style="color:#000">load_now</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;2.69&#34;</span> <span style="color:#000">helper_test_haproxy_1</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;true&#34;</span> helper_test_medic-os_1<span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;true&#34;</span> -</span></span></code></pre></div><h2 id="cookie-collisions">Cookie collisions</h2> -<p>The CHT stores its cookies based on the domain. This means if you&rsquo;re running two concurrent instances on <code>https://192-168-68-40.local-ip.medicmobile.org:8443</code> and <code>https://192-168-68-40.local-ip.medicmobile.org:8440</code> (note different ports), the CHT would write the cookie under the same <code>192-168-68-40.local-ip.medicmobile.org</code> domain. When logging out of one instance, you would get logged out of both and other consistencies.</p> -<p>To avoid this collision of cookies, you can use different IP addresses to access the instances. This works because of two reasons:</p> -<ol> -<li>the TLS certificate being used is valid for any subdomain of <code>*.local-ip.medicmobile.org</code>. Further, the URL always resolves to the IP passed in the <code>*</code> section, so you can use any IP</li> -<li>the IPs that are available to reference your <code>localhost</code> are actually a <code>/8</code> netmask, meaning <a href="https://en.wikipedia.org/wiki/Localhost#Name_resolution">there are 16 million addresses</a> to choose from!</li> -</ol> -<p>Using the above two reasons, these URLs could work to avoid the cookie collision:</p> -<ul> -<li><code>https://127-0-0-1.local-ip.medicmobile.org:8443</code></li> -<li><code>https://127-0-0-2.local-ip.medicmobile.org:8440</code></li> -</ul> -<p>This would result in the domains being <code>127.0.0.1</code> and <code>127.0.0.2</code> from the CHT&rsquo;s perspective. When using a mobile device for testing, you&rsquo;re limited to use the LAN ip output in the helper and can not use the <code>127.x.x.x</code> IPs.</p>Hosting: SSL Cert Install in CHT 3.xhttps://docs.communityhealthtoolkit.org/hosting/3.x/ssl-cert-install/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/3.x/ssl-cert-install/ -<h2 id="requirements">Requirements</h2> -<ul> -<li>Installed CHT-Core 3.x via either <a href="https://docs.communityhealthtoolkit.org/hosting/3.x/self-hosting/">Self Hosted</a>, <a href="https://docs.communityhealthtoolkit.org/hosting/3.x/ec2-setup-guide/">EC2</a> or <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/">Local Setup</a>, but must use <code>docker-compose</code>.</li> -<li>Your own SSL certifications like Let&rsquo;s Encrypt.</li> -</ul> -<h2 id="copy-certs-into-medic-os-container">Copy certs into medic-os container</h2> -<p>On your server copy the <code>.crt</code> and <code>.key</code> files to the <code>medic-os</code> container. The existing self signed <code>.crt</code> and <code>.key</code> files will be overwritten:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo docker cp /path/to/ssl.crt medic-os:/srv/settings/medic-core/nginx/private/default.crt -</span></span><span style="display:flex;"><span>sudo docker cp /path/to/ssl.key medic-os:/srv/settings/medic-core/nginx/private/default.key -</span></span></code></pre></div><h2 id="restart-services">Restart services</h2> -<p>Now that the <code>.crt</code> and <code>.key</code> files are in place, restart <code>nginx</code> in the <code>medic-os</code> container with:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker <span style="color:#204a87">exec</span> -it medic-os /boot/svc-restart medic-core nginx -</span></span></code></pre></div><h2 id="view-nginx-logs">View Nginx Logs</h2> -<p>To troubleshoot any problems with the new certificates, after running <code>docker exec -it medic-os bash</code>, the <code>nginx</code> log files can be found in <code>/srv/storage/medic-core/nginx/logs/</code>, including:</p> -<ul> -<li>access.log</li> -<li>error-ssl.log</li> -<li>error.log</li> -<li>startup.log</li> -</ul>Hosting: Offline Hosting of CHT 3.x Serverhttps://docs.communityhealthtoolkit.org/hosting/3.x/offline/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/3.x/offline/ -<div class="pageinfo pageinfo-primary"> -<p>This guide is not meant for a production CHT instance. Support may be added in the future an offline CHT server in a production environment. Please see the &ldquo;Considerations&rdquo; section below.</p> -<p>Proceed only if you have staff familiar with DNS, TLS Certs, DHCP, LAN topology and Linux in general. This is a complex deployment where mistakes are easy to make unless proper training is in place.</p> -</div> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -This guide only applies to CHT 3.x. -</div> -<h2 id="introduction">Introduction</h2> -<p>The CHT is built as an <a href="https://docs.communityhealthtoolkit.org/core/overview/offline-first/">Offline-First</a> application. This applies to clients, either browsers or Android applications, connecting to the CHT server. The server itself assumes it has Internet connectivity to provide services such as DNS, software updates and general use connectivity. This document explores what it looks like when the CHT server is offline without these services available.</p> -<p>Running a CHT server offline requires no modifications to the CHT itself. Instead, supporting services normally found online are replicated locally.</p> -<h2 id="considerations">Considerations</h2> -<p>An offline CHT server is most appropriate for a development environment. There are serious implications to consider before deploying an offline instance per our <a href="https://docs.communityhealthtoolkit.org/hosting/requirements/#considerations">existing requirements</a>.</p> -<p>Additionally, if users are going to migrate between offline locations with the same domain name, always ensure different login and passwords are used for all users across instances. This will prevent a client from another CHT instance trying to synchronize with a CHT instance it shouldn&rsquo;t synchronize with, possibly causing data corruption or privacy issues through unintended data access.</p> -<h2 id="requirements">Requirements</h2> -<p>A CHT instance is accessible offline when you can resolve the domain to an IP address, and a TLS certificate is on the CHT server with a common name (CN) that matches the domain name. On top of the <a href="https://docs.communityhealthtoolkit.org/hosting/requirements/">existing requirements</a>, the following aspects must also be considered.</p> -<h3 id="static-ip">Static IP</h3> -<p>The CHT server needs to be given a static IP so that DNS will always resolve to the correct host.</p> -<h3 id="tls-certificate">TLS Certificate</h3> -<p>Browsers might allow you to connect to a server with an invalid TLS certificate after you <a href="https://www.ssl.com/guide/troubleshooting-ssl-tls-browser-errors-and-warnings/">bypass the warning</a>. Android apps however, like <a href="https://github.com/medic/cht-android">CHT Android App</a>, require a valid TLS certificate to work correctly, therefore you would need to acquire a valid TLS certificate from a certificate authority (CA) and install it on your CHT server.</p> -<p>It is common to use <a href="https://en.wikipedia.org/wiki/Let%27s_encrypt">Let&rsquo;s Encrypt</a> to acquire certificates because they provide free certificates. Let&rsquo;s Encrypt certificates expire after 90 days, so the server will need to be constantly updated with a new certificate. Other CAs provide certificates that expire after a year, so this concern will always apply.</p> -<p>After acquiring the certificate, if you are running a Docker-based CHT deployment, see <a href="https://docs.communityhealthtoolkit.org/hosting/3.x/ssl-cert-install/">TLS instructions for Docker</a> to install the certificate.</p> -<h3 id="domain-name-server">Domain Name Server</h3> -<p>In order to match the static IP of the web server to the CN in the certificate, a Domain Name Server (DNS) must be used. This will allow any client on the LAN to easily connect to your CHT server without needing anything more than the domain name.</p> -<p>Most LANs will defer to the Internet Service Provider (ISP) to provide DNS, but there is no ISP in an offline scenario. Instead, one must be provided. This DNS server will then be configured to have an <code>A</code> record (or <code>AAAA</code> in the case of IPv6) to point to the CHT server.</p> -<h3 id="dynamic-host-configuration-protocol">Dynamic Host Configuration Protocol</h3> -<p>Any new client that connects to a network will get an IP address from a Dynamic Host Configuration Protocol (DHCP) server. It is critical that the DHCP server for the LAN the CHT is on instructs all clients to use the DNS server configured above.</p> -<h3 id="wi-fi-ap">Wi-Fi AP</h3> -<p>A Wi-FI Access Point (AP) needs to be deployed on the LAN so Android devices can connect to the CHT. This can be an AP included with the router or a standalone AP. If the AP is standalone, check that any DHCP or DNS servers that could conflict with the one above are disabled.</p> -<h2 id="benefits-over-other-solutions">Benefits Over Other Solutions</h2> -<p>An offline deployment may consider substituting some requirements above with these other solutions. Note that ngrok and local-ip.co require Internet connectivity, so are not an offline solution.</p> -<h3 id="ngrok">ngrok</h3> -<p>When an offline solution is deployed, traffic stays 100% local, whereas when using either <a href="https://docs.communityhealthtoolkit.org/apps/guides/debugging/secure-sharing-of-developer-instance/">your own reverse proxy</a> or a third party provider like <a href="https://ngrok.com/">ngrok</a>, traffic may traverse 100s or 1,000s of kilometers to ultimately reach the CHT server which is 10 meters away. This can help when Internet connectivity is very slow, very expensive per megabyte, or both.</p> -<h3 id="local-ipco">local-ip.co</h3> -<p><a href="http://local-ip.co/">local-ip.co</a>, and <a href="https://local-ip.medicmobile.org/">related services</a>, offer both the TLS certificates and private keys for <code>*.my.local-ip.co</code>. Additionally, the service has a DNS server that dynamically maps any IP you pass in the sub-sub-domain to the real world IP such that <code>192-168-0-1.my.local-ip.co</code> would resolve to <code>192.168.0.1</code>. This can make it very handy to deploy a development instance where all HTTP traffic remains local (unlike <code>ngrok</code> above).</p> -<p>As the DNS traffic still needs to leave your network and return, it is not a viable solution for a truly offline CHT deployment.</p> -<h3 id="self-signed-certificates">Self-Signed Certificates</h3> -<p>Another option to consider is to <a href="https://gist.github.com/fntlnz/cf14feb5a46b2eda428e000157447309">self-sign the certificates</a> and then either bypass the warnings in browsers or install the new CA root certificate on your devices. While this may work for a development environment with a single developer, it will be hard to scale to an environment where you&rsquo;d like to easily provision many Android devices. The work will be much more than just installing an APK form the Play Store (or the slightly harder side load process).</p> -<p>This may only work on certain, older version of Android as well.</p> -<h3 id="no-dhcp-or-dns-server">No DHCP or DNS Server</h3> -<p>To avoid installing both the DHCP and DNS servers, an Android app that enables custom DNS entries, like <a href="https://play.google.com/store/apps/details?id=com.burakgon.dnschanger">DNS Changer</a> could be used. As <a href="https://stackoverflow.com/questions/6370017/mapping-a-hostname-to-an-ip-address-on-android">seen here</a>, on each Android device you could install this and add custom DNS entries to reach the TLS certificate on the CHT.</p> -<p>Like the self-signed certificate solution, this is hard to scale and would need to be complimented by editing <code>/etc/hosts</code> files on desktop browsers.</p> \ No newline at end of file +3.x on Community Health Toolkithttps://docs.communityhealthtoolkit.org/hosting/3.x/Recent content in 3.x on Community Health ToolkitHugo -- gohugo.ioenAWS Hosting in CHT 3.xhttps://docs.communityhealthtoolkit.org/hosting/3.x/ec2-setup-guide/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/3.x/ec2-setup-guide/Most production CHT instances are deployed on AWS EC2. Leveraging Elastic Compute Cloud (EC2) and Elastic Block Store (EBS), CHT instances can easily be scaled up with larger EC2 instances and have easy increased disk space, backup and restores with EBS. +This guide will walk you through the process of creating an EC2 instance, mounting an EBS volume and provisioning Docker containers. +Create and Configure EC2 Instance Create EC2 (use security best practices)Self Hosting in CHT 3.xhttps://docs.communityhealthtoolkit.org/hosting/3.x/self-hosting/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/3.x/self-hosting/Whether run on bare-metal or in a cloud provider, the Community Health Toolkit (CHT) core framework has been packaged into a docker container to make it portable and easy to install. It is available from dockerhub. To learn more how to work with docker you could follow the tutorial here and the cheat sheet here. +Note Before continuing, ensure all requirements are met. Installing with a compose file The CHT containers are installed using docker compose so that you can run multiple containers as a single service.App Developer Hosting in CHT 3.xhttps://docs.communityhealthtoolkit.org/hosting/3.x/app-developer/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/3.x/app-developer/This guide assumes you are a CHT app developer wanting to either run concurrent instances of the CHT, or easily be able to switch between different instances without loosing any data while doing so. To do development on the CHT core itself, see the development guide. +To deploy the CHT in production, see either AWS hosting or Self hosting +Getting started Be sure to meet the CHT hosting requirements first.SSL Cert Install in CHT 3.xhttps://docs.communityhealthtoolkit.org/hosting/3.x/ssl-cert-install/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/3.x/ssl-cert-install/Requirements Installed CHT-Core 3.x via either Self Hosted, EC2 or Local Setup, but must use docker-compose. Your own SSL certifications like Let&rsquo;s Encrypt. Copy certs into medic-os container On your server copy the .crt and .key files to the medic-os container. The existing self signed .crt and .key files will be overwritten: +sudo docker cp /path/to/ssl.crt medic-os:/srv/settings/medic-core/nginx/private/default.crt sudo docker cp /path/to/ssl.key medic-os:/srv/settings/medic-core/nginx/private/default.key Restart services Now that the .crt and .key files are in place, restart nginx in the medic-os container with:Offline Hosting of CHT 3.x Serverhttps://docs.communityhealthtoolkit.org/hosting/3.x/offline/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/3.x/offline/This guide is not meant for a production CHT instance. Support may be added in the future an offline CHT server in a production environment. Please see the &ldquo;Considerations&rdquo; section below. +Proceed only if you have staff familiar with DNS, TLS Certs, DHCP, LAN topology and Linux in general. This is a complex deployment where mistakes are easy to make unless proper training is in place. +Note This guide only applies to CHT 3. \ No newline at end of file diff --git a/hosting/3.x/offline/index.html b/hosting/3.x/offline/index.html index 2586e5fc8d..c050a58d4a 100644 --- a/hosting/3.x/offline/index.html +++ b/hosting/3.x/offline/index.html @@ -1,9 +1,9 @@ -Offline Hosting of CHT 3.x Server | Community Health Toolkit +Offline Hosting of CHT 3.x Server | Community Health Toolkit

    Offline Hosting of CHT 3.x Server

    Deploying and hosting CHT Core server instances without Internet connectivity

    This guide is not meant for a production CHT instance. Support may be added in the future an offline CHT server in a production environment. Please see the “Considerations” section below.

    Proceed only if you have staff familiar with DNS, TLS Certs, DHCP, LAN topology and Linux in general. This is a complex deployment where mistakes are easy to make unless proper training is in place.

    Introduction

    The CHT is built as an Offline-First application. This applies to clients, either browsers or Android applications, connecting to the CHT server. The server itself assumes it has Internet connectivity to provide services such as DNS, software updates and general use connectivity. This document explores what it looks like when the CHT server is offline without these services available.

    Running a CHT server offline requires no modifications to the CHT itself. Instead, supporting services normally found online are replicated locally.

    Considerations

    An offline CHT server is most appropriate for a development environment. There are serious implications to consider before deploying an offline instance per our existing requirements.

    Additionally, if users are going to migrate between offline locations with the same domain name, always ensure different login and passwords are used for all users across instances. This will prevent a client from another CHT instance trying to synchronize with a CHT instance it shouldn’t synchronize with, possibly causing data corruption or privacy issues through unintended data access.

    Requirements

    A CHT instance is accessible offline when you can resolve the domain to an IP address, and a TLS certificate is on the CHT server with a common name (CN) that matches the domain name. On top of the existing requirements, the following aspects must also be considered.

    Static IP

    The CHT server needs to be given a static IP so that DNS will always resolve to the correct host.

    TLS Certificate

    Browsers might allow you to connect to a server with an invalid TLS certificate after you bypass the warning. Android apps however, like CHT Android App, require a valid TLS certificate to work correctly, therefore you would need to acquire a valid TLS certificate from a certificate authority (CA) and install it on your CHT server.

    It is common to use Let’s Encrypt to acquire certificates because they provide free certificates. Let’s Encrypt certificates expire after 90 days, so the server will need to be constantly updated with a new certificate. Other CAs provide certificates that expire after a year, so this concern will always apply.

    After acquiring the certificate, if you are running a Docker-based CHT deployment, see TLS instructions for Docker to install the certificate.

    Domain Name Server

    In order to match the static IP of the web server to the CN in the certificate, a Domain Name Server (DNS) must be used. This will allow any client on the LAN to easily connect to your CHT server without needing anything more than the domain name.

    Most LANs will defer to the Internet Service Provider (ISP) to provide DNS, but there is no ISP in an offline scenario. Instead, one must be provided. This DNS server will then be configured to have an A record (or AAAA in the case of IPv6) to point to the CHT server.

    Dynamic Host Configuration Protocol

    Any new client that connects to a network will get an IP address from a Dynamic Host Configuration Protocol (DHCP) server. It is critical that the DHCP server for the LAN the CHT is on instructs all clients to use the DNS server configured above.

    Wi-Fi AP

    A Wi-FI Access Point (AP) needs to be deployed on the LAN so Android devices can connect to the CHT. This can be an AP included with the router or a standalone AP. If the AP is standalone, check that any DHCP or DNS servers that could conflict with the one above are disabled.

    Benefits Over Other Solutions

    An offline deployment may consider substituting some requirements above with these other solutions. Note that ngrok and local-ip.co require Internet connectivity, so are not an offline solution.

    ngrok

    When an offline solution is deployed, traffic stays 100% local, whereas when using either your own reverse proxy or a third party provider like ngrok, traffic may traverse 100s or 1,000s of kilometers to ultimately reach the CHT server which is 10 meters away. This can help when Internet connectivity is very slow, very expensive per megabyte, or both.

    local-ip.co

    local-ip.co, and related services, offer both the TLS certificates and private keys for *.my.local-ip.co. Additionally, the service has a DNS server that dynamically maps any IP you pass in the sub-sub-domain to the real world IP such that 192-168-0-1.my.local-ip.co would resolve to 192.168.0.1. This can make it very handy to deploy a development instance where all HTTP traffic remains local (unlike ngrok above).

    As the DNS traffic still needs to leave your network and return, it is not a viable solution for a truly offline CHT deployment.

    Self-Signed Certificates

    Another option to consider is to self-sign the certificates and then either bypass the warnings in browsers or install the new CA root certificate on your devices. While this may work for a development environment with a single developer, it will be hard to scale to an environment where you’d like to easily provision many Android devices. The work will be much more than just installing an APK form the Play Store (or the slightly harder side load process).

    This may only work on certain, older version of Android as well.

    No DHCP or DNS Server

    To avoid installing both the DHCP and DNS servers, an Android app that enables custom DNS entries, like DNS Changer could be used. As seen here, on each Android device you could install this and add custom DNS entries to reach the TLS certificate on the CHT.

    Like the self-signed certificate solution, this is hard to scale and would need to be complimented by editing /etc/hosts files on desktop browsers.

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/hosting/3.x/self-hosting/index.html b/hosting/3.x/self-hosting/index.html index 0991ec1bd3..f9ebc8b18f 100644 --- a/hosting/3.x/self-hosting/index.html +++ b/hosting/3.x/self-hosting/index.html @@ -1,9 +1,9 @@ -Self Hosting in CHT 3.x | Community Health Toolkit +Self Hosting in CHT 3.x | Community Health Toolkit

    Self Hosting in CHT 3.x

    Hosting the CHT on self run infrastructure

    Whether run on bare-metal or in a cloud provider, the Community Health Toolkit (CHT) core framework has been packaged into a docker container to make it portable and easy to install. It is available from dockerhub. To learn more how to work with docker you could follow the tutorial here and the cheat sheet here.

    Installing with a compose file

    The CHT containers are installed using docker compose so that you can run multiple containers as a single service.

    Start by choosing the location where you would like to save your compose configuration file. Then create the docker-compose.yml file by cding into the correct directory and running:

    curl -s -o docker-compose.yml https://raw.githubusercontent.com/medic/cht-core/master/scripts/docker-helper/docker-compose-developer-3.x-only.yml
    + Create project issue

    Self Hosting in CHT 3.x

    Hosting the CHT on self run infrastructure

    Whether run on bare-metal or in a cloud provider, the Community Health Toolkit (CHT) core framework has been packaged into a docker container to make it portable and easy to install. It is available from dockerhub. To learn more how to work with docker you could follow the tutorial here and the cheat sheet here.

    Installing with a compose file

    The CHT containers are installed using docker compose so that you can run multiple containers as a single service.

    Start by choosing the location where you would like to save your compose configuration file. Then create the docker-compose.yml file by cding into the correct directory and running:

    curl -s -o docker-compose.yml https://raw.githubusercontent.com/medic/cht-core/master/scripts/docker-helper/docker-compose-developer-3.x-only.yml
     

    The install requires an admin password that it will configure in the database. You need to provide this externally as an environment variable. Before you run the compose file, you need to export this variable as shown below.

    export DOCKER_COUCHDB_ADMIN_PASSWORD=myAwesomeCHTAdminPassword

    You can then run docker-compose in the folder where you put your compose docker-compose.yml file. To start, run it interactively to see all the logs on screen and be able to stop the containers with ctrl + c:

    sudo docker-compose up 
     

    If there are no errors, stop the containers with ctrl + c and then run it detached with -d:

    sudo docker-compose up -d
     

    Note In certain shells, docker-compose may not interpolate the admin password that was exported in DOCKER_COUCHDB_ADMIN_PASSWORD. Check if this is the case by searching the logs in the medic-os dockers instance. If the docker logs medic-os command below returns a user and password, then the export above failed, and you should use this user and password to complete the installation:

    docker logs medic-os  |grep 'New CouchDB Admin'
    @@ -335,7 +335,8 @@
     

    Be sure to check the available storage space regularly and expand your volume when needed

    Backup

    Regular backups should be made of the /srv directory to have holistic and easy to restore copies of all important data and the current CHT version installed. To backup just the data and not the CHT, make copies of /srv/storage/medic-core/. This directory includes 4 key sub-directies:

    • ./couchdb
    • ./openssh
    • ./nginx
    • ./passwd

    To make backups of just CouchDB data outside of the CHT docker infrastructure, please see CouchDB’s Backup docs for 2.3.1. Please note:

    • CouchDB data files are in /srv/storage/medic-core/couchdb/data in the medic-os container.
    • Backing up via replication is discouraged as restored DBs can cause offline users to restart replication from zero. Use file backups instead.

    Hosting > 3.x > AWS Hosting

    Hosting the CHT on Amazon EC2

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/hosting/3.x/ssl-cert-install/index.html b/hosting/3.x/ssl-cert-install/index.html index 5180da7233..90edef4145 100644 --- a/hosting/3.x/ssl-cert-install/index.html +++ b/hosting/3.x/ssl-cert-install/index.html @@ -1,9 +1,9 @@ -SSL Cert Install in CHT 3.x | Community Health Toolkit +SSL Cert Install in CHT 3.x | Community Health Toolkit

    SSL Cert Install in CHT 3.x

    SSL Cert Installation for Self-Hosting Setups using Medic OS/3.x

    Requirements

    • Installed CHT-Core 3.x via either Self Hosted, EC2 or Local Setup, but must use docker-compose.
    • Your own SSL certifications like Let’s Encrypt.

    Copy certs into medic-os container

    On your server copy the .crt and .key files to the medic-os container. The existing self signed .crt and .key files will be overwritten:

    sudo docker cp /path/to/ssl.crt medic-os:/srv/settings/medic-core/nginx/private/default.crt
    + Create project issue

    SSL Cert Install in CHT 3.x

    SSL Cert Installation for Self-Hosting Setups using Medic OS/3.x

    Requirements

    • Installed CHT-Core 3.x via either Self Hosted, EC2 or Local Setup, but must use docker-compose.
    • Your own SSL certifications like Let’s Encrypt.

    Copy certs into medic-os container

    On your server copy the .crt and .key files to the medic-os container. The existing self signed .crt and .key files will be overwritten:

    sudo docker cp /path/to/ssl.crt medic-os:/srv/settings/medic-core/nginx/private/default.crt
     sudo docker cp /path/to/ssl.key medic-os:/srv/settings/medic-core/nginx/private/default.key
     

    Restart services

    Now that the .crt and .key files are in place, restart nginx in the medic-os container with:

    docker exec -it medic-os /boot/svc-restart medic-core nginx 
     

    View Nginx Logs

    To troubleshoot any problems with the new certificates, after running docker exec -it medic-os bash, the nginx log files can be found in /srv/storage/medic-core/nginx/logs/, including:

    • access.log
    • error-ssl.log
    • error.log
    • startup.log

    Hosting > 3.x > Self Hosting

    Hosting the CHT on self run infrastructure

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/hosting/4.x/_partial_docker_directories/index.html b/hosting/4.x/_partial_docker_directories/index.html index fb7dd30428..aabf2d71d6 100644 --- a/hosting/4.x/_partial_docker_directories/index.html +++ b/hosting/4.x/_partial_docker_directories/index.html @@ -1,4 +1,4 @@ -Docker Directory Setup | Community Health Toolkit +Docker Directory Setup | Community Health Toolkit

    Docker Directory Setup

    Create the following directory structure:

    |-- /home/ubuntu/cht/
    + Create project issue

    Docker Directory Setup

    Create the following directory structure:

    |-- /home/ubuntu/cht/
                       |-- compose/
                       |-- certs/
                       |-- couchdb/
                       |-- upgrade-service/
     

    By calling this mkdir commands:

    mkdir -p /home/ubuntu/cht/{compose,certs,upgrade-service,couchdb}
     
    1. compose - docker-compose files for cht-core and CouchDB
    2. certs - TLS certificates directory
    3. upgrade-service - where docker-compose file for the upgrade-service
    4. couchdb - the path for the docker-compose file of the upgrade-service (not used in multi-node)
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/hosting/4.x/adding-tls-certificates/index.html b/hosting/4.x/adding-tls-certificates/index.html index a4122d0da6..490c7f4dd4 100644 --- a/hosting/4.x/adding-tls-certificates/index.html +++ b/hosting/4.x/adding-tls-certificates/index.html @@ -1,9 +1,9 @@ -Adding TLS certificates in CHT 4.x | Community Health Toolkit +Adding TLS certificates in CHT 4.x | Community Health Toolkit

    Adding TLS certificates in CHT 4.x

    How to add TLS certificates to your docker hosted CHT 4.x instance

    By default, CHT 4.x will create a self-signed certificate for every deployment. These instructions are for changing to either a pre-existing certificate or automatically creating and renewing a Certbot based certificate using ACME, like Let’s Encrypt.

    This guide assumes you’ve already met the hosting requirements, specifically around Docker being installed.

    Pre-existing certificate

    To load your certificates into your CHT instance, we’ll be creating an interstitial container called cht-temp-tls which will enable you to copy your local certificate files into the native docker volume.

    Prerequisites

    You have two files locally on your workstation in the directory you’re currently in:

    • key.pem - the private key for your TLS certificate
    • cert.pem - both the public and any interstitial keys concatenated into one file

    Also, be sure you have started your CHT instance once and all your volumes are created.

    Loading the certificate

    1. Find the name of your cht-ssl volume with this call:

      docker volume ls --filter "name=cht-ssl"
      + Create project issue

      Adding TLS certificates in CHT 4.x

      How to add TLS certificates to your docker hosted CHT 4.x instance

      By default, CHT 4.x will create a self-signed certificate for every deployment. These instructions are for changing to either a pre-existing certificate or automatically creating and renewing a Certbot based certificate using ACME, like Let’s Encrypt.

      This guide assumes you’ve already met the hosting requirements, specifically around Docker being installed.

      Pre-existing certificate

      To load your certificates into your CHT instance, we’ll be creating an interstitial container called cht-temp-tls which will enable you to copy your local certificate files into the native docker volume.

      Prerequisites

      You have two files locally on your workstation in the directory you’re currently in:

      • key.pem - the private key for your TLS certificate
      • cert.pem - both the public and any interstitial keys concatenated into one file

      Also, be sure you have started your CHT instance once and all your volumes are created.

      Loading the certificate

      1. Find the name of your cht-ssl volume with this call:

        docker volume ls --filter "name=cht-ssl"
         

        It is very likely that cht_cht-ssl is the name of our cht-ssl volume.

      2. Using the volume name found in step 1, start a container called temp which allow us to copy files into the docker volume:

        docker run -d --rm --name temp -v cht_cht-ssl:/etc/nginx/private/ alpine tail -f /dev/null
         
      3. Copy the two pem files into the volume via the temporary container:

        docker cp key.pem temp:/etc/nginx/private/.
         docker cp cert.pem temp:/etc/nginx/private/.
        @@ -344,7 +344,8 @@
         "RequestError: Error [ERR_TLS_CERT_ALTNAME_INVALID]: Hostname/IP does not match certificate's altnames:
         Host: abc.com. is not in the cert's altnames: DNS:def.org"
         

      The addition of servername resolves this error by providing routing information. See docs for tls.connect(options[, callback]): “Server name for the SNI (Server Name Indication) TLS extension. It is the name of the host being connected to, and must be a host name, and not an IP address.”.

      A servername parameter may be added to all requests to the haproxy/couchdb by setting the environment variable ADD_SERVERNAME_TO_HTTP_AGENT to true.

      A similar change can be made for the http clients used in the application by setting PROXY_CHANGE_ORIGIN to true. This sets the changeOrigin parameter of all the http-proxy clients to true, which “changes the origin of the host header to the target URL”. See http-proxy: options.

      -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/hosting/4.x/app-developer/index.html b/hosting/4.x/app-developer/index.html index 40a280f640..923cb58bb6 100644 --- a/hosting/4.x/app-developer/index.html +++ b/hosting/4.x/app-developer/index.html @@ -1,9 +1,9 @@ -App Developer Hosting in CHT 4.x | Community Health Toolkit +App Developer Hosting in CHT 4.x | Community Health Toolkit

    App Developer Hosting in CHT 4.x

    Hosting the CHT when developing apps

    This guide assumes you are a CHT app developer wanting to either run concurrent instances of the CHT, or easily be able to switch between different instances without losing any data while doing so. To do development on the CHT Core Framework itself, see the development guide.

    To deploy the CHT 3.x in production, see either AWS hosting or Self hosting. 4.x production hosting guides are coming soon!

    Getting started

    Be sure to meet the CHT hosting requirements first. To avoid conflicts, ensure that all other CHT 4.x instances are stopped. To stop ALL containers, you can use

    docker kill $(docker ps -q)
    + Create project issue

    App Developer Hosting in CHT 4.x

    Hosting the CHT when developing apps

    This guide assumes you are a CHT app developer wanting to either run concurrent instances of the CHT, or easily be able to switch between different instances without losing any data while doing so. To do development on the CHT Core Framework itself, see the development guide.

    To deploy the CHT 3.x in production, see either AWS hosting or Self hosting. 4.x production hosting guides are coming soon!

    Getting started

    Be sure to meet the CHT hosting requirements first. To avoid conflicts, ensure that all other CHT 4.x instances are stopped. To stop ALL containers, you can use

    docker kill $(docker ps -q)
     

    After meeting these requirements, create a directory and download the developer YAML files in the directory you want to store them. This example uses ~/cht-4-app-developer as the directory:

    mkdir  ~/cht-4-app-developer && cd ~/cht-4-app-developer
     curl -s -o docker-compose.yml https://raw.githubusercontent.com/medic/cht-upgrade-service/main/docker-compose.yml
     curl -s -o cht-core.yml https://staging.dev.medicmobile.org/_couch/builds_4/medic%3Amedic%3Amaster/docker-compose/cht-core.yml
    @@ -388,7 +388,8 @@
     
     If just container name is shown above, a fresh local-ip.medicmobile.org certificate was downloaded fresh local-ip.medicmobile.org.
     

    File locations

    The bash script keeps files in two places:

    • *.env files - the same directory as the bash script.
    • ~/medic/cht-docker/ files - in your home directory, a sub-directory is created for each project. Within each project directory, a compose directory has the two compose files and the couch directory has the CouchDB datafiles.

    While you can manually remove any of these, it’s best to use the destroy command above to ensure all related data files are deleted too.

    Video

    Here is a video of the helper being run on 1 Dec 2022. The video references lazydocker which is a great way to monitor and control your local docker environment:


    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/hosting/4.x/backups/index.html b/hosting/4.x/backups/index.html index bf45dfe7f6..4e5769e308 100644 --- a/hosting/4.x/backups/index.html +++ b/hosting/4.x/backups/index.html @@ -1,9 +1,9 @@ -Backups in CHT 4.x | Community Health Toolkit +Backups in CHT 4.x | Community Health Toolkit

    Backups in CHT 4.x

    Which data to backup when hosting the CHT 4.x

    This guide is about backups in CHT 4.x - there’s the self hosted guide for 3.x which includes backups for 3.x.

    Introduction

    As CHT 4.x uses a container per service, the only data that needs to be backed up is:

    • CouchDB database
    • Docker Compose and .env files
    • TLS certificates

    This is because Docker containers are inherently stateless so all the important binaries are already stored in CHT’s Docker images. Docker Compose files, including the .env file, store all of your deployment’s configuration. Finally, the TLS certificates should be backed up to reduce recovery time.

    How to backup each of these three pieces of data is covered below.

    Therefore, you do not need to back up the docker images for:

    • nginx
    • sentinel
    • api
    • haproxy
    • couchdb
    • healthcheck
    • upgrade-service

    Assumptions

    This guide assumes you have an Ubuntu server running CHT 4.x in Docker as described in our Self Hosting in CHT 4.x - Single CouchDB Node guide. If you run docker ps --format '{{.Names}}' you should see something like this:

    cht_nginx_1
    + Create project issue

    Backups in CHT 4.x

    Which data to backup when hosting the CHT 4.x

    This guide is about backups in CHT 4.x - there’s the self hosted guide for 3.x which includes backups for 3.x.

    Introduction

    As CHT 4.x uses a container per service, the only data that needs to be backed up is:

    • CouchDB database
    • Docker Compose and .env files
    • TLS certificates

    This is because Docker containers are inherently stateless so all the important binaries are already stored in CHT’s Docker images. Docker Compose files, including the .env file, store all of your deployment’s configuration. Finally, the TLS certificates should be backed up to reduce recovery time.

    How to backup each of these three pieces of data is covered below.

    Therefore, you do not need to back up the docker images for:

    • nginx
    • sentinel
    • api
    • haproxy
    • couchdb
    • healthcheck
    • upgrade-service

    Assumptions

    This guide assumes you have an Ubuntu server running CHT 4.x in Docker as described in our Self Hosting in CHT 4.x - Single CouchDB Node guide. If you run docker ps --format '{{.Names}}' you should see something like this:

    cht_nginx_1
     cht_sentinel_1
     cht_api_1
     cht_haproxy_1
    @@ -313,7 +313,8 @@
     

    Note - In the volumes listed above, there is no volume for CouchDB data. This is because the compose file declares this as a bind mount. Bind mounts use the host file system directly and do not show up in docker volume ls calls. It’s therefore assumed your CouchDB data location is declared in /home/ubuntu/cht/upgrade-service/.env which sets it with COUCHDB_DATA=/home/ubuntu/cht/couchdb.

    You should have SSH access to the server with root access.

    Backup software

    It’s assumed you are using which ever tool you’re familiar with which might include rsync, borg, duplicity or other solution. The locations of the backups should follow the 3-2-1 rule:

    There should be at least 3 copies of the data, stored on 2 different types of storage media, and one copy should be kept offsite, in a remote location. - Wikipedia

    Duplicity has the handy benefit of offering built in encryption using GPG. Consider using this if you don’t have an existing solution for encrypted backups.

    CouchDB

    Assuming your CouchDB is stored in /home/ubuntu/cht/couchdb, you should use these steps to back it up:

    1. While you don’t need to stop CouchDB to back it up, ensure you follow best practices to back it up. See the CouchDB site for more info. Note that Medic recommends NOT using replication for backup.
    2. It is strongly recommended you encrypt your backups given the sensitivity of the contents. Do this now before copying the backup files to their long term location.
    3. Backup the CouchDB files using the software specified above

    Docker Compose files

    All compose files, and the corresponding .env file, are in these three locations:

    • /home/ubuntu/cht/compose/*.yml
    • /home/ubuntu/cht/upgrade-service/*.yml
    • /home/ubuntu/cht/upgrade-service/.env

    While all three of these are trivial to recreate by downloading them again, they may change over time so should be archived with your CouchDB data. Further, when there’s been a critical failure of a production CHT instance, you want to be sure to make the restore process as speedy as possible.

    As all of these files are only read when Docker first loads a service, you can simply copy them whenever you want without stopping any of the CHT services. They should be copied with the same frequency and put in the same location as the CouchDB data using the backup software specified above.

    TLS certificates

    Like the compose files, the TLS certificate files can easily be regenerated or re-downloaded from your Certificate Authority, like Let’s Encrypt for example. However, you want to have a backup of the at the ready to ease the restore process.

    1. Copy the cert and key files from the nginx container:

      docker cp cht_nginx_1:/etc/nginx/private/key.pem .
       docker cp cht_nginx_1:/etc/nginx/private/cert.pem .
       
    2. Back the up to the same location and frequency as the CouchDB data using the backup software specified above.

    Testing backups

    A backup that isn’t tested, is effectively not a backup. For a backup to be successful, a complete restore from all locations in the 3-2-1 plan need to be fully tested and documented as to how a restore works. The more practiced and better documented the restore process, the less downtime a production CHT instance will have after data loss.

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/hosting/4.x/data-migration/index.html b/hosting/4.x/data-migration/index.html index 88d7fa6d9f..e0b80d70c0 100644 --- a/hosting/4.x/data-migration/index.html +++ b/hosting/4.x/data-migration/index.html @@ -1,9 +1,9 @@ -Migration from CHT 3.x to CHT 4.x | Community Health Toolkit +Migration from CHT 3.x to CHT 4.x | Community Health Toolkit

    Migration from CHT 3.x to CHT 4.x

    Guide to migrate existent data from CHT 3.x to CHT 4.x

    The hosting architecture differs entirely between CHT-Core 3.x and CHT-Core 4.x. Migrating data from an existing instance running CHT 3.x requires a few manual steps. + Create project issue

    Migration from CHT 3.x to CHT 4.x

    Guide to migrate existent data from CHT 3.x to CHT 4.x

    The hosting architecture differs entirely between CHT-Core 3.x and CHT-Core 4.x. Migrating data from an existing instance running CHT 3.x requires a few manual steps. This guide will present the required steps while using a migration helping tool, called couchdb-migration. This tool interfaces with CouchDb, to update shard maps and database metadata. By the end of this guide, your CHT-Core 3.x CouchDb will be down and CHT-Core 4.x ready to be used. Using this tool is not required, and the same result can be achieved by calling CouchDb endpoints directly. Consult CouchDB documentation for details about moving shards.

    1. Install CHT data migration tool

    Open your terminal and run these commands. They will create a new directory, download a docker compose file and download the required docker image.

    mkdir -p ~/couchdb-migration/
    @@ -434,7 +434,8 @@
     

    From this list, you should find the names for :

    • containers that ran CHT-Core 3.x, likely named medic-os and haproxy.
    • temporary single node CouchDb containers, likely named couchdb-single-couchdb-1
    • temporary clustered CouchDb containers, likely named couchdb-cluster-couchdb-2.local-1, couchdb-cluster-couchdb-1.local-1 and couchdb-cluster-couchdb-3.local-1

    To remove containers, run these commands:

    docker stop <container> <container> <container>
     docker rm <container> <container> <container>
     
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/hosting/4.x/index.html b/hosting/4.x/index.html index c442b3fb30..c3e676a668 100644 --- a/hosting/4.x/index.html +++ b/hosting/4.x/index.html @@ -1,9 +1,9 @@ -4.x | Community Health Toolkit +4.x | Community Health Toolkit

    4.x

    Guides for hosting CHT 4.x applications

    Before beginning any of these guides, be sure to meet all of the CHT hosting requirements first.

    To host a production instance of CHT, use the Self Hosting in CHT 4.x guide. To do app development, see our App Developer hosting guide.

    To view 3.x hosting options, see the 3.x hosting section


    Migration from CHT 3.x to CHT 4.x

    Guide to migrate existent data from CHT 3.x to CHT 4.x

    Self Hosting in CHT 4.x

    Details for hosting the CHT on self run infrastructure

    App Developer Hosting in CHT 4.x

    Hosting the CHT when developing apps

    Adding TLS certificates in CHT 4.x

    How to add TLS certificates to your docker hosted CHT 4.x instance

    Viewing server logs in CHT 4.x

    What to do when you need to find server side errors in CHT 4.x

    Backups in CHT 4.x

    Which data to backup when hosting the CHT 4.x


    CHT Applications > + Create project issue

    4.x

    Guides for hosting CHT 4.x applications

    Before beginning any of these guides, be sure to meet all of the CHT hosting requirements first.

    To host a production instance of CHT, use the Self Hosting in CHT 4.x guide. To do app development, see our App Developer hosting guide.

    To view 3.x hosting options, see the 3.x hosting section


    Migration from CHT 3.x to CHT 4.x

    Guide to migrate existent data from CHT 3.x to CHT 4.x

    Self Hosting in CHT 4.x

    Details for hosting the CHT on self run infrastructure

    App Developer Hosting in CHT 4.x

    Hosting the CHT when developing apps

    Adding TLS certificates in CHT 4.x

    How to add TLS certificates to your docker hosted CHT 4.x instance

    Viewing server logs in CHT 4.x

    What to do when you need to find server side errors in CHT 4.x

    Backups in CHT 4.x

    Which data to backup when hosting the CHT 4.x


    CHT Applications > Quick Guides > Updates > Preparing to upgrade to CHT 4.0

    Steps to ensure your CHT App will run smoothly on CHT 4.0 and later

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/hosting/4.x/index.xml b/hosting/4.x/index.xml index 1802ba76c9..3d2afdacdf 100644 --- a/hosting/4.x/index.xml +++ b/hosting/4.x/index.xml @@ -1,709 +1,10 @@ -Community Health Toolkit – 4.xhttps://docs.communityhealthtoolkit.org/hosting/4.x/Recent content in 4.x on Community Health ToolkitHugo -- gohugo.ioenHosting: Migration from CHT 3.x to CHT 4.xhttps://docs.communityhealthtoolkit.org/hosting/4.x/data-migration/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/data-migration/ -<p>The hosting architecture differs entirely between CHT-Core 3.x and CHT-Core 4.x. Migrating data from an existing instance running CHT 3.x requires a few manual steps. -This guide will present the required steps while using a migration helping tool, called <code>couchdb-migration</code>. This tool interfaces with CouchDb, to update shard maps and database metadata. -By the end of this guide, your CHT-Core 3.x CouchDb will be down and CHT-Core 4.x ready to be used. -Using this tool is not required, and the same result can be achieved by calling CouchDb endpoints directly. <a href="https://docs.couchdb.org/en/stable/cluster/sharding.html#moving-a-shard">Consult CouchDB documentation for details about moving shards</a>.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -If after upgrading you get an error, <code>Cannot convert undefined or null to object</code> - please see <a href="https://github.com/medic/cht-core/issues/8040">issue #8040</a> for a work around. This only affects CHT 4.0.0, 4.0.1, 4.1.0 and 4.1.1. It was fixed in CHT 4.2.0. -</div> -<h3 id="1-install-cht-data-migration-tool">1. Install CHT data migration tool</h3> -<p>Open your terminal and run these commands. They will create a new directory, download a docker compose file and download the required docker image.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>mkdir -p ~/couchdb-migration/ -</span></span><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/couchdb-migration/ -</span></span><span style="display:flex;"><span>curl -s -o ./docker-compose.yml https://raw.githubusercontent.com/medic/couchdb-migration/main/docker-compose.yml -</span></span><span style="display:flex;"><span>docker-compose up -</span></span></code></pre></div><p>For the following steps, the tool needs access to your CouchDb installation. To allow this access, you will need to provide a URL to your CouchDB installation that includes authentication. -If your installation exposes a different port for CouchDb cluster API endpoints, please export that port. -If running against an installation of <code>MedicOS</code>, please make sure that the protocol of the URL is <code>https</code>.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">export</span> <span style="color:#000">COUCH_URL</span><span style="color:#ce5c00;font-weight:bold">=</span>http<span style="color:#ce5c00;font-weight:bold">(</span>s<span style="color:#ce5c00;font-weight:bold">)</span>://&lt;authentication&gt;@&lt;host-ip&gt;:&lt;port&gt; -</span></span></code></pre></div><p>For simplicity, you could store these required values in an <code>.env</code> file:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>cat &gt; <span style="color:#4e9a06">${</span><span style="color:#000">HOME</span><span style="color:#4e9a06">}</span>/couchdb-migration/.env <span style="color:#4e9a06">&lt;&lt; EOF -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">COUCH_URL=http(s)://&lt;authentication&gt;@&lt;host-ip&gt;:&lt;port&gt; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">EOF</span> -</span></span></code></pre></div><h3 id="2-prepare-cht-core-3x-installation-for-upgrading">2. Prepare CHT-Core 3.x installation for upgrading</h3> -<p>Backup your data! If you encounter any problems executing the instructions of this guide, you should be able to restore your CHT 3X instance using the backup data. -<a href="https://docs.communityhealthtoolkit.org/hosting/3.x/self-hosting/#backup">Consult information about backups for details</a>. -Ensure no changes happen to your CouchDB data in your CHT 3.x server after you have begun the migration process.</p> -<p>To minimize downtime when upgrading, it&rsquo;s advised to prepare the 3.x installation for the 4.x upgrade, and pre-index all views that are required by 4.x.</p> -<p>The migration tool provides a command which will download all 4.x views to your 3.x CouchDb, and initiate view indexing. <code>&lt;desired CHT version&gt;</code> is any version at or above <code>4.0.0</code>:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/couchdb-migration/ -</span></span><span style="display:flex;"><span>docker-compose run couch-migration pre-index-views &lt;desired CHT version&gt; -</span></span></code></pre></div><p>Once view indexing is finished, proceed with the next step.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -If this step is omitted, 4.x API will fail to respond to requests until all views are indexed. Depending on the size of the database, this could take many hours, or even days. -</div> -<h3 id="3-save-existent-couchdb-configuration">3. Save existent CouchDb configuration</h3> -<p>Some CouchDb configuration values must be ported from existent CouchDb to the 4.x installation. Store them in a safe location before shutting down 3.x CouchDb. -Use the migration tool to obtain these values:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/couchdb-migration/ -</span></span><span style="display:flex;"><span>docker-compose run couch-migration get-env -</span></span></code></pre></div><h5 id="a-couchdb-secret">a) CouchDB secret</h5> -<p>Used in encrypting all CouchDb passwords and session tokens.</p> -<h5 id="b-couchdb-server-uuid">b) CouchDb server uuid</h5> -<p>Used in generating replication checkpointer documents, which track where replication progress between every client and the server, and ensure that clients don&rsquo;t re-download or re-upload documents.</p> -<h3 id="4-locate-and-make-a-copy-of-your-couchdb-data-folder">4. Locate and make a copy of your CouchDb Data folder</h3> -<p>a) If running in MedicOS, <a href="https://docs.communityhealthtoolkit.org/hosting/3.x/self-hosting/#backup">CouchDb data folder</a> can be found at <code>/srv/storage/medic-core/couchdb/data</code>.</p> -<p>b) If running a custom installation of CouchDb, data would be typically stored at <code>/opt/couchdb/data</code>.</p> -<h3 id="5-stop-your-3x-couchdb--cht-core-installation-and-launch-4x-couchdb-installation">5. Stop your 3.x CouchDb / CHT-Core installation and launch 4.x CouchDb installation</h3> -<p>Depending on your project scalability needs and technical possibilities, you must decide whether you will deploy CouchDb in a single node or in a cluster with multiple nodes. -Please consult this guide about clustering and horizontal scalability to make an informed decision. <insert link></p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -You can start with single node and then change to a cluster. This involves running the migration tool again to distribute shards from the existent node to the new nodes. -</div> -<p>Depending on your choice, follow the instructions that match your deployment below:</p> -<h4 id="single-node">Single node</h4> -<p>a) Download 4.x single-node CouchDb docker-compose file:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>mkdir -p ~/couchdb-single/ -</span></span><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/couchdb-single/ -</span></span><span style="display:flex;"><span>curl -s -o ./docker-compose.yml https://staging.dev.medicmobile.org/_couch/builds_4/medic:medic:&lt;desired CHT version&gt;/docker-compose/cht-couchdb.yml -</span></span></code></pre></div><p>a) Make a copy of the 3.x CouchDb data folder from <strong>step 4</strong>.</p> -<p>b) Set the correct environment variables:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>cat &gt; <span style="color:#4e9a06">${</span><span style="color:#000">HOME</span><span style="color:#4e9a06">}</span>/couchdb-single/.env <span style="color:#4e9a06">&lt;&lt; EOF -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">COUCHDB_USER=&lt;admin&gt; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">COUCHDB_PASSWORD=&lt;password&gt; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">COUCHDB_SECRET=&lt;COUCHDB_SECRET from step 3&gt; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">COUCHDB_UUID=&lt;COUCHDB_UUID from step 3&gt; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">COUCHDB_DATA=&lt;absolute path to folder created in step 5.a&gt; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">EOF</span> -</span></span></code></pre></div><p>c) Start 4.x CouchDb.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/couchdb-single/ -</span></span><span style="display:flex;"><span>docker-compose up -d -</span></span></code></pre></div><p>d) Update <code>couchdb-migration</code> environment variables. Depending on your setup, it&rsquo;s possible you will need to update <code>CHT_NETWORK</code> and <code>COUCH_URL</code> to match the newly started 4.x CouchDb. -From this point on, the <code>couchdb-migration</code> container should connect to the same docker network as your CouchDb installation, in order to access APIs that are only available on protected ports. Correctly setting <code>CHT_NETWORK</code> is required for the next steps to succeed. -To get the correct <code>docker-network-name</code> and <code>docker-service-name</code>, you can use <code>docker network ls</code> to list all networks and <code>docker network inspect &lt;docker-network-name&gt;</code> to get the name of the CouchDb container that exists in this network.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>cat &gt; <span style="color:#4e9a06">${</span><span style="color:#000">HOME</span><span style="color:#4e9a06">}</span>/couchdb-migration/.env <span style="color:#4e9a06">&lt;&lt; EOF -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">CHT_NETWORK=&lt;docker-network-name&gt; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">COUCH_URL=http://&lt;authentication&gt;@&lt;docker-container-name&gt;:&lt;port&gt; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">EOF</span> -</span></span></code></pre></div><p>e) Check that <code>couchdb-migration</code> can connect to the CouchDb instance and that CouchDb is running. You&rsquo;ll know it is working when the <code>docker-compose</code> call exits without errors and logs <code>CouchDb is Ready</code>.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/couchdb-migration/ -</span></span><span style="display:flex;"><span>docker-compose run couch-migration check-couchdb-up -</span></span></code></pre></div><p>f) Change metadata to match the new CouchDb node</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/couchdb-migration/ -</span></span><span style="display:flex;"><span>docker-compose run couch-migration move-node -</span></span></code></pre></div><p>g) Run the <code>verify</code> command to check whether the migration was successful.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>docker-compose run couch-migration verify -</span></span></code></pre></div><p>If all checks pass, you should see a message <code>Migration verification passed</code>. It is then safe to proceed with starting CHT-Core 4.x, using the same environment variables you saved in <code>~/couchdb-single/.env</code>.</p> -<p>h) <a href="#6-cleanup">Remove unnecessary containers</a>.</p> -<h4 id="multi-node">Multi node</h4> -<p>a) Download 4.x clustered CouchDb docker-compose file:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>mkdir -p ~/couchdb-cluster/ -</span></span><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/couchdb-cluster/ -</span></span><span style="display:flex;"><span>curl -s -o ./docker-compose.yml https://staging.dev.medicmobile.org/_couch/builds_4/medic:medic:&lt;desired CHT version&gt;/docker-compose/cht-couchdb-clustered.yml -</span></span></code></pre></div><p>b) Create a data folder for every one of the CouchDb nodes. -If you were going to a 3 cluster node, this would be:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>mkdir -p ~/couchdb-data/main -</span></span><span style="display:flex;"><span>mkdir -p ~/couchdb-data/secondary1 -</span></span><span style="display:flex;"><span>mkdir -p ~/couchdb-data/secondary2 -</span></span></code></pre></div><p>c) Copy the 3.x CouchDb data folder into <code>~/couchdb-data/main</code>, which will be your main CouchDb node. This main node will create your cluster and the other secondary nodes will be added to it. In <code>main</code>&rsquo;s environment variable file, define <code>CLUSTER_PEER_IPS</code>. In all other secondary nodes, declare the <code>COUCHDB_SYNC_ADMINS_NODE</code> variable instead.</p> -<p>d) Create a <code>shards</code> and a <code>.shards</code> directory in every secondary node folder.</p> -<p>e) Set the correct environment variables:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>cat &gt; <span style="color:#4e9a06">${</span><span style="color:#000">HOME</span><span style="color:#4e9a06">}</span>/couchdb-cluster/.env <span style="color:#4e9a06">&lt;&lt; EOF -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">COUCHDB_USER=&lt;admin&gt; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">COUCHDB_PASSWORD=&lt;password&gt; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">COUCHDB_SECRET=&lt;COUCHDB_SECRET from step 3&gt; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">COUCHDB_UUID=&lt;COUCHDB_UUID from step 3&gt; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">DB1_DATA=&lt;absolute path to main folder created in step 5.a&gt; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">DB2_DATA=&lt;absolute path to secondary1 folder created in step 5.a&gt; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">DB3_DATA=&lt;absolute path to secondary2 folder created in step 5.a&gt; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">EOF</span> -</span></span></code></pre></div><p>f) Start 4.x CouchDb.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/couchdb-cluster/ -</span></span><span style="display:flex;"><span>docker-compose up -d -</span></span></code></pre></div><p>g) Update <code>couchdb-migration</code> environment variables. Depending on your setup, it&rsquo;s possible you will need to update <code>CHT_NETWORK</code> and <code>COUCH_URL</code> to match the newly started 4.x CouchDb. -From this point on, the <code>couchdb-migration</code> container should connect to the same docker network as your CouchDb installation, in order to access APIs that are only available on protected ports. Correctly setting <code>CHT_NETWORK</code> is required for the next steps to succeed. -To get the correct <code>docker-network-name</code> and <code>docker-service-name</code>, you can use <code>docker network ls</code> to list all networks and <code>docker network inspect &lt;docker-network-name&gt;</code> to get the name of the CouchDb container that exists in this network.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>cat &gt; <span style="color:#4e9a06">${</span><span style="color:#000">HOME</span><span style="color:#4e9a06">}</span>/couchdb-migration/.env <span style="color:#4e9a06">&lt;&lt; EOF -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">CHT_NETWORK=&lt;docker-network-name&gt; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">COUCH_URL=http://&lt;authentication&gt;@&lt;docker-container-name&gt;:&lt;port&gt; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">EOF</span> -</span></span></code></pre></div><p>h) Check that <code>couchdb-migration</code> can connect to the CouchDb instance and that CouchDb is running. You&rsquo;ll know it is working when the <code>docker-compose</code> call exits without errors and logs <code>CouchDb Cluster is Ready</code>.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/couchdb-migration/ -</span></span><span style="display:flex;"><span>docker-compose run couch-migration check-couchdb-up &lt;number-of-nodes&gt; -</span></span></code></pre></div><p>i) Generate the shard distribution matrix and get instructions for final shard locations.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/couchdb-migration/ -</span></span><span style="display:flex;"><span><span style="color:#000">shard_matrix</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#204a87;font-weight:bold">$(</span>docker-compose run couch-migration generate-shard-distribution-matrix<span style="color:#204a87;font-weight:bold">)</span> -</span></span><span style="display:flex;"><span>docker-compose run couch-migration shard-move-instructions <span style="color:#000">$shard_matrix</span> -</span></span></code></pre></div><p>j) Follow the instructions from the step above and move the shard files to the correct location, according to the shard distribution matrix. This is a manual step that requires to physically move data around on disk.</p> -<p>Example of moving one shard from one node to another:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>/couchdb_data_main -</span></span><span style="display:flex;"><span> /.delete -</span></span><span style="display:flex;"><span> /_dbs.couch -</span></span><span style="display:flex;"><span> /_nodes.couch -</span></span><span style="display:flex;"><span> /_users.couch -</span></span><span style="display:flex;"><span> /.shards -</span></span><span style="display:flex;"><span> /00000000-15555554 -</span></span><span style="display:flex;"><span> /2aaaaaaa-3ffffffe -</span></span><span style="display:flex;"><span> /3fffffff-55555553 -</span></span><span style="display:flex;"><span> /6aaaaaa9-7ffffffd -</span></span><span style="display:flex;"><span> /7ffffffe-95555552 -</span></span><span style="display:flex;"><span> /15555555-2aaaaaa9 -</span></span><span style="display:flex;"><span> /55555554-6aaaaaa8 -</span></span><span style="display:flex;"><span> /95555553-aaaaaaa7 -</span></span><span style="display:flex;"><span> /shards -</span></span><span style="display:flex;"><span> /00000000-15555554 -</span></span><span style="display:flex;"><span> /2aaaaaaa-3ffffffe -</span></span><span style="display:flex;"><span> /3fffffff-55555553 -</span></span><span style="display:flex;"><span> /6aaaaaa9-7ffffffd -</span></span><span style="display:flex;"><span> /7ffffffe-95555552 -</span></span><span style="display:flex;"><span> /15555555-2aaaaaa9 -</span></span><span style="display:flex;"><span> /55555554-6aaaaaa8 -</span></span><span style="display:flex;"><span> /95555553-aaaaaaa7 -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span>/couchdb_data_secondary -</span></span><span style="display:flex;"><span> /.shards -</span></span><span style="display:flex;"><span> /shards -</span></span></code></pre></div><p>After moving two shards: <code>55555554-6aaaaaa8</code> and <code>6aaaaaa9-7ffffffd</code></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>/couchdb_data_main -</span></span><span style="display:flex;"><span> /.delete -</span></span><span style="display:flex;"><span> /_dbs.couch -</span></span><span style="display:flex;"><span> /_nodes.couch -</span></span><span style="display:flex;"><span> /_users.couch -</span></span><span style="display:flex;"><span> /.shards -</span></span><span style="display:flex;"><span> /00000000-15555554 -</span></span><span style="display:flex;"><span> /2aaaaaaa-3ffffffe -</span></span><span style="display:flex;"><span> /3fffffff-55555553 -</span></span><span style="display:flex;"><span> /7ffffffe-95555552 -</span></span><span style="display:flex;"><span> /15555555-2aaaaaa9 -</span></span><span style="display:flex;"><span> /95555553-aaaaaaa7 -</span></span><span style="display:flex;"><span> /shards -</span></span><span style="display:flex;"><span> /00000000-15555554 -</span></span><span style="display:flex;"><span> /2aaaaaaa-3ffffffe -</span></span><span style="display:flex;"><span> /3fffffff-55555553 -</span></span><span style="display:flex;"><span> /7ffffffe-95555552 -</span></span><span style="display:flex;"><span> /15555555-2aaaaaa9 -</span></span><span style="display:flex;"><span> /95555553-aaaaaaa7 -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span>/couchdb_data_secondary -</span></span><span style="display:flex;"><span> /.shards -</span></span><span style="display:flex;"><span> /6aaaaaa9-7ffffffd -</span></span><span style="display:flex;"><span> /55555554-6aaaaaa8 -</span></span><span style="display:flex;"><span> /shards -</span></span><span style="display:flex;"><span> /6aaaaaa9-7ffffffd -</span></span><span style="display:flex;"><span> /55555554-6aaaaaa8 -</span></span></code></pre></div><p>k) Change metadata to match the new shard distribution. We declared <code>$shard_matrix</code> in step &ldquo;g&rdquo; above, so it is still set now:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>docker-compose run couch-migration move-shards <span style="color:#000">$shard_matrix</span> -</span></span></code></pre></div><p>l) Remove old node from the cluster:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>docker-compose run couch-migration remove-node couchdb@127.0.0.1 -</span></span></code></pre></div><p>j) Run the <code>verify</code> command to check whether the migration was successful.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>docker-compose run couch-migration verify -</span></span></code></pre></div><p>If all checks pass, you should see a message <code>Migration verification passed</code>. It is then safe to proceed with starting CHT-Core 4.x, using the same environment variables you saved in <code>~/couchdb-cluster/.env</code>.</p> -<p>k) <a href="#6-cleanup">Remove unnecessary containers</a>.</p> -<h3 id="6-cleanup">6. Cleanup</h3> -<p>It&rsquo;s very important to remove temporary containers that were used during migration and containers that deployed the previous CHT-Core 3.x installation. Only follow this step after making sure your CHT-Core 4.x installation is ready and can be used.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Warning</h4> -Even if these containers are stopped, depending on their configuration, they could restart when the Docker engine is restarted, for example on system reboot. -</div> -<h5 id="risks-for-not-removing-containers-include">Risks for not removing containers include</h5> -<ul> -<li>resource contention - where your new CHT installation might not have access to certain resources (for example network ports) because they are already used.</li> -<li>data corruption - when multiple CouchDb installations are accessing the same data source.</li> -<li>data loss - when multiple CouchDb installation are exposing on the same port.</li> -</ul> -<p>To get a list of all containers run:</p> -<pre tabindex="0"><code>docker ps -a -</code></pre><p>From this list, you should find the names for :</p> -<ul> -<li>containers that ran CHT-Core 3.x, likely named <code>medic-os</code> and <code>haproxy</code>.</li> -<li>temporary single node CouchDb containers, likely named <code>couchdb-single-couchdb-1</code></li> -<li>temporary clustered CouchDb containers, likely named <code>couchdb-cluster-couchdb-2.local-1</code>, <code>couchdb-cluster-couchdb-1.local-1</code> and <code>couchdb-cluster-couchdb-3.local-1</code></li> -</ul> -<p>To remove containers, run these commands:</p> -<pre tabindex="0"><code>docker stop &lt;container&gt; &lt;container&gt; &lt;container&gt; -docker rm &lt;container&gt; &lt;container&gt; &lt;container&gt; -</code></pre>Hosting: Self Hosting in CHT 4.xhttps://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/ -<h1 id="recommendations-and-considerations">Recommendations and considerations</h1> -<h2 id="multi-vs-single-node-couchdb-requirements">Multi vs Single node couchdb requirements</h2> -<p>For smaller deployments a <a href="https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/single-node/">single node CouchDB</a> instance can be used, for larger deployments a <a href="https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/multiple-nodes/">multi-node CouchDB</a> cluster is generally recommended</p> -<table> -<thead> -<tr> -<th>Consideration</th> -<th><a href="https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/single-node/">Single node CouchDB</a></th> -<th><a href="https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/multiple-nodes/">Multi-node clustered CouchDB</a></th> -</tr> -</thead> -<tbody> -<tr> -<td>Less than -4 000 users</td> -<td><span title="Yes">✔</span></td> -<td><span title="Yes">✔</span></td> -</tr> -<tr> -<td>More than -4 000 users</td> -<td><span title="No">❌</span></td> -<td><span title="Yes">✔</span></td> -</tr> -<tr> -<td>Less than -10 000 documents per day</td> -<td><span title="Yes">✔</span></td> -<td><span title="Yes">✔</span></td> -</tr> -<tr> -<td>More than -10 000 documents per day</td> -<td><span title="No">❌</span></td> -<td><span title="Yes">✔</span></td> -</tr> -<tr> -<td>Seamless upgrade with multi-node docker compose</td> -<td><span title="Yes">✔</span></td> -<td><span title="No">❌</span></td> -</tr> -<tr> -<td>Seamless upgrade with multi-node kubernetes/k3s</td> -<td><span title="Yes">✔</span></td> -<td><span title="Yes">✔</span></td> -</tr> -</tbody> -</table> -<h2 id="cloud-provider-vs-bare-metal">Cloud provider vs Bare metal</h2> -<table> -<thead> -<tr> -<th>Consideration</th> -<th>Cloud provider</th> -<th>Bare Metal</th> -</tr> -</thead> -<tbody> -<tr> -<td>Data needs to be in-country</td> -<td><span title="No">❌</span></td> -<td><span title="Yes">✔</span></td> -</tr> -</tbody> -</table>Hosting: App Developer Hosting in CHT 4.xhttps://docs.communityhealthtoolkit.org/hosting/4.x/app-developer/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/app-developer/ -<div class="pageinfo pageinfo-primary"> -<p>This guide assumes you are a CHT app developer wanting to either run concurrent instances of the CHT, or easily be able to switch between different instances without losing any data while doing so. To do development on the CHT Core Framework itself, see the <a href="https://docs.communityhealthtoolkit.org/contribute/code/core/dev-environment/">development guide</a>.</p> -<p>To deploy the CHT 3.x in production, see either <a href="https://docs.communityhealthtoolkit.org/hosting/3.x/ec2-setup-guide/">AWS hosting</a> or <a href="https://docs.communityhealthtoolkit.org/hosting/3.x/self-hosting/">Self hosting</a>. 4.x production hosting guides are coming soon!</p> -</div> -<h2 id="getting-started">Getting started</h2> -<p>Be sure to meet the <a href="https://docs.communityhealthtoolkit.org/hosting/requirements/">CHT hosting requirements</a> first. To avoid conflicts, ensure that all other CHT 4.x instances are stopped. To stop ALL containers, you can use</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>docker <span style="color:#204a87">kill</span> <span style="color:#204a87;font-weight:bold">$(</span>docker ps -q<span style="color:#204a87;font-weight:bold">)</span> -</span></span></code></pre></div><p>After meeting these requirements, create a directory and download the developer YAML files in the directory you want to store them. This example uses <code>~/cht-4-app-developer</code> as the directory:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>mkdir ~/cht-4-app-developer <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#204a87">cd</span> ~/cht-4-app-developer -</span></span><span style="display:flex;"><span>curl -s -o docker-compose.yml https://raw.githubusercontent.com/medic/cht-upgrade-service/main/docker-compose.yml -</span></span><span style="display:flex;"><span>curl -s -o cht-core.yml https://staging.dev.medicmobile.org/_couch/builds_4/medic%3Amedic%3Amaster/docker-compose/cht-core.yml -</span></span><span style="display:flex;"><span>curl -s -o cht-couchdb.yml https://staging.dev.medicmobile.org/_couch/builds_4/medic%3Amedic%3Amaster/docker-compose/cht-couchdb.yml -</span></span></code></pre></div><p>You should now have 3 compose files which we can check with <code>ls</code>:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>ls -</span></span><span style="display:flex;"><span>cht-core.yml cht-couchdb.yml docker-compose.yml -</span></span></code></pre></div><p>To start the first developer CHT instance, run <code>docker-compose</code>, prepending the needed environment variables:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#000">CHT_COMPOSE_PROJECT_NAME</span><span style="color:#ce5c00;font-weight:bold">=</span>app-devl <span style="color:#000">COUCHDB_SECRET</span><span style="color:#ce5c00;font-weight:bold">=</span>foo <span style="color:#000">DOCKER_CONFIG_PATH</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">${</span><span style="color:#000">PWD</span><span style="color:#4e9a06">}</span> <span style="color:#000">COUCHDB_DATA</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">${</span><span style="color:#000">PWD</span><span style="color:#4e9a06">}</span>/couchd <span style="color:#000">CHT_COMPOSE_PATH</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">${</span><span style="color:#000">PWD</span><span style="color:#4e9a06">}</span> <span style="color:#000">COUCHDB_USER</span><span style="color:#ce5c00;font-weight:bold">=</span>medic <span style="color:#000">COUCHDB_PASSWORD</span><span style="color:#ce5c00;font-weight:bold">=</span>password docker-compose up -</span></span></code></pre></div><p>This may take some minutes to fully start depending on the speed of the internet connection and speed of the host. This is because docker needs to download all the storage layers for all the containers and the CHT needs to run the first run set up. After downloads and setup has completed, the CHT should be accessible on <a href="https://localhost">https://localhost</a>. You can log in with username <code>medic</code> and password <code>password</code>.</p> -<p>When connecting to a new dev CHT instance for the first time, an error will be shown, &ldquo;Your connection is not private&rdquo; with <code>NET::ERR_CERT_AUTHORITY_INVALID</code> (see <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/privacy.error.png">screenshot</a>). To get past this, click &ldquo;Advanced&rdquo; and then click &ldquo;Proceed to localhost&rdquo;.</p> -<h2 id="running-the-nth-cht-instance">Running the Nth CHT instance</h2> -<p>After running the first instance of the CHT, it&rsquo;s easy to run as many more as are needed. This is achieved by specifying different:</p> -<ul> -<li>port for <code>HTTP</code> redirects (<code>CHT_HTTP</code>)</li> -<li>port for <code>HTTPS</code> traffic (<code>NGINX_HTTP_PORT</code>)</li> -<li>directory for storing the compose files and CouchDB files</li> -</ul> -<p>Assuming you want to start a new project called <code>the_second</code> and start the instance on <code>HTTP</code> port <code>8081</code> and <code>HTTPS</code> port <code>8443</code>, we would first create a new directory and download the same files:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>mkdir ~/the_second <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> <span style="color:#204a87">cd</span> ~/the_second -</span></span><span style="display:flex;"><span>curl -s -o docker-compose.yml https://raw.githubusercontent.com/medic/cht-upgrade-service/main/docker-compose.yml -</span></span><span style="display:flex;"><span>curl -s -o cht-core.yml https://staging.dev.medicmobile.org/_couch/builds_4/medic%3Amedic%3Amaster/docker-compose/cht-core.yml -</span></span><span style="display:flex;"><span>curl -s -o cht-couchdb.yml https://staging.dev.medicmobile.org/_couch/builds_4/medic%3Amedic%3Amaster/docker-compose/cht-couchdb.yml -</span></span></code></pre></div><p>Then, we would use the same <code>docker-compose</code> command as above, but this time specify the ports:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#000">NGINX_HTTP_PORT</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#0000cf;font-weight:bold">8081</span> <span style="color:#000">NGINX_HTTPS_PORT</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#0000cf;font-weight:bold">8444</span> <span style="color:#000">CHT_COMPOSE_PROJECT_NAME</span><span style="color:#ce5c00;font-weight:bold">=</span>app-devl <span style="color:#000">COUCHDB_SECRET</span><span style="color:#ce5c00;font-weight:bold">=</span>foo <span style="color:#000">DOCKER_CONFIG_PATH</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">${</span><span style="color:#000">PWD</span><span style="color:#4e9a06">}</span> <span style="color:#000">COUCHDB_DATA</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">${</span><span style="color:#000">PWD</span><span style="color:#4e9a06">}</span>/couchd <span style="color:#000">CHT_COMPOSE_PATH</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">${</span><span style="color:#000">PWD</span><span style="color:#4e9a06">}</span> <span style="color:#000">COUCHDB_USER</span><span style="color:#ce5c00;font-weight:bold">=</span>medic <span style="color:#000">COUCHDB_PASSWORD</span><span style="color:#ce5c00;font-weight:bold">=</span>password docker-compose up -</span></span></code></pre></div><p>The second instance is now accessible at <a href="https://localhost:8444">https://localhost:8444</a> and again using username <code>medic</code> and password <code>password</code> to login.</p> -<h2 id="the-env-file">The <code>.env</code> file</h2> -<p>Often times it&rsquo;s convenient to use revision control, like GitHub, to store and publish changes in a CHT app. A nice compliment to this is to store the specifics on how to run the <code>docker-compose</code> command for each app. By using a shared <code>docker-compose</code> configuration for all developers on the same app, it avoids any port collisions and enables all developers to have a unified configuration.</p> -<p>Using the above <code>the_second</code> sample project, we can create a file <code>~/the_second/.env</code> with this contents:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#000">NGINX_HTTP_PORT</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#0000cf;font-weight:bold">8081</span> -</span></span><span style="display:flex;"><span><span style="color:#000">NGINX_HTTPS_PORT</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#0000cf;font-weight:bold">8444</span> -</span></span><span style="display:flex;"><span><span style="color:#000">CHT_COMPOSE_PROJECT_NAME</span><span style="color:#ce5c00;font-weight:bold">=</span>second -</span></span><span style="display:flex;"><span><span style="color:#000">COUCHDB_SECRET</span><span style="color:#ce5c00;font-weight:bold">=</span>foo -</span></span><span style="display:flex;"><span><span style="color:#000">DOCKER_CONFIG_PATH</span><span style="color:#ce5c00;font-weight:bold">=</span>./ -</span></span><span style="display:flex;"><span><span style="color:#000">COUCHDB_DATA</span><span style="color:#ce5c00;font-weight:bold">=</span>./couchd -</span></span><span style="display:flex;"><span><span style="color:#000">CHT_COMPOSE_PATH</span><span style="color:#ce5c00;font-weight:bold">=</span>./ -</span></span><span style="display:flex;"><span><span style="color:#000">COUCHDB_USER</span><span style="color:#ce5c00;font-weight:bold">=</span>medic -</span></span><span style="display:flex;"><span><span style="color:#000">COUCHDB_PASSWORD</span><span style="color:#ce5c00;font-weight:bold">=</span>password -</span></span></code></pre></div><p>Now it&rsquo;s easy to boot this environment:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/the_second -</span></span><span style="display:flex;"><span>docker-compose up -</span></span></code></pre></div><h2 id="switching--concurrent-projects">Switching &amp; concurrent projects</h2> -<p>The easiest way to switch between projects is to stop the first set of containers and start the second set. Cancel the first project running in the foreground with <code>ctrl + c</code> and <code>stop</code> all the project&rsquo;s services:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>docker stop second_api_1 second_cht-upgrade-service_1 second_couchdb_1 second_haproxy_1 second_healthcheck_1 second_nginx_1 second_sentinel_1 -</span></span></code></pre></div><p>Alternately, you can stop ALL containers (even non-CHT ones!) with <code>docker kill $(docker ps -q)</code>. Then start the other CHT project using either the <code>.env</code> file or use the explicit command with ports and other environment variables as shown above.</p> -<p>To run projects concurrently open a second terminal and start the second project so you don&rsquo;t have to cancel and <code>stop</code> the first project. Remember to avoid port conflicts!</p> -<h2 id="cht-docker-helper-for-4x">CHT Docker Helper for 4.x</h2> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -This is for CHT 4.x. To use a CHT 3.x version, see the earlier <a href="https://docs.communityhealthtoolkit.org/hosting/3.x/app-developer/#cht-docker-helper">CHT Docker Helper page</a> -</div> -<p>The <code>cht-docker-compose.sh</code> scripts downloads 3 compose files and builds an <code>.env</code> file used above. This greatly eases starting your first CHT instance with a simple text based GUI which works on Windows (WSL2), macOS (both x86 and Apple Silicon) and Linux.</p> -<p><img src="cht-docker-helper.png" alt="The cht-docker-compose.sh script showing the URL and version of the CHT instance as well as number of containers launched, global container count, medic images downloaded count and OS load average. Finally a &ldquo;Successfully started my_first_project&rdquo; message is shown and denotes the login is &ldquo;medic&rdquo; and the password is &ldquo;password&rdquo;."></p> -<p>This script brings a lot of benefits with it:</p> -<ul> -<li>You only have to download one bash script</li> -<li>All compose files and images will be downloaded automatically for you</li> -<li>All networks, storage volumes and containers will be created</li> -<li>A valid TLS certificate will be installed, allowing you to easily test on with CHT Android natively on a mobile device</li> -<li>An unused port is automatically chosen for you when creating a new project. No more manually looking at your existing <code>.env</code> files!</li> -</ul> -<h3 id="installing">Installing</h3> -<p>To get started using it:</p> -<ol> -<li>Clone the <a href="https://github.com/medic/cht-core/">CHT Core</a> repo</li> -<li>When you want to check for updates, just run <code>git pull origin</code> in the <code>cht-core</code> directory.</li> -</ol> -<p>If you want a more stand-alone version, you can <code>curl</code> the bash script directly, but you can&rsquo;t use <code>git</code> to easily update it then:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>curl -s -o cht-docker-compose.sh https://raw.githubusercontent.com/medic/cht-core/master/scripts/docker-helper-4.x/cht-docker-compose.sh -</span></span></code></pre></div><h3 id="usage">Usage</h3> -<p>Always run the script from the directory where it lives. If you launch it from a different directory, relative paths will fail:</p> -<table> -<thead> -<tr> -<th>Do</th> -<th>Don&rsquo;t</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>./cht-docker-compose.sh</code></td> -<td><code>./docker-helper-4.x/cht-docker-compose.sh</code></td> -</tr> -</tbody> -</table> -<h4 id="launching">Launching</h4> -<p>Run the script with:</p> -<pre tabindex="0"><code>./cht-docker-compose.sh -</code></pre><p>The first time you run, you will be prompted to create a new project. Here&rsquo;s what that looks like:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>./cht-docker-compose.sh -</span></span><span style="display:flex;"><span>Would you like to initialize a new project <span style="color:#ce5c00;font-weight:bold">[</span>y/N<span style="color:#ce5c00;font-weight:bold">]</span>? y -</span></span><span style="display:flex;"><span>How <span style="color:#204a87;font-weight:bold">do</span> you want to name the project? <span style="color:#0000cf;font-weight:bold">4</span> OH The First -</span></span><span style="display:flex;"><span>Downloading compose files ... <span style="color:#204a87;font-weight:bold">done</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span>Creating network <span style="color:#4e9a06">&#34;4_oh_the_first-cht-net&#34;</span> with the default driver -</span></span><span style="display:flex;"><span>Creating my_first_cht_project-dir_cht-upgrade-service_1 ... <span style="color:#204a87;font-weight:bold">done</span> -</span></span><span style="display:flex;"><span>Starting project <span style="color:#4e9a06">&#34;4_oh_the_first&#34;</span>. First run takes a <span style="color:#204a87;font-weight:bold">while</span>. Will try <span style="color:#204a87;font-weight:bold">for</span> up to five minutes........ -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> -------------------------------------------------------- -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> Success! <span style="color:#4e9a06">&#34;4_oh_the_first&#34;</span> is <span style="color:#204a87">set</span> up: -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> https://127-0-0-1.local-ip.medicmobile.org:10444/ <span style="color:#ce5c00;font-weight:bold">(</span>CHT<span style="color:#ce5c00;font-weight:bold">)</span> -</span></span><span style="display:flex;"><span> https://127-0-0-1.local-ip.medicmobile.org:10444/_utils/ <span style="color:#ce5c00;font-weight:bold">(</span>Fauxton<span style="color:#ce5c00;font-weight:bold">)</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> Login: medic -</span></span><span style="display:flex;"><span> Password: password -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> -------------------------------------------------------- -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span>Start existing project -</span></span><span style="display:flex;"><span> ./cht-docker-compose.sh ENV-FILE.env -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span>Stop and keep project: -</span></span><span style="display:flex;"><span> ./cht-docker-compose.sh ENV-FILE.env stop -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span>Stop and destroy all project data: -</span></span><span style="display:flex;"><span> ./cht-docker-compose.sh ENV-FILE.env destroy -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span>https://docs.communityhealthtoolkit.org/apps/guides/hosting/4.x/app-developer/ -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> Have a great day! -</span></span></code></pre></div><p>If you have many existing projects, you can specify them to launch them directly. If you had a project called <code>4_oh_the_first</code> you would run:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>./cht-docker-compose.sh 4_oh_the_first.env -</span></span></code></pre></div><h4 id="stopping">Stopping</h4> -<p>When you&rsquo;re done with a project, it&rsquo;s good to stop all the containers to reduce load on your computer. Do this by specifying the project and the <code>stop</code> command. This command will simply stop the active Docker containers, and not delete any data. Using our existing example <code>4_oh_the_first</code> project, you would call:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>./cht-docker-compose.sh 4_oh_the_first.env stop -</span></span></code></pre></div><h4 id="destroying">Destroying</h4> -<p>When you want to <strong>permanently delete all files and all data</strong> for a project, specify the project and the <code>destroy</code> command. Using our existing example <code>4_oh_the_first</code> project, you would call:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>./cht-docker-compose.sh 4_oh_the_first.env destroy -</span></span></code></pre></div><p>Be sure you want to do this, there is no &ldquo;are you sure?&rdquo; prompt and it will delete all your data.</p> -<p>Also note that this command will use the <code>sudo</code> command when deleting the CouchDB data, so it may prompt for your password.</p> -<h4 id="debugging">Debugging</h4> -<p>To get debug output while running the docker helper, you can prepend the <code>DEBUG=true</code> flag like this:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#000">DEBUG</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#204a87">true</span> ./cht-docker-compose.sh -</span></span></code></pre></div><p>This shows load average, CHT container count, global container count, and a table of services with their status like this:</p> -<pre tabindex="0"><code>---DEBUG INFO--- -Load: 3.75 2.92 2.93 -CHT Containers: 7 -Global Containers 15 -Service Status Container Image -cht-upgrade-service running 400_deleteme-dir-cht-upgrade-service-1 public.ecr.aws/s5s3h4s7/cht-upgrade-service:latest -haproxy NA NA public.ecr.aws/medic/cht-haproxy:4.4.0-8229-outbound-push -healthcheck running 400_deleteme_healthcheck_1 public.ecr.aws/medic/cht-haproxy-healthcheck:4.4.0-8229-outbound-push -api running 400_deleteme_api_1 public.ecr.aws/medic/cht-api:4.4.0-8229-outbound-push -sentinel running 400_deleteme_sentinel_1 public.ecr.aws/medic/cht-sentinel:4.4.0-8229-outbound-push -nginx running 400_deleteme_nginx_1 public.ecr.aws/medic/cht-nginx:4.4.0-8229-outbound-push -couchdb running 400_deleteme_couchdb_1 public.ecr.aws/medic/cht-couchdb:4.4.0-8229-outbound-push -</code></pre><h4 id="troubleshooting">Troubleshooting</h4> -<p>When you are starting a CHT Core instance using Docker Helper 4.x and don&rsquo;t have any containers created, images downloaded, or storage volumes created - the <code>*.local-ip.medicmobile.org</code> TLS certificate fails to install, which leads to a browser <code>Your connection is not private</code> message.</p> -<p>To solve this issue, follow the steps below:</p> -<ol> -<li>First, find the name of the <code>nginx</code> container with: <code>docker ps --filter &quot;name=nginx&quot; --format '{{ .Names }}'</code>.</li> -<li>After cloning the <a href="https://github.com/medic/cht-core">CHT Core repo</a>, <code>cd</code> into the <code>scripts</code> directory: <code>cd ./cht-core/scripts</code>.</li> -<li>Using the container name from the first command, call the script to update the certificate: <code>./add-local-ip-certs-to-docker-4.x.sh CONTAINER_NAME</code>.</li> -</ol> -<p>These three steps look like as following assuming that <code>CONTAINER_NAME</code> is equal to <code>4_3_0_nginx_1</code>. Note that <code>CONTAINER_NAME</code> will be different for each instance of CHT you run with Docker Helper:</p> -<pre tabindex="0"><code>$ docker ps --filter &#34;name=nginx&#34; --format &#39;{{ .Names }}&#39; -4_3_0_nginx_1 -$ cd Documents/MedicMobile/cht-core/scripts/ -scripts $ ./add-local-ip-certs-to-docker-4.x.sh 4_3_0_nginx_1 -4_3_0_nginx_1 -If just container name is shown above, a fresh local-ip.medicmobile.org certificate was downloaded fresh local-ip.medicmobile.org. -</code></pre><h3 id="file-locations">File locations</h3> -<p>The bash script keeps files in two places:</p> -<ul> -<li><strong><code>*.env</code> files</strong> - the same directory as the bash script.</li> -<li><strong><code>~/medic/cht-docker/</code> files</strong> - in your home directory, a sub-directory is created for each project. Within each project directory, a <code>compose</code> directory has the two compose files and the <code>couch</code> directory has the CouchDB datafiles.</li> -</ul> -<p>While you can manually remove any of these, it&rsquo;s best to use the <code>destroy</code> command above to ensure all related data files are deleted too.</p> -<h3 id="video">Video</h3> -<p>Here is a video of the helper being run on 1 Dec 2022. The video references <code>lazydocker</code> which is <a href="https://github.com/jesseduffield/lazydocker">a great way</a> to monitor and control your local docker environment:</p> -<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"> -<iframe src="https://www.youtube.com/embed/hrcy8JlJP9M" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe> -</div> -<hr>Hosting: Adding TLS certificates in CHT 4.xhttps://docs.communityhealthtoolkit.org/hosting/4.x/adding-tls-certificates/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/adding-tls-certificates/ -<p>By default, CHT 4.x will create a self-signed certificate for every deployment. These instructions are for changing to either a pre-existing certificate or automatically creating and renewing a <a href="https://certbot.eff.org/">Certbot</a> based certificate using <a href="https://acmeclients.com/">ACME</a>, like <a href="https://letsencrypt.org/">Let&rsquo;s Encrypt</a>.</p> -<p>This guide assumes you&rsquo;ve already met the <a href="https://docs.communityhealthtoolkit.org/hosting/requirements/">hosting requirements</a>, specifically around Docker being installed.</p> -<h2 id="pre-existing-certificate">Pre-existing certificate</h2> -<p>To load your certificates into your CHT instance, we&rsquo;ll be creating an interstitial container called <code>cht-temp-tls</code> which will enable you to copy your local certificate files into the native docker volume.</p> -<h3 id="prerequisites">Prerequisites</h3> -<p>You have two files locally on your workstation in the directory you&rsquo;re currently in:</p> -<ul> -<li><code>key.pem</code> - the private key for your TLS certificate</li> -<li><code>cert.pem</code> - both the public and any interstitial keys concatenated into one file</li> -</ul> -<p>Also, be sure you have started your CHT instance once and all your volumes are created.</p> -<h3 id="loading-the-certificate">Loading the certificate</h3> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -<code>docker compose</code> should work, but you may need to use the older style <code>docker-compose</code> if you get an error <code>docker: 'compose' is not a docker command</code>. -</div> -<ol> -<li> -<p>Find the name of your <code>cht-ssl</code> volume with this call:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>docker volume ls --filter <span style="color:#4e9a06">&#34;name=cht-ssl&#34;</span> -</span></span></code></pre></div><p>It is very likely that <code>cht_cht-ssl</code> is the name of our <code>cht-ssl</code> volume.</p> -</li> -<li> -<p>Using the volume name found in step 1, start a container called <code>temp</code> which allow us to copy files into the docker volume:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>docker run -d --rm --name temp -v cht_cht-ssl:/etc/nginx/private/ alpine tail -f /dev/null -</span></span></code></pre></div></li> -<li> -<p>Copy the two pem files into the volume via the temporary container:</p> -<pre tabindex="0"><code>docker cp key.pem temp:/etc/nginx/private/. -docker cp cert.pem temp:/etc/nginx/private/. -</code></pre></li> -<li> -<p>Stop the <code>temp</code> container:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>docker <span style="color:#204a87">kill</span> temp -</span></span></code></pre></div></li> -</ol> -<p>Your certificates are now safely stored in the native docker volume. Restart your CHT instance the way you started it, being sure to set the correct <code>CERTIFICATE_MODE</code> and <code>SSL_VOLUME_MOUNT_PATH</code> per the <a href="#prerequisites">prerequisites</a>.</p> -<h2 id="certbot-certificate">Certbot certificate</h2> -<p><em>This Feature available on CHT 4.2.0 or later</em></p> -<p>If you have a deployment with a static, public IP and a domain name pointing to that IP, you can have Certbot automatically create free TLS certificates by using <a href="https://hub.docker.com/r/certbot/certbot/">their Docker image</a>.</p> -<p>Assuming your CHT instance is running with the default self signed cert. Be sure to change <code>cht.example.com</code> to your domain first though:</p> -<p>Assuming your CHT instance is <strong>already running with the default self-signed cert</strong>:</p> -<ol> -<li>Edit the CHT&rsquo;s environment file at <code>/home/ubuntu/cht/upgrade-service/.env</code> so this line is present: -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#000">CERTIFICATE_MODE</span><span style="color:#ce5c00;font-weight:bold">=</span>AUTO_GENERATE -</span></span></code></pre></div>This will ensure the <code>deploy.sh</code> script that certbot uses to deploy the certificates is available for use.</li> -<li>Restart your CHT instance to ensure the new <code>CERTIFICATE_MODE</code> value takes effect: -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> /home/ubuntu/cht/upgrade-service/ -</span></span><span style="display:flex;"><span>docker stop <span style="color:#204a87;font-weight:bold">$(</span>docker ps --filter <span style="color:#4e9a06">&#34;name=^cht*&#34;</span> -q<span style="color:#204a87;font-weight:bold">)</span> -</span></span><span style="display:flex;"><span>docker stop <span style="color:#204a87;font-weight:bold">$(</span>docker ps --filter <span style="color:#4e9a06">&#34;name=^upgrade-service*&#34;</span> -q<span style="color:#204a87;font-weight:bold">)</span> -</span></span><span style="display:flex;"><span>docker compose up --detach -</span></span></code></pre></div></li> -<li>Create certbot compose and env files by copying and pasting this code: -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>mkdir -p /home/ubuntu/cht/certbot -</span></span><span style="display:flex;"><span><span style="color:#204a87">cd</span> /home/ubuntu/cht/certbot -</span></span><span style="display:flex;"><span>cat &gt; docker-compose.yml <span style="color:#4e9a06">&lt;&lt; EOF -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">version: &#39;3.9&#39; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">services: -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> certbot: -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> container_name: certbot -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> hostname: certbot -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> image: certbot/certbot -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> volumes: -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> - ssl-storage:/etc/nginx/private/ -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> - ssl-storage:/var/log/letsencrypt/ -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> command: certonly --debug --deploy-hook /etc/nginx/private/deploy.sh --webroot -w /etc/nginx/private/certbot/ --domain \$DOMAIN --non-interactive --key-type rsa --agree-tos --register-unsafely-without-email \$STAGING -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">volumes: -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> ssl-storage: -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> name: \${CHT_SSL_VOLUME} -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> external: true -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">EOF</span> -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span>cat &gt; .env <span style="color:#4e9a06">&lt;&lt; EOF -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">DOMAIN=cht.example.com -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">STAGING= -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">CHT_SSL_VOLUME=cht_cht-ssl -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">TZ=America/Whitehorse -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">EOF</span> -</span></span></code></pre></div>Certbot only lets you create the identical certificates 5 times per 7 days. If you&rsquo;re unsure of how this works you can change <code>STAGING=</code> to <code>STAGING=--staging</code> in the <code>/home/ubuntu/cht/certbot/.env</code> file to do repeated tests. Be sure to change this back to <code>STAGING=</code> when you&rsquo;re ready to create production certificates.</li> -<li>Generate certs: -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> /home/ubuntu/cht/certbot -</span></span><span style="display:flex;"><span>docker compose up -</span></span></code></pre></div></li> -<li>Run this command to find the name of your CHT ngnix container: -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>docker ps --filter <span style="color:#4e9a06">&#34;name=nginx&#34;</span> --format <span style="color:#4e9a06">&#39;{{ .Names }}&#39;</span> -</span></span></code></pre></div></li> -<li>Assuming the name is <code>cht_nginx_1</code> from the prior step, reload your <code>nginx</code> config with this command: -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>docker <span style="color:#204a87">exec</span> -it cht_nginx_1 nginx -s reload -</span></span></code></pre></div></li> -<li>Attempt to renew your certificates once a week by adding this cronjob via <code>crontab -e</code>. Certbot will only renew them as needed: -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#0000cf;font-weight:bold">0</span> <span style="color:#0000cf;font-weight:bold">0</span> * * <span style="color:#0000cf;font-weight:bold">0</span> <span style="color:#204a87">cd</span> /home/ubuntu/cht/certbot<span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span>docker compose up -</span></span></code></pre></div></li> -</ol> -<h2 id="troubleshooting">Troubleshooting</h2> -<h3 id="proxying">Proxying</h3> -<h4 id="err_tls_cert_altname_invalid">ERR_TLS_CERT_ALTNAME_INVALID</h4> -<p>When proxying to HTTPS from HTTP (for example where an ingress does TLS termination in an SNI environment and then the traffic is proxied to an HTTPS service (eg, haproxy)), not including a <code>servername</code> for a request to the HTTPS server (eg, def.org) produces the following error:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span><span style="color:#4e9a06">&#39;ERR_TLS_CERT_ALTNAME_INVALID&#39;</span> -</span></span><span style="display:flex;"><span><span style="color:#4e9a06">&#34;RequestError: Error [ERR_TLS_CERT_ALTNAME_INVALID]: Hostname/IP does not match certificate&#39;s altnames: -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">Host: abc.com. is not in the cert&#39;s altnames: DNS:def.org&#34;</span> -</span></span></code></pre></div><p>The addition of <code>servername</code> resolves this error by providing routing information. See <a href="https://nodejs.org/api/tls.html">docs</a> for <code>tls.connect(options[, callback])</code>: &ldquo;Server name for the SNI (Server Name Indication) TLS extension. It is the name of the host being connected to, and must be a host name, and not an IP address.&rdquo;.</p> -<p>A <code>servername</code> parameter may be added to all requests to the haproxy/couchdb by setting the environment variable <code>ADD_SERVERNAME_TO_HTTP_AGENT</code> to <code>true</code>.</p> -<p>A similar change can be made for the http clients used in the application by setting <code>PROXY_CHANGE_ORIGIN</code> to <code>true</code>. This sets the <code>changeOrigin</code> parameter of all the <code>http-proxy</code> clients to <code>true</code>, which &ldquo;changes the origin of the host header to the target URL&rdquo;. See <a href="https://www.npmjs.com/package/http-proxy#options">http-proxy: options</a>.</p>Hosting: Viewing server logs in CHT 4.xhttps://docs.communityhealthtoolkit.org/hosting/4.x/logs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/logs/ -<p>CHT 4.x has the following services running via Docker and each can have its logs queried:</p> -<ul> -<li>nginx</li> -<li>sentinel</li> -<li>api</li> -<li>haproxy</li> -<li>couchdb</li> -<li>healthcheck</li> -<li>upgrade-service</li> -</ul> -<h2 id="setting-log-level">Setting log level</h2> -<p>By default, the CHT server logs are set to the <code>info</code> level. To change the log level to <code>debug</code>, you can set the <code>NODE_ENV</code> environment variable to <code>development</code>. A log level of <code>debug</code> can affect system performance and cause log files sizes to grow rapidly. It is recommended to temporarily set the log level to <code>debug</code> only when needed for troubleshooting.</p> -<h2 id="viewing-logs">Viewing logs</h2> -<p>First, find the actual names of the containers with the <code>docker ps --format '{{.Names}}'</code> command which should show something like this:</p> -<pre tabindex="0"><code>cht_nginx_1 -cht_sentinel_1 -cht_api_1 -cht_haproxy_1 -cht_healthcheck_1 -cht_couchdb_1 -upgrade-service-cht-upgrade-service-1 -</code></pre><p>You can then use the <code>docker logs</code> command to view the logs of any given container. For example, if we call <code>docker logs cht_nginx_1</code> it will show ALL the logs from that container. To show only the last 5 lines, you can use the <code>--tail</code> flag to specify the number of lines like this <code>docker logs cht_nginx_1 --tail 5</code>. The result will look like this:</p> -<pre tabindex="0"><code>10.131.161.1 - - [15/Feb/2023:21:08:35 +0000] &#34;GET /medic/_changes?feed=longpoll&amp;heartbeat=10000&amp;since=115-g1AAAAH5eJyF0LENwjAQBVCLRIAEFBTMgESBCA0lrACJBzgnRXSKoKJmClaAxEswRZbIDCTHZ4GzXPzCT-d_rowx8zIqzDK_3fOycKdkf9jucJIKVyMybmVtxhQp6E-s23jfME00B-LdUWQIOBBxmJkG3gXJHHtfM8WaA2ncQ6RnGmsOZLjG5mLtk2mmSKAUCPHGSkxT3dZAiK_IR28A1AMhzta2wbko2iJe3ndMC92iaIfAzwrTmn8as5aY&amp;limit=25 HTTP/1.1&#34; 499 0 &#34;https://10-131-161-159.local-ip.medicmobile.org/&#34; &#34;Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0&#34; -10.131.161.1 - - [15/Feb/2023:21:08:35 +0000] &#34;GET /medic-user-medic-meta/_changes?include_docs=true&amp;feed=longpoll&amp;heartbeat=10000&amp;since=13-g1AAAAH5eJyF0LENwkAMhWETKGkoWIICERpKWAESD3BOiugUQUXNFKwAiZdgiiyRGUjMY4FYV_zFfbJ8VxPRspqXtCpu96Iq5ZTuD9sdTlrjKgkka-Y8htkE-hOWjWrrOBCVo9noOBATzMwcB5JLVG0cB9LKw2xwHMh4XdCF-TktgTIg5I0nubYBQr5mnxiSaTsAIWfmzp2LRTvkpdq7Fov2CH7WYOMP5CCWMg&amp;limit=25 HTTP/1.1&#34; 499 0 &#34;https://10-131-161-159.local-ip.medicmobile.org/&#34; &#34;Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0&#34; -10.131.161.1 - - [15/Feb/2023:21:08:35 +0000] &#34;GET /fontawesome-webfont.woff2 HTTP/1.1&#34; 304 0 &#34;https://10-131-161-159.local-ip.medicmobile.org/styles.css&#34; &#34;Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0&#34; -10.131.161.1 - - [15/Feb/2023:21:08:35 +0000] &#34;GET /fonts/NotoSans-Bold.ttf HTTP/1.1&#34; 304 0 &#34;https://10-131-161-159.local-ip.medicmobile.org/styles.css&#34; &#34;Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0&#34; -10.131.161.1 - - [15/Feb/2023:21:08:35 +0000] &#34;GET /fonts/NotoSans-Regular.ttf HTTP/1.1&#34; 200 221787 &#34;https://10-131-161-159.local-ip.medicmobile.org/styles.css&#34; &#34;Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0 -</code></pre><p>Sometimes you may want to search the logs for a specific string. To search, use the pipe (<code>|</code>) and <code>grep</code> commands to do this. Here we search for all the times HA Proxy thought CouchDB wasn&rsquo;t reachable (<code>DOWN</code>) with this call <code>docker logs cht_haproxy_1 2&gt;&amp;1 | grep 'DOWN'</code>:</p> -<pre tabindex="0"><code>&lt;145&gt;Feb 15 20:52:06 haproxy[25]: Server couchdb-servers/couchdb is DOWN, reason: Layer7 wrong status, code: 0, info: &#34;via agent : down&#34;, check duration: 208ms. 0 active and 0 backup servers left. 0 sessions active, 0 requeued, 0 remaining in queue. -[WARNING] 045/205206 (25) : Server couchdb-servers/couchdb is DOWN, reason: Layer7 wrong status, code: 0, info: &#34;via agent : down&#34;, check duration: 208ms. 0 active and 0 backup servers left. 0 sessions active, 0 requeued, 0 remaining in queue. -[WARNING] 045/205601 (25) : Server couchdb-servers/couchdb is DOWN, reason: Layer7 wrong status, code: 0, info: &#34;via agent : down&#34;, check duration: 207ms. 0 active and 0 backup servers left. 5 sessions active, 0 requeued, 0 remaining in queue. -&lt;145&gt;Feb 15 20:56:01 haproxy[25]: Server couchdb-servers/couchdb is DOWN, reason: Layer7 wrong status, code: 0, info: &#34;via agent : down&#34;, check duration: 207ms. 0 active and 0 backup servers left. 5 sessions active, 0 requeued, 0 remaining in queue. -</code></pre><p>If you want to watch the logs for a specific container in real time, you can use the <code>--follow</code> flag. This command would watch the requests come into API in realtime: <code>docker logs cht_api_1 --follow</code>. It&rsquo;s nice to couple this with the <code>--tail</code> command so you only see the last 5 lines of the existing logs before watching for new lines with <code>docker logs cht_api_1 --follow --tail 5</code> which would show this:</p> -<pre tabindex="0"><code>RES d17d71f5-2dcb-4ebb-bb0e-7874b3000570 10.131.161.1 - GET /medic/_design/medic-client/_view/reports_by_subject?keys=%5B%22557e79b8-2d99-4bd1-a4d6-a44491d483d8%22%5D HTTP/1.0 200 - 12.452 ms -RES e43c5d7f-4e32-433a-a96d-ef991f4298a3 10.131.161.1 - GET /medic/_design/medic/_view/doc_summaries_by_id?keys=%5B%22557e79b8-2d99-4bd1-a4d6-a44491d483d8%22%5D HTTP/1.0 200 - 31.226 ms -REQ c656ecc7-e6af-4564-ad63-2cab2c42844a 10.131.161.1 - GET /medic/_all_docs?include_docs=true&amp;startkey=%22target~2023-02~557e79b8-2d99-4bd1-a4d6-a44491d483d8~%22&amp;endkey=%22target~2023-02~557e79b8-2d99-4bd1-a4d6-a44491d483d8~%EF%BF%B0%22 HTTP/1.0 -RES c656ecc7-e6af-4564-ad63-2cab2c42844a 10.131.161.1 - GET /medic/_all_docs?include_docs=true&amp;startkey=%22target~2023-02~557e79b8-2d99-4bd1-a4d6-a44491d483d8~%22&amp;endkey=%22target~2023-02~557e79b8-2d99-4bd1-a4d6-a44491d483d8~%EF%BF%B0%22 HTTP/1.0 200 - 11.153 ms -2023-02-15 21:54:49 DEBUG: Checking for a configured outgoing message service -</code></pre>Hosting: Backups in CHT 4.xhttps://docs.communityhealthtoolkit.org/hosting/4.x/backups/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/backups/ -<div class="pageinfo pageinfo-primary"> -<p>This guide is about backups in CHT 4.x - there&rsquo;s the <a href="https://docs.communityhealthtoolkit.org/hosting/3.x/self-hosting/#backup">self hosted guide for 3.x</a> which includes backups for 3.x.</p> -</div> -<h2 id="introduction">Introduction</h2> -<p>As CHT 4.x uses a container per service, the only data that needs to be backed up is:</p> -<ul> -<li>CouchDB database</li> -<li>Docker Compose and <code>.env</code> files</li> -<li>TLS certificates</li> -</ul> -<p>This is because Docker containers are inherently stateless so all the important binaries are already stored in <a href="https://gallery.ecr.aws/s5s3h4s7/">CHT&rsquo;s Docker images</a>. Docker Compose files, including the <code>.env</code> file, store all of your deployment&rsquo;s configuration. Finally, the TLS certificates should be backed up to reduce recovery time.</p> -<p>How to backup each of these three pieces of data is covered below.</p> -<p>Therefore, you do <strong>not</strong> need to back up the docker images for:</p> -<ul> -<li>nginx</li> -<li>sentinel</li> -<li>api</li> -<li>haproxy</li> -<li>couchdb</li> -<li>healthcheck</li> -<li>upgrade-service</li> -</ul> -<h2 id="assumptions">Assumptions</h2> -<p>This guide assumes you have an Ubuntu server running CHT 4.x in Docker as described in our <a href="https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/single-node/">Self Hosting in CHT 4.x - Single CouchDB Node</a> guide. If you run <code>docker ps --format '{{.Names}}'</code> you should see something like this:</p> -<pre tabindex="0"><code>cht_nginx_1 -cht_sentinel_1 -cht_api_1 -cht_haproxy_1 -cht_healthcheck_1 -cht_couchdb_1 -upgrade-service-cht-upgrade-service-1 -</code></pre><p>If you run <code>docker volume ls</code> you should see something like this:</p> -<pre tabindex="0"><code>DRIVER VOLUME NAME -local cht_cht-credentials -local cht_cht-ssl -</code></pre><p><strong>Note</strong> - In the volumes listed above, there is no volume for CouchDB data. This is because the compose file declares this as a <a href="https://docs.docker.com/storage/bind-mounts/">bind mount</a>. Bind mounts use the host file system directly and do not show up in <code>docker volume ls</code> calls. It&rsquo;s therefore assumed your CouchDB data location is declared in <code>/home/ubuntu/cht/upgrade-service/.env</code> which sets it with <code>COUCHDB_DATA=/home/ubuntu/cht/couchdb</code>.</p> -<p>You should have SSH access to the server with <code>root</code> access.</p> -<h3 id="backup-software">Backup software</h3> -<p>It&rsquo;s assumed you are using which ever tool you&rsquo;re familiar with which might include <a href="https://rsync.samba.org/examples.html">rsync</a>, <a href="https://borgbackup.readthedocs.io/en/stable/">borg</a>, <a href="https://duplicity.gitlab.io/">duplicity</a> or other solution. The locations of the backups should follow the 3-2-1 rule:</p> -<blockquote> -<p>There should be at least 3 copies of the data, stored on 2 different types of storage media, and one copy should be kept offsite, in a remote location. <em>- <a href="https://en.wikipedia.org/wiki/Backup">Wikipedia</a></em></p> -</blockquote> -<p>Duplicity has the handy benefit of offering built in encryption using <a href="https://gnupg.org/">GPG</a>. Consider using this if you don&rsquo;t have an existing solution for encrypted backups.</p> -<h2 id="couchdb">CouchDB</h2> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -CouchDB backups, by necessity, will have PII and PHI. They should be safely stored to prevent unauthorized access including encrypting backups. -</div> -<p>Assuming your CouchDB is stored in <code>/home/ubuntu/cht/couchdb</code>, you should use these steps to back it up:</p> -<ol> -<li>While you don&rsquo;t need to stop CouchDB to back it up, ensure you follow best practices to back it up. See the <a href="https://docs.couchdb.org/en/stable/maintenance/backups.html">CouchDB site</a> for more info. Note that Medic recommends NOT using replication for backup.</li> -<li>It is strongly recommended you encrypt your backups given the sensitivity of the contents. Do this now before copying the backup files to their long term location.</li> -<li>Backup the CouchDB files using the <a href="#backup-software">software specified above</a></li> -</ol> -<h2 id="docker-compose-files">Docker Compose files</h2> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -The <code>.env</code> file contains cleartext passwords. It should be safely stored to prevent unauthorized access. -</div> -<p>All compose files, and the corresponding <code>.env</code> file, are in these three locations:</p> -<ul> -<li>/home/ubuntu/cht/compose/*.yml</li> -<li>/home/ubuntu/cht/upgrade-service/*.yml</li> -<li>/home/ubuntu/cht/upgrade-service/.env</li> -</ul> -<p>While all three of these are trivial to recreate by downloading them again, they may change over time so should be archived with your CouchDB data. Further, when there&rsquo;s been a critical failure of a production CHT instance, you want to be sure to make the restore process as speedy as possible.</p> -<p>As all of these files are only read when Docker first loads a service, you can simply copy them whenever you want without stopping any of the CHT services. They should be copied with the same frequency and put in the same location as the CouchDB data using the <a href="#backup-software">backup software specified above</a>.</p> -<h2 id="tls-certificates">TLS certificates</h2> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -The <code>.key</code> file is the private key for TLS certificate. It should be safely stored to prevent unauthorized access. -</div> -<p>Like the compose files, the TLS certificate files can easily be regenerated or re-downloaded from your Certificate Authority, like Let&rsquo;s Encrypt for example. However, you want to have a backup of the at the ready to ease the restore process.</p> -<ol> -<li> -<p>Copy the cert and key files from the nginx container:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>docker cp cht_nginx_1:/etc/nginx/private/key.pem . -</span></span><span style="display:flex;"><span>docker cp cht_nginx_1:/etc/nginx/private/cert.pem . -</span></span></code></pre></div></li> -<li> -<p>Back the up to the same location and frequency as the CouchDB data using the <a href="#backup-software">backup software specified above</a>.</p> -</li> -</ol> -<h2 id="testing-backups">Testing backups</h2> -<p>A backup that isn&rsquo;t tested, is effectively not a backup. For a backup to be successful, a complete restore from all locations in the 3-2-1 plan need to be fully tested and documented as to how a restore works. The more practiced and better documented the restore process, the less downtime a production CHT instance will have after data loss.</p>Hosting: Docker Directory Setuphttps://docs.communityhealthtoolkit.org/hosting/4.x/_partial_docker_directories/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/_partial_docker_directories/ -<p>Create the following directory structure:</p> -<pre tabindex="0"><code>|-- /home/ubuntu/cht/ -|-- compose/ -|-- certs/ -|-- couchdb/ -|-- upgrade-service/ -</code></pre><p>By calling this <code>mkdir</code> commands:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>mkdir -p /home/ubuntu/cht/<span style="color:#ce5c00;font-weight:bold">{</span>compose,certs,upgrade-service,couchdb<span style="color:#ce5c00;font-weight:bold">}</span> -</span></span></code></pre></div><ol> -<li><code>compose</code> - docker-compose files for cht-core and CouchDB</li> -<li><code>certs</code> - TLS certificates directory</li> -<li><code>upgrade-service</code> - where docker-compose file for the upgrade-service</li> -<li><code>couchdb</code> - the path for the docker-compose file of the upgrade-service (not used in multi-node)</li> -</ol> \ No newline at end of file +4.x on Community Health Toolkithttps://docs.communityhealthtoolkit.org/hosting/4.x/Recent content in 4.x on Community Health ToolkitHugo -- gohugo.ioenMigration from CHT 3.x to CHT 4.xhttps://docs.communityhealthtoolkit.org/hosting/4.x/data-migration/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/data-migration/The hosting architecture differs entirely between CHT-Core 3.x and CHT-Core 4.x. Migrating data from an existing instance running CHT 3.x requires a few manual steps. This guide will present the required steps while using a migration helping tool, called couchdb-migration. This tool interfaces with CouchDb, to update shard maps and database metadata. By the end of this guide, your CHT-Core 3.x CouchDb will be down and CHT-Core 4.x ready to be used.App Developer Hosting in CHT 4.xhttps://docs.communityhealthtoolkit.org/hosting/4.x/app-developer/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/app-developer/This guide assumes you are a CHT app developer wanting to either run concurrent instances of the CHT, or easily be able to switch between different instances without losing any data while doing so. To do development on the CHT Core Framework itself, see the development guide. +To deploy the CHT 3.x in production, see either AWS hosting or Self hosting. 4.x production hosting guides are coming soon! +Getting started Be sure to meet the CHT hosting requirements first.Adding TLS certificates in CHT 4.xhttps://docs.communityhealthtoolkit.org/hosting/4.x/adding-tls-certificates/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/adding-tls-certificates/By default, CHT 4.x will create a self-signed certificate for every deployment. These instructions are for changing to either a pre-existing certificate or automatically creating and renewing a Certbot based certificate using ACME, like Let&rsquo;s Encrypt. +This guide assumes you&rsquo;ve already met the hosting requirements, specifically around Docker being installed. +Pre-existing certificate To load your certificates into your CHT instance, we&rsquo;ll be creating an interstitial container called cht-temp-tls which will enable you to copy your local certificate files into the native docker volume.Viewing server logs in CHT 4.xhttps://docs.communityhealthtoolkit.org/hosting/4.x/logs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/logs/CHT 4.x has the following services running via Docker and each can have its logs queried: +nginx sentinel api haproxy couchdb healthcheck upgrade-service Setting log level By default, the CHT server logs are set to the info level. To change the log level to debug, you can set the NODE_ENV environment variable to development. A log level of debug can affect system performance and cause log files sizes to grow rapidly.Backups in CHT 4.xhttps://docs.communityhealthtoolkit.org/hosting/4.x/backups/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/backups/This guide is about backups in CHT 4.x - there&rsquo;s the self hosted guide for 3.x which includes backups for 3.x. +Introduction As CHT 4.x uses a container per service, the only data that needs to be backed up is: +CouchDB database Docker Compose and .env files TLS certificates This is because Docker containers are inherently stateless so all the important binaries are already stored in CHT&rsquo;s Docker images. Docker Compose files, including the .Docker Directory Setuphttps://docs.communityhealthtoolkit.org/hosting/4.x/_partial_docker_directories/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/_partial_docker_directories/Create the following directory structure: +|-- /home/ubuntu/cht/ |-- compose/ |-- certs/ |-- couchdb/ |-- upgrade-service/ By calling this mkdir commands: +mkdir -p /home/ubuntu/cht/{compose,certs,upgrade-service,couchdb} compose - docker-compose files for cht-core and CouchDB certs - TLS certificates directory upgrade-service - where docker-compose file for the upgrade-service couchdb - the path for the docker-compose file of the upgrade-service (not used in multi-node) \ No newline at end of file diff --git a/hosting/4.x/logs/index.html b/hosting/4.x/logs/index.html index 791cd86c90..80be729219 100644 --- a/hosting/4.x/logs/index.html +++ b/hosting/4.x/logs/index.html @@ -1,9 +1,9 @@ -Viewing server logs in CHT 4.x | Community Health Toolkit +Viewing server logs in CHT 4.x | Community Health Toolkit

    Viewing server logs in CHT 4.x

    What to do when you need to find server side errors in CHT 4.x

    CHT 4.x has the following services running via Docker and each can have its logs queried:

    • nginx
    • sentinel
    • api
    • haproxy
    • couchdb
    • healthcheck
    • upgrade-service

    Setting log level

    By default, the CHT server logs are set to the info level. To change the log level to debug, you can set the NODE_ENV environment variable to development. A log level of debug can affect system performance and cause log files sizes to grow rapidly. It is recommended to temporarily set the log level to debug only when needed for troubleshooting.

    Viewing logs

    First, find the actual names of the containers with the docker ps --format '{{.Names}}' command which should show something like this:

    cht_nginx_1
    + Create project issue

    Viewing server logs in CHT 4.x

    What to do when you need to find server side errors in CHT 4.x

    CHT 4.x has the following services running via Docker and each can have its logs queried:

    • nginx
    • sentinel
    • api
    • haproxy
    • couchdb
    • healthcheck
    • upgrade-service

    Setting log level

    By default, the CHT server logs are set to the info level. To change the log level to debug, you can set the NODE_ENV environment variable to development. A log level of debug can affect system performance and cause log files sizes to grow rapidly. It is recommended to temporarily set the log level to debug only when needed for troubleshooting.

    Viewing logs

    First, find the actual names of the containers with the docker ps --format '{{.Names}}' command which should show something like this:

    cht_nginx_1
     cht_sentinel_1
     cht_api_1
     cht_haproxy_1
    @@ -328,7 +328,8 @@
     Quick Guides >
     Debugging >
     Browser and Phone Logs

    How to obtain Android and browser client logs

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/hosting/4.x/self-hosting/_partial_upgrade_service/index.html b/hosting/4.x/self-hosting/_partial_upgrade_service/index.html index ff18e71436..f38d54c68c 100644 --- a/hosting/4.x/self-hosting/_partial_upgrade_service/index.html +++ b/hosting/4.x/self-hosting/_partial_upgrade_service/index.html @@ -1,5 +1,5 @@ -Upgrading the cht-upgrade-service | Community Health Toolkit -

    Upgrading the cht-upgrade-service

    Upgrading the cht-upgrade-service

    The CHT Upgrade Service provides an interface between the CHT Core API and Docker to allow easy startup and one-click upgrades from the CHT Admin UI. Occasionally, the CHT Upgrade Service, itself, will need to be upgraded. If an upgrade is available, it is highly recommended that you install the upgrade for the CHT Upgrade Service before performing further upgrades on your CHT instance. This is done via the following steps:

    1. Verify that the version of the cht-upgrade-service image in your ./upgrade-service/docker-compose.yml files is set to latest.
    2. Pull the latest cht-upgrade-service image from Docker Hub and replace the current container by running the following command:
      cd /home/ubuntu/cht/upgrade-service
      + Create project issue

      Upgrading the cht-upgrade-service

      Upgrading the cht-upgrade-service

      The CHT Upgrade Service provides an interface between the CHT Core API and Docker to allow easy startup and one-click upgrades from the CHT Admin UI. Occasionally, the CHT Upgrade Service, itself, will need to be upgraded. If an upgrade is available, it is highly recommended that you install the upgrade for the CHT Upgrade Service before performing further upgrades on your CHT instance. This is done via the following steps:

      1. Verify that the version of the cht-upgrade-service image in your ./upgrade-service/docker-compose.yml files is set to latest.
      2. Pull the latest cht-upgrade-service image from Docker Hub and replace the current container by running the following command:
        cd /home/ubuntu/cht/upgrade-service
         docker compose pull
         docker compose up --detach
         

      Follow the Product Releases channel on the CHT forum to stay informed about new releases and upgrades.

      -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/hosting/4.x/self-hosting/index.html b/hosting/4.x/self-hosting/index.html index b16c588fc8..7da98ff3a6 100644 --- a/hosting/4.x/self-hosting/index.html +++ b/hosting/4.x/self-hosting/index.html @@ -1,9 +1,9 @@ -Self Hosting in CHT 4.x | Community Health Toolkit +Self Hosting in CHT 4.x | Community Health Toolkit

    Self Hosting in CHT 4.x

    Details for hosting the CHT on self run infrastructure

    Recommendations and considerations

    Multi vs Single node couchdb requirements

    For smaller deployments a single node CouchDB instance can be used, for larger deployments a multi-node CouchDB cluster is generally recommended

    ConsiderationSingle node CouchDBMulti-node clustered CouchDB
    Less than + Create project issue

    Self Hosting in CHT 4.x

    Details for hosting the CHT on self run infrastructure

    Recommendations and considerations

    Multi vs Single node couchdb requirements

    For smaller deployments a single node CouchDB instance can be used, for larger deployments a multi-node CouchDB cluster is generally recommended

    ConsiderationSingle node CouchDBMulti-node clustered CouchDB
    Less than 4 000 users
    More than 4 000 users
    Less than 10 000 documents per day
    More than 10 000 documents per day
    Seamless upgrade with multi-node docker compose
    Seamless upgrade with multi-node kubernetes/k3s

    Cloud provider vs Bare metal

    ConsiderationCloud providerBare Metal
    Data needs to be in-country

    Self Hosting in CHT 4.x - Single CouchDB Node

    Self Hosting in CHT 4.x - Single CouchDB Node

    Self Hosting in CHT 4.x - Multiple CouchDB Nodes on Docker Swarm

    Hosting the CHT on self run infrastructure with horizontally scaled CouchDB nodes

    Self Hosting in CHT 4.x - Multiple CouchDB Nodes on k3s on VMWare

    Hosting the CHT on self run VMware infrastructure for multiple CHT-Core projects that utilize horizontally scaled CouchDB nodes

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/hosting/4.x/self-hosting/index.xml b/hosting/4.x/self-hosting/index.xml index 7d702e570b..a41bf31772 100644 --- a/hosting/4.x/self-hosting/index.xml +++ b/hosting/4.x/self-hosting/index.xml @@ -1,703 +1,7 @@ -Community Health Toolkit – Self Hosting in CHT 4.xhttps://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/Recent content in Self Hosting in CHT 4.x on Community Health ToolkitHugo -- gohugo.ioenHosting: Self Hosting in CHT 4.x - Single CouchDB Nodehttps://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/single-node/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/single-node/ -<div class="pageinfo pageinfo-primary"> -<p>This for a single node CHT 4.x instance and is the recommended solution for small deployments. If you want a more powerful setup, check out <a href="https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/multiple-nodes/">the 4.x multi-node install docs</a>.</p> -</div> -<h2 id="prerequisites">Prerequisites</h2> -<p>Be sure you have followed <a href="https://docs.communityhealthtoolkit.org/hosting/requirements/">the requirements document</a> including installing Docker and Docker Compose. This guide assumes you&rsquo;re using the <code>ubuntu</code> user and that it <a href="https://askubuntu.com/a/477554">has <code>sudo-less</code> access to Docker</a>.</p> -<h2 id="directory-structure">Directory Structure</h2> -<p>Create the following directory structure:</p> -<pre tabindex="0"><code>|-- /home/ubuntu/cht/ -|-- compose/ -|-- certs/ -|-- couchdb/ -|-- upgrade-service/ -</code></pre><p>By calling this <code>mkdir</code> commands:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>mkdir -p /home/ubuntu/cht/<span style="color:#ce5c00;font-weight:bold">{</span>compose,certs,upgrade-service,couchdb<span style="color:#ce5c00;font-weight:bold">}</span> -</span></span></code></pre></div><ol> -<li><code>compose</code> - docker-compose files for cht-core and CouchDB</li> -<li><code>certs</code> - TLS certificates directory</li> -<li><code>upgrade-service</code> - where docker-compose file for the upgrade-service</li> -<li><code>couchdb</code> - the path for the docker-compose file of the upgrade-service (not used in multi-node)</li> -</ol> -<h2 id="download-required-docker-compose-files">Download required docker-compose files</h2> -<p>The following 3 <code>curl</code> commands download CHT version <code>4.0.1</code> compose files, which you can change as needed. Otherwise, call:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> /home/ubuntu/cht/ -</span></span><span style="display:flex;"><span>curl -s -o ./compose/cht-core.yml https://staging.dev.medicmobile.org/_couch/builds_4/medic:medic:4.3.1/docker-compose/cht-core.yml -</span></span><span style="display:flex;"><span>curl -s -o ./compose/cht-couchdb.yml https://staging.dev.medicmobile.org/_couch/builds_4/medic:medic:4.3.1/docker-compose/cht-couchdb.yml -</span></span><span style="display:flex;"><span>curl -s -o ./upgrade-service/docker-compose.yml https://raw.githubusercontent.com/medic/cht-upgrade-service/main/docker-compose.yml -</span></span></code></pre></div><h2 id="prepare-environment-variables-file">Prepare Environment Variables file</h2> -<p>Prepare a <code>.env</code> file by running this code:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>sudo apt install wamerican -</span></span><span style="display:flex;"><span><span style="color:#000">uuid</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#204a87;font-weight:bold">$(</span>uuidgen<span style="color:#204a87;font-weight:bold">)</span> -</span></span><span style="display:flex;"><span><span style="color:#000">couchdb_secret</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#204a87;font-weight:bold">$(</span>shuf -n7 /usr/share/dict/words --random-source<span style="color:#ce5c00;font-weight:bold">=</span>/dev/random <span style="color:#000;font-weight:bold">|</span> tr <span style="color:#4e9a06">&#39;\n&#39;</span> <span style="color:#4e9a06">&#39;-&#39;</span> <span style="color:#000;font-weight:bold">|</span> tr -d <span style="color:#4e9a06">&#34;&#39;&#34;</span> <span style="color:#000;font-weight:bold">|</span> cut -d<span style="color:#4e9a06">&#39;-&#39;</span> -f1,2,3,4,5,6,7<span style="color:#204a87;font-weight:bold">)</span> -</span></span><span style="display:flex;"><span><span style="color:#000">couchdb_password</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#204a87;font-weight:bold">$(</span>shuf -n7 /usr/share/dict/words --random-source<span style="color:#ce5c00;font-weight:bold">=</span>/dev/random <span style="color:#000;font-weight:bold">|</span> tr <span style="color:#4e9a06">&#39;\n&#39;</span> <span style="color:#4e9a06">&#39;-&#39;</span> <span style="color:#000;font-weight:bold">|</span> tr -d <span style="color:#4e9a06">&#34;&#39;&#34;</span> <span style="color:#000;font-weight:bold">|</span> cut -d<span style="color:#4e9a06">&#39;-&#39;</span> -f1,2,3,4,5,6,7<span style="color:#204a87;font-weight:bold">)</span> -</span></span><span style="display:flex;"><span>cat &gt; /home/ubuntu/cht/upgrade-service/.env <span style="color:#4e9a06">&lt;&lt; EOF -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">CHT_COMPOSE_PROJECT_NAME=cht -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">COUCHDB_SECRET=${couchdb_secret} -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">DOCKER_CONFIG_PATH=/home/ubuntu/cht/upgrade-service -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">COUCHDB_DATA=/home/ubuntu/cht/couchdb -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">CHT_COMPOSE_PATH=/home/ubuntu/cht/compose -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">COUCHDB_USER=medic -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">COUCHDB_PASSWORD=${couchdb_password} -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">COUCHDB_UUID=${uuid} -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"># &lt;ADD_SERVERNAME_TO_HTTP_AGENT&gt;: (optional, default: false, configures: api) Adds &#39;servername&#39; to HTTP agent for certificate issues in receiving traffic when proxying HTTPS-&gt;HTTP in SNI environments with external TLS termination. -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"># See &#39;tls.connect(options[, callback])&#39; (https://nodejs.org/api/tls.html). May resolve &#39;ERR_TLS_CERT_ALTNAME_INVALID&#39; error. -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"># ADD_SERVERNAME_TO_HTTP_AGENT=true -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"># &lt;PROXY_CHANGE_ORIGIN&gt;: (optional, default: false, configures: api) See http-proxy (https://www.npmjs.com/package/http-proxy#options). Sets &#39;changeOrigin&#39; to &#39;true&#39; for HTTP clients. For certificate issues in proxying HTTP-&gt;HTTPS -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"># PROXY_CHANGE_ORIGIN=true -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">EOF</span> -</span></span></code></pre></div><p>Note that secure passwords and UUIDs were generated on the first four calls and saved in the resulting <code>.env</code> file. Uncomment optional items if required.</p> -<h2 id="launch-containers">Launch containers</h2> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -This section has the first use of <code>docker compose</code>. This should work, but you may need to use the older style <code>docker-compose</code> if you get an error <code>docker: 'compose' is not a docker command.</code>. -</div> -<p>To start your CHT instance, run the following</p> -<pre tabindex="0"><code>cd /home/ubuntu/cht/upgrade-service -docker compose up --detach -</code></pre><p>Docker will start the upgrade service, which in turn pulls the required images and starts all the services as defined by the compose files in <code>/home/ubuntu/cht/compose</code>.</p> -<p>To follow the progress tail the log of the upgrade service container by running this:</p> -<p><code>docker logs -f upgrade-service_cht-upgrade-service_1</code></p> -<p>To make sure everything is running correctly, call <code>docker ps</code> and make sure that 7 CHT containers show:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES -</span></span><span style="display:flex;"><span>8c1c22d526f3 public.ecr.aws/s5s3h4s7/cht-nginx:4.0.1-4.0.1 <span style="color:#4e9a06">&#34;/docker-entrypoint.…&#34;</span> <span style="color:#0000cf;font-weight:bold">17</span> minutes ago Up <span style="color:#0000cf;font-weight:bold">8</span> minutes 0.0.0.0:80-&gt;80/tcp, :::80-&gt;80/tcp, 0.0.0.0:443-&gt;443/tcp, :::443-&gt;443/tcp cht_nginx_1 -</span></span><span style="display:flex;"><span>f7b596be2721 public.ecr.aws/s5s3h4s7/cht-api:4.0.1-4.0.1 <span style="color:#4e9a06">&#34;/bin/bash /api/dock…&#34;</span> <span style="color:#0000cf;font-weight:bold">17</span> minutes ago Up <span style="color:#0000cf;font-weight:bold">8</span> minutes 5988/tcp cht_api_1 -</span></span><span style="display:flex;"><span>029cd86ac721 public.ecr.aws/s5s3h4s7/cht-sentinel:4.0.1-4.0.1 <span style="color:#4e9a06">&#34;/bin/bash /sentinel…&#34;</span> <span style="color:#0000cf;font-weight:bold">17</span> minutes ago Up <span style="color:#0000cf;font-weight:bold">8</span> minutes cht_sentinel_1 -</span></span><span style="display:flex;"><span>61ee1e0b377b public.ecr.aws/s5s3h4s7/cht-haproxy-healthcheck:4.0.1-4.0.1 <span style="color:#4e9a06">&#34;/bin/sh -c \&#34;/app/ch…&#34;</span> <span style="color:#0000cf;font-weight:bold">17</span> minutes ago Up <span style="color:#0000cf;font-weight:bold">8</span> minutes cht_healthcheck_1 -</span></span><span style="display:flex;"><span>87415a2d91ea public.ecr.aws/s5s3h4s7/cht-haproxy:4.0.1-4.0.1 <span style="color:#4e9a06">&#34;/entrypoint.sh -f /…&#34;</span> <span style="color:#0000cf;font-weight:bold">17</span> minutes ago Up <span style="color:#0000cf;font-weight:bold">8</span> minutes 5984/tcp cht_haproxy_1 -</span></span><span style="display:flex;"><span>58454457467a public.ecr.aws/s5s3h4s7/cht-couchdb:4.0.1-4.0.1 <span style="color:#4e9a06">&#34;tini -- /docker-ent…&#34;</span> <span style="color:#0000cf;font-weight:bold">17</span> minutes ago Up <span style="color:#0000cf;font-weight:bold">8</span> minutes 4369/tcp, 5984/tcp, 9100/tcp cht_couchdb_1 -</span></span><span style="display:flex;"><span>d01343658f3f public.ecr.aws/s5s3h4s7/cht-upgrade-service:latest <span style="color:#4e9a06">&#34;node /app/src/index…&#34;</span> <span style="color:#0000cf;font-weight:bold">17</span> minutes ago Up <span style="color:#0000cf;font-weight:bold">8</span> minutes upgrade-service-cht-upgrade-service-1 -</span></span></code></pre></div><p>This should show related to the CHT core are running</p> -<ul> -<li>cht_nginx</li> -<li>cht_api</li> -<li>cht_sentinel</li> -<li>cht_couchdb</li> -<li>cht_healthcheck</li> -<li>cht_haproxy</li> -<li>cht-upgrade-service</li> -</ul> -<p>Take note of the <code>STATUS</code> column and make sure no errors are displayed there. If any container is restarting or mentioning any other error, check the logs using the <code>docker logs &lt;container-name&gt;</code> command.</p> -<p>If all has gone well, nginx should now be listening at both port 80 and port 443. Port 80 has a permanent redirect to port 443, so you can only access the CHT using https.</p> -<p>To login as the <code>medic</code> user in the web app, you can find your password with this command:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>grep COUCHDB_PASSWORD /home/ubuntu/cht/upgrade-service/.env <span style="color:#000;font-weight:bold">|</span> cut -d<span style="color:#4e9a06">&#39;=&#39;</span> -f2 -</span></span></code></pre></div><h2 id="tls-certificates">TLS Certificates</h2> -<p>See the <a href="https://docs.communityhealthtoolkit.org/hosting/4.x/adding-tls-certificates/">TLS Certificates page</a> for how to import your certificates.</p> -<h2 id="upgrades">Upgrades</h2> -<p>During upgrades, the CHT upgrade service updates the docker-compose files located in <code>/home/ubuntu/cht/compose/</code>. This means that any and all changes made to the docker-compose files will be overwritten. If there is ever a need to make any changes to the docker-compose files, be sure to re-do them post upgrades or should consider implementing them outside of those docker-compose files.</p> -<h3 id="upgrading-the-cht-upgrade-service">Upgrading the cht-upgrade-service</h3> -<p>The <a href="https://github.com/medic/cht-upgrade-service">CHT Upgrade Service</a> provides an interface between the CHT Core API and Docker to allow easy startup and one-click upgrades from the CHT Admin UI. Occasionally, the CHT Upgrade Service, itself, will need to be upgraded. If an upgrade is available, it is highly recommended that you install the upgrade for the CHT Upgrade Service before performing further upgrades on your CHT instance. This is done via the following steps:</p> -<ol> -<li>Verify that the <em>version</em> of the <code>cht-upgrade-service</code> image in your <code>./upgrade-service/docker-compose.yml</code> files is set to <code>latest</code>.</li> -<li>Pull the latest <code>cht-upgrade-service</code> image from Docker Hub and replace the current container by running the following command: -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> /home/ubuntu/cht/upgrade-service -</span></span><span style="display:flex;"><span>docker compose pull -</span></span><span style="display:flex;"><span>docker compose up --detach -</span></span></code></pre></div></li> -</ol> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Upgrading the CHT Upgrade Service will not cause a new CHT version to be installed. The CHT Core and CouchDB containers are not affected. -</div> -<p>Follow the <a href="https://forum.communityhealthtoolkit.org/c/product/releases/26">Product Releases channel</a> on the CHT forum to stay informed about new releases and upgrades.</p>Hosting: Self Hosting in CHT 4.x - Multiple CouchDB Nodes on Docker Swarmhttps://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/multiple-nodes/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/multiple-nodes/ -<div class="pageinfo pageinfo-primary"> -<p>The clustered multi-node hosting described below is recommended for deployments that need increased performance gains. These gains will increase the complexity of troubleshooting and decrease the ease ongoing maintenance.</p> -<p>If you are unsure which deployment to use check out <a href="https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/#recommendations-and-considerations">Self-hosting recommendations</a>.</p> -</div> -<h3 id="about-clustered-deployments">About clustered deployments</h3> -<p>In a clustered CHT setup, there are multiple CouchDB nodes responding to users. The ability to <a href="https://en.wikipedia.org/wiki/Horizontal_scaling#Horizontal_(scale_out)_and_vertical_scaling_(scale_up)">horizontally scale</a> a CHT instance was added in version CHT 4.0.0. In this document we set up a three node CouchDB cluster. We require all three CouchDB nodes to be running and healthy before installing the CHT. Our healthcheck service determines the health of the CouchDB nodes and turns off the CHT if any single node is not functional.</p> -<h3 id="nodes">Nodes</h3> -<ul> -<li>CHT Core (1x) - Core functionality of the CHT including API and sentinel</li> -<li>CouchDB (3x) - 3 node CouchDB cluster</li> -</ul> -<h2 id="prerequisites">Prerequisites</h2> -<h3 id="servers">Servers</h3> -<p>Provision four Ubuntu servers (22.04 as of this writing) that meet our <a href="https://docs.communityhealthtoolkit.org/hosting/requirements/">hosting requirements</a> including installing Docker and Docker Compose on all of them. This guide assumes you&rsquo;re using the <code>ubuntu</code> user, with a home directory of <code>/home/ubuntu</code> and that it <a href="https://askubuntu.com/a/477554">has <code>sudo-less</code> access to Docker</a>.</p> -<h3 id="network">Network</h3> -<p>Make sure the following ports are open for all nodes:</p> -<ul> -<li><code>7946 TCP/UDP</code> - For Docker communication amongst nodes</li> -<li><code>2377 TCP</code> - Docker cluster management communication</li> -<li><code>4789 UDP</code> - Docker overlay network traffic</li> -<li><code>ICMP</code> - For ping</li> -</ul> -<p>As a security measure, be sure to restrict the IP addresses of the four nodes only to be able to connect to these ports.</p> -<h2 id="create-an-overlay-network">Create an Overlay Network</h2> -<p>To set up a private network that only the four nodes can use, we&rsquo;ll use <code>docker swarm</code>&rsquo;s overlay network feature. You&rsquo;ll first need to initialize the swarm on the CHT Core node and then join the swarm on each of the three CouchDB nodes.</p> -<h3 id="cht-core-node">CHT Core node</h3> -<p>Initialize swarm mode by running:</p> -<pre tabindex="0"><code>docker swarm init -</code></pre><p>This will output:</p> -<div class="highlight"><div style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"> -<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"> -<pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1 -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2 -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3 -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4 -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5 -</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6 -</span></code></pre></td> -<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"> -<pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>Swarm initialized: current node <span style="color:#ce5c00;font-weight:bold">(</span>ca7z1v4tm9q4kf9uimreqoauj<span style="color:#ce5c00;font-weight:bold">)</span> is now a manager. -</span></span><span style="display:flex;"><span>To add a worker to this swarm, run the following command: -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span> docker swarm join --token &lt;very-long-token-value&gt; &lt;main-server-private-ip&gt;:2377 -</span></span><span style="display:flex;"><span> -</span></span><span style="display:flex;"><span>To add a manager to this swarm, run <span style="color:#4e9a06">&#39;docker swarm join-token manager&#39;</span> and follow the instructions. </span></span></code></pre></td></tr></table> -</div> -</div> -<p>Then create overlay network by calling:</p> -<pre tabindex="0"><code>docker network create --driver=overlay --attachable cht-net -</code></pre><h3 id="couchdb-nodes">CouchDB nodes</h3> -<p>On each of these three CouchDB nodes run the <code>docker swarm join</code> command given to you in <a href="#cht-core-node">line 4 above in &ldquo;CHT Core node&rdquo;</a>:</p> -<pre><code>docker swarm join --token &lt;very-long-token-value&gt; &lt;main-server-private-ip&gt;:2377` -</code></pre> -<h3 id="confirm-swarm">Confirm swarm</h3> -<p>Back on the CHT Core node, run <code>docker node ls</code> and ensure you see 4 nodes listed as <code>STATUS</code> of <code>Ready</code> and <code>AVAILABILITY</code> of <code>Active</code></p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION -</span></span><span style="display:flex;"><span>zolpxb5jpej8yiq9gcyv2nrdj * cht-core Ready Active Leader 20.10.23 -</span></span><span style="display:flex;"><span>y9giir8c3ydifxvwozs3sn8vw couchdb1 Ready Active 20.10.23 -</span></span><span style="display:flex;"><span>mi3vj0prd76djbvxms43urqiv couchdb2 Ready Active 20.10.23 -</span></span><span style="display:flex;"><span>kcpxlci3jjjtm6xjz7v50ef7k couchdb3 Ready Active 20.10.23 -</span></span></code></pre></div><h2 id="cht-core-installation">CHT Core installation</h2> -<p>Create the following directory structure:</p> -<pre tabindex="0"><code>|-- /home/ubuntu/cht/ -|-- compose/ -|-- certs/ -|-- couchdb/ -|-- upgrade-service/ -</code></pre><p>By calling this <code>mkdir</code> commands:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>mkdir -p /home/ubuntu/cht/<span style="color:#ce5c00;font-weight:bold">{</span>compose,certs,upgrade-service,couchdb<span style="color:#ce5c00;font-weight:bold">}</span> -</span></span></code></pre></div><ol> -<li><code>compose</code> - docker-compose files for cht-core and CouchDB</li> -<li><code>certs</code> - TLS certificates directory</li> -<li><code>upgrade-service</code> - where docker-compose file for the upgrade-service</li> -<li><code>couchdb</code> - the path for the docker-compose file of the upgrade-service (not used in multi-node)</li> -</ol> -<h3 id="prepare-environment-variables-file">Prepare Environment Variables file</h3> -<p>Prepare an <code>.env</code> file by running this code:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>sudo apt install wamerican -</span></span><span style="display:flex;"><span><span style="color:#000">uuid</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#204a87;font-weight:bold">$(</span>uuidgen<span style="color:#204a87;font-weight:bold">)</span> -</span></span><span style="display:flex;"><span><span style="color:#000">couchdb_secret</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#204a87;font-weight:bold">$(</span>shuf -n7 /usr/share/dict/words --random-source<span style="color:#ce5c00;font-weight:bold">=</span>/dev/random <span style="color:#000;font-weight:bold">|</span> tr <span style="color:#4e9a06">&#39;\n&#39;</span> <span style="color:#4e9a06">&#39;-&#39;</span> <span style="color:#000;font-weight:bold">|</span> tr -d <span style="color:#4e9a06">&#34;&#39;&#34;</span> <span style="color:#000;font-weight:bold">|</span> cut -d<span style="color:#4e9a06">&#39;-&#39;</span> -f1,2,3,4,5,6,7<span style="color:#204a87;font-weight:bold">)</span> -</span></span><span style="display:flex;"><span><span style="color:#000">couchdb_password</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#204a87;font-weight:bold">$(</span>shuf -n7 /usr/share/dict/words --random-source<span style="color:#ce5c00;font-weight:bold">=</span>/dev/random <span style="color:#000;font-weight:bold">|</span> tr <span style="color:#4e9a06">&#39;\n&#39;</span> <span style="color:#4e9a06">&#39;-&#39;</span> <span style="color:#000;font-weight:bold">|</span> tr -d <span style="color:#4e9a06">&#34;&#39;&#34;</span> <span style="color:#000;font-weight:bold">|</span> cut -d<span style="color:#4e9a06">&#39;-&#39;</span> -f1,2,3,4,5,6,7<span style="color:#204a87;font-weight:bold">)</span> -</span></span><span style="display:flex;"><span>cat &gt; /home/ubuntu/cht/upgrade-service/.env <span style="color:#4e9a06">&lt;&lt; EOF -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">CHT_COMPOSE_PROJECT_NAME=cht -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">DOCKER_CONFIG_PATH=/home/ubuntu/cht/upgrade-service -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">CHT_COMPOSE_PATH=/home/ubuntu/cht/compose -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">COUCHDB_USER=medic -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">COUCHDB_PASSWORD=${couchdb_password} -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">COUCHDB_SERVERS=couchdb-1.local,couchdb-2.local,couchdb-3.local -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"># &lt;ADD_SERVERNAME_TO_HTTP_AGENT&gt;: (optional, default: false, configures: api) Adds &#39;servername&#39; to HTTP agent for certificate issues in receiving traffic when proxying HTTPS-&gt;HTTP in SNI environments with external TLS termination. -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"># See &#39;tls.connect(options[, callback])&#39; (https://nodejs.org/api/tls.html). May resolve &#39;ERR_TLS_CERT_ALTNAME_INVALID&#39; error. -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"># ADD_SERVERNAME_TO_HTTP_AGENT=true -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"># &lt;PROXY_CHANGE_ORIGIN&gt;: (optional, default: false, configures: api) See http-proxy (https://www.npmjs.com/package/http-proxy#options). Sets &#39;changeOrigin&#39; to &#39;true&#39; for HTTP clients. For certificate issues in proxying HTTP-&gt;HTTPS -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"># PROXY_CHANGE_ORIGIN=true -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">EOF</span> -</span></span></code></pre></div><p>Note that secure passwords and UUIDs were generated on the first four calls and saved in the resulting <code>.env</code> file. Uncomment optional items if required.</p> -<h3 id="download-compose-files">Download compose files</h3> -<p>The following 2 <code>curl</code> commands download CHT version <code>4.0.1</code> compose files, which you can change as needed. Otherwise, call:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> /home/ubuntu/cht/ -</span></span><span style="display:flex;"><span>curl -s -o ./compose/cht-core.yml https://staging.dev.medicmobile.org/_couch/builds_4/medic:medic:4.3.1/docker-compose/cht-core.yml -</span></span><span style="display:flex;"><span>curl -s -o ./upgrade-service/docker-compose.yml https://raw.githubusercontent.com/medic/cht-upgrade-service/main/docker-compose.yml -</span></span></code></pre></div><h4 id="compose-file-overrides">Compose file overrides</h4> -<p>We need to override the <code>networks:</code> in the two compose files we just created. Create the override file with this code:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>cat &gt; /home/ubuntu/cht/compose/cluster-overrides.yml <span style="color:#4e9a06">&lt;&lt; EOF -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">version: &#39;3.9&#39; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">networks: -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> cht-net: -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> driver: overlay -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> external: true -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">EOF</span> -</span></span></code></pre></div><h3 id="tls-certificates">TLS Certificates</h3> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -This section has the first use of <code>docker compose</code>. This should work, but you may need to use the older style <code>docker-compose</code> if you get an error <code>docker: 'compose' is not a docker command</code>. -</div> -<p>To ensure the needed docker volume is created, start the CHT Core services, which will intentionally all fail as the CouchDB nodes don&rsquo;t exist. We&rsquo;ll then ensure they&rsquo;re all stopped with the <code>docker kill</code> at the end. Note that this command has will <code>sleep 120</code> (wait for 2 minutes) in hopes of</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> /home/ubuntu/cht/upgrade-service/ -</span></span><span style="display:flex;"><span>docker compose up -d -</span></span><span style="display:flex;"><span>sleep <span style="color:#0000cf;font-weight:bold">120</span> -</span></span><span style="display:flex;"><span>docker <span style="color:#204a87">kill</span> <span style="color:#204a87;font-weight:bold">$(</span>docker ps --quiet<span style="color:#204a87;font-weight:bold">)</span> -</span></span></code></pre></div><p>With docker volume having been created, see the <a href="https://docs.communityhealthtoolkit.org/hosting/4.x/adding-tls-certificates/">TLS Certificates page</a> for how to import your certificates on the CHT Core node.</p> -<h2 id="couchdb-installation-on-3-nodes">CouchDB installation on 3 nodes</h2> -<p>Now that CHT Core is installed, we need to install CouchDB on the three nodes. Be sure all 3 nodes <a href="#prerequisites">meet the prerequisites</a> before proceeding.</p> -<h3 id="prepare-environment-variables-file-1">Prepare Environment Variables file</h3> -<p>First, <strong>on the CHT Core node</strong>, get your CouchDB password with this command:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>grep COUCHDB_PASSWORD /home/ubuntu/cht/upgrade-service/.env <span style="color:#000;font-weight:bold">|</span> cut -d<span style="color:#4e9a06">&#39;=&#39;</span> -f2 -</span></span></code></pre></div><p>Now, <strong>on all 3 CouchDB nodes</strong>, create an <code>.env</code> file by running this code. You&rsquo;ll need to replace <code>PASSWORD-FROM-ABOVE</code> so it is the same on all three nodes:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>sudo apt install wamerican -</span></span><span style="display:flex;"><span>mkdir -p /home/ubuntu/cht/srv -</span></span><span style="display:flex;"><span><span style="color:#000">uuid</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#204a87;font-weight:bold">$(</span>uuidgen<span style="color:#204a87;font-weight:bold">)</span> -</span></span><span style="display:flex;"><span><span style="color:#000">couchdb_secret</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#204a87;font-weight:bold">$(</span>shuf -n7 /usr/share/dict/words --random-source<span style="color:#ce5c00;font-weight:bold">=</span>/dev/random <span style="color:#000;font-weight:bold">|</span> tr <span style="color:#4e9a06">&#39;\n&#39;</span> <span style="color:#4e9a06">&#39;-&#39;</span> <span style="color:#000;font-weight:bold">|</span> tr -d <span style="color:#4e9a06">&#34;&#39;&#34;</span> <span style="color:#000;font-weight:bold">|</span> cut -d<span style="color:#4e9a06">&#39;-&#39;</span> -f1,2,3,4,5,6,7<span style="color:#204a87;font-weight:bold">)</span> -</span></span><span style="display:flex;"><span>cat &gt; /home/ubuntu/cht/.env <span style="color:#4e9a06">&lt;&lt; EOF -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">CHT_COMPOSE_PROJECT_NAME=cht -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">COUCHDB_SECRET=${couchdb_secret} -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">COUCHDB_DATA=/home/ubuntu/cht/couchdb -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">COUCHDB_USER=medic -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">COUCHDB_PASSWORD=PASSWORD-FROM-ABOVE -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">COUCHDB_UUID=${uuid} -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">EOF</span> -</span></span></code></pre></div><p>Note that secure passwords and UUIDs were generated and saved in the resulting <code>.env</code> file.</p> -<h4 id="couchdb-node-1">CouchDB Node 1</h4> -<p>Create <code>/home/ubuntu/cht/docker-compose.yml</code> on Node 1 by running this code:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> /home/ubuntu/cht/ -</span></span><span style="display:flex;"><span>curl -s -o ./docker-compose.yml https://staging.dev.medicmobile.org/_couch/builds_4/medic:medic:4.3.1/docker-compose/cht-couchdb.yml -</span></span></code></pre></div><p>Now create the override file to have Node 1 join the <code>cht-net</code> overlay network we created above. As well, we&rsquo;ll set some <code>services:</code> specific overrides:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>cat &gt; /home/ubuntu/cht/cluster-overrides.yml <span style="color:#4e9a06">&lt;&lt; EOF -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">version: &#39;3.9&#39; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">services: -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> couchdb: -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> container_name: couchdb-1.local -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> environment: -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> - &#34;SVC_NAME=${SVC1_NAME:-couchdb-1.local}&#34; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> - &#34;CLUSTER_PEER_IPS=couchdb-2.local,couchdb-3.local&#34; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">networks: -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> cht-net: -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> driver: overlay -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> external: true -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">EOF</span> -</span></span></code></pre></div><h4 id="couchdb-node-2">CouchDB Node 2</h4> -<p>Like we did for Node 1, create <code>/home/ubuntu/cht/docker-compose.yml</code> and the <code>cluster-overrides.yml</code> file on Node 2 by running this code:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> /home/ubuntu/cht/ -</span></span><span style="display:flex;"><span>curl -s -o ./docker-compose.yml https://staging.dev.medicmobile.org/_couch/builds_4/medic:medic:4.3.1/docker-compose/cht-couchdb.yml -</span></span><span style="display:flex;"><span>cat &gt; /home/ubuntu/cht/cluster-overrides.yml <span style="color:#4e9a06">&lt;&lt; EOF -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">version: &#39;3.9&#39; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">services: -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> couchdb: -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> container_name: couchdb-2.local -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> environment: -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> - &#34;SVC_NAME=couchdb-2.local&#34; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> - &#34;COUCHDB_SYNC_ADMINS_NODE=${COUCHDB_SYNC_ADMINS_NODE:-couchdb-1.local}&#34; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">networks: -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> cht-net: -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> driver: overlay -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> external: true -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">EOF</span> -</span></span></code></pre></div><h4 id="couchdb-node-3">CouchDB Node 3</h4> -<p>Finally, we&rsquo;ll match Node 3 up with the others by running this code:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> /home/ubuntu/cht/ -</span></span><span style="display:flex;"><span>curl -s -o ./docker-compose.yml https://staging.dev.medicmobile.org/_couch/builds_4/medic:medic:4.3.1/docker-compose/cht-couchdb.yml -</span></span><span style="display:flex;"><span>cat &gt; /home/ubuntu/cht/cluster-overrides.yml <span style="color:#4e9a06">&lt;&lt; EOF -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">version: &#39;3.9&#39; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">services: -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> couchdb: -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> container_name: couchdb-3.local -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> environment: -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> - &#34;SVC_NAME=couchdb-3.local&#34; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> - &#34;COUCHDB_SYNC_ADMINS_NODE=${COUCHDB_SYNC_ADMINS_NODE:-couchdb-1.local}&#34; -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">networks: -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> cht-net: -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> driver: overlay -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06"> external: true -</span></span></span><span style="display:flex;"><span><span style="color:#4e9a06">EOF</span> -</span></span></code></pre></div><h2 id="starting-services">Starting Services</h2> -<h3 id="couchdb-nodes-1">CouchDB Nodes</h3> -<ol> -<li> -<p>On each of the three CouchDB nodes starting with node 3, then 2 then 1. Be sure to wait until <code>docker-compose</code> is finished running and has returned you to the command prompt before continuing to the next node:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> /home/ubuntu/cht -</span></span><span style="display:flex;"><span>docker compose -f docker-compose.yml -f cluster-overrides.yml up -d -</span></span></code></pre></div></li> -<li> -<p>Watch the logs and wait for everything to be up and running. You can run this on each node to watch the logs:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> /home/ubuntu/cht -</span></span><span style="display:flex;"><span>docker compose logs --follow -</span></span></code></pre></div><p>Nodes 2 and 3 should show output like <code>couchdb is ready</code> after node 1 has started.</p> -<p>Node 1 will show this when it has added all nodes:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>cht-couchdb-1.local-1 <span style="color:#000;font-weight:bold">|</span> <span style="color:#ce5c00;font-weight:bold">{</span><span style="color:#4e9a06">&#34;ok&#34;</span>:true<span style="color:#ce5c00;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span>cht-couchdb-1.local-1 <span style="color:#000;font-weight:bold">|</span> <span style="color:#ce5c00;font-weight:bold">{</span><span style="color:#4e9a06">&#34;all_nodes&#34;</span>:<span style="color:#ce5c00;font-weight:bold">[</span><span style="color:#4e9a06">&#34;couchdb@couchdb-1.local&#34;</span>,<span style="color:#4e9a06">&#34;couchdb@couchdb-2.local&#34;</span>,<span style="color:#4e9a06">&#34;couchdb@couchdb-3.local&#34;</span><span style="color:#ce5c00;font-weight:bold">]</span>,<span style="color:#4e9a06">&#34;cluster_nodes&#34;</span>:<span style="color:#ce5c00;font-weight:bold">[</span><span style="color:#4e9a06">&#34;couchdb@couchdb-1.local&#34;</span>,<span style="color:#4e9a06">&#34;couchdb@couchdb-2.local&#34;</span>,<span style="color:#4e9a06">&#34;couchdb@couchdb-3.local&#34;</span><span style="color:#ce5c00;font-weight:bold">]}</span> -</span></span></code></pre></div></li> -</ol> -<h3 id="cht-core">CHT Core</h3> -<p>Now that CouchDB is running on all the nodes, start the CHT Core:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> /home/ubuntu/cht/upgrade-service/ -</span></span><span style="display:flex;"><span>docker compose -f docker-compose.yml -f ../compose/cluster-overrides.yml up -d -</span></span></code></pre></div><p>To follow the progress tail the log of the upgrade service container by running:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> /home/ubuntu/cht/upgrade-service/ -</span></span><span style="display:flex;"><span>docker compose logs --follow -</span></span></code></pre></div><p>To make sure everything is running correctly, call <code>docker ps</code> and make sure that 6 CHT containers show:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES -</span></span><span style="display:flex;"><span>8c1c22d526f3 public.ecr.aws/s5s3h4s7/cht-nginx:4.0.1-4.0.1 <span style="color:#4e9a06">&#34;/docker-entrypoint.…&#34;</span> <span style="color:#0000cf;font-weight:bold">17</span> minutes ago Up <span style="color:#0000cf;font-weight:bold">8</span> minutes 0.0.0.0:80-&gt;80/tcp, :::80-&gt;80/tcp, 0.0.0.0:443-&gt;443/tcp, :::443-&gt;443/tcp cht_nginx_1 -</span></span><span style="display:flex;"><span>f7b596be2721 public.ecr.aws/s5s3h4s7/cht-api:4.0.1-4.0.1 <span style="color:#4e9a06">&#34;/bin/bash /api/dock…&#34;</span> <span style="color:#0000cf;font-weight:bold">17</span> minutes ago Up <span style="color:#0000cf;font-weight:bold">8</span> minutes 5988/tcp cht_api_1 -</span></span><span style="display:flex;"><span>029cd86ac721 public.ecr.aws/s5s3h4s7/cht-sentinel:4.0.1-4.0.1 <span style="color:#4e9a06">&#34;/bin/bash /sentinel…&#34;</span> <span style="color:#0000cf;font-weight:bold">17</span> minutes ago Up <span style="color:#0000cf;font-weight:bold">8</span> minutes cht_sentinel_1 -</span></span><span style="display:flex;"><span>61ee1e0b377b public.ecr.aws/s5s3h4s7/cht-haproxy-healthcheck:4.0.1-4.0.1 <span style="color:#4e9a06">&#34;/bin/sh -c \&#34;/app/ch…&#34;</span> <span style="color:#0000cf;font-weight:bold">17</span> minutes ago Up <span style="color:#0000cf;font-weight:bold">8</span> minutes cht_healthcheck_1 -</span></span><span style="display:flex;"><span>87415a2d91ea public.ecr.aws/s5s3h4s7/cht-haproxy:4.0.1-4.0.1 <span style="color:#4e9a06">&#34;/entrypoint.sh -f /…&#34;</span> <span style="color:#0000cf;font-weight:bold">17</span> minutes ago Up <span style="color:#0000cf;font-weight:bold">8</span> minutes 5984/tcp cht_haproxy_1 cht_couchdb_1 -</span></span><span style="display:flex;"><span>d01343658f3f public.ecr.aws/s5s3h4s7/cht-upgrade-service:latest <span style="color:#4e9a06">&#34;node /app/src/index…&#34;</span> <span style="color:#0000cf;font-weight:bold">17</span> minutes ago Up <span style="color:#0000cf;font-weight:bold">8</span> minutes upgrade-service-cht-upgrade-service-1 -</span></span></code></pre></div><p>This should show related to the CHT core are running</p> -<ul> -<li>cht_nginx</li> -<li>cht_api</li> -<li>cht_sentinel</li> -<li>cht_healthcheck</li> -<li>cht_haproxy</li> -<li>cht-upgrade-service</li> -</ul> -<p>Take note of the <code>STATUS</code> column and make sure no errors are displayed. If any container is restarting or mentioning any other error, check the logs using the <code>docker logs &lt;container-name&gt;</code> command.</p> -<p>If all has gone well, <code>nginx</code> should now be listening at both port 80 and port 443. Port 80 has a permanent redirect to port 443, so you can only access the CHT using https.</p> -<p>To login as the medic user in the web app, you can find your password with this command:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>grep COUCHDB_PASSWORD /home/ubuntu/cht/upgrade-service/.env <span style="color:#000;font-weight:bold">|</span> cut -d<span style="color:#4e9a06">&#39;=&#39;</span> -f2 -</span></span></code></pre></div><h2 id="upgrades">Upgrades</h2> -<p>Upgrades are completely manual for the clustered setup right now. You have to go into each of the docker compose files and modify the image tag and take containers down and restart them.</p> -<h3 id="upgrading-the-cht-upgrade-service">Upgrading the cht-upgrade-service</h3> -<p>The <a href="https://github.com/medic/cht-upgrade-service">CHT Upgrade Service</a> provides an interface between the CHT Core API and Docker to allow easy startup and one-click upgrades from the CHT Admin UI. Occasionally, the CHT Upgrade Service, itself, will need to be upgraded. If an upgrade is available, it is highly recommended that you install the upgrade for the CHT Upgrade Service before performing further upgrades on your CHT instance. This is done via the following steps:</p> -<ol> -<li>Verify that the <em>version</em> of the <code>cht-upgrade-service</code> image in your <code>./upgrade-service/docker-compose.yml</code> files is set to <code>latest</code>.</li> -<li>Pull the latest <code>cht-upgrade-service</code> image from Docker Hub and replace the current container by running the following command: -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> /home/ubuntu/cht/upgrade-service -</span></span><span style="display:flex;"><span>docker compose pull -</span></span><span style="display:flex;"><span>docker compose up --detach -</span></span></code></pre></div></li> -</ol> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Upgrading the CHT Upgrade Service will not cause a new CHT version to be installed. The CHT Core and CouchDB containers are not affected. -</div> -<p>Follow the <a href="https://forum.communityhealthtoolkit.org/c/product/releases/26">Product Releases channel</a> on the CHT forum to stay informed about new releases and upgrades.</p>Hosting: Self Hosting in CHT 4.x - Multiple CouchDB Nodes on k3s on VMWarehttps://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/self-hosting-k3s-multinode/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/self-hosting-k3s-multinode/ -<div class="pageinfo pageinfo-primary"> -<p>This page covers an example k3s cluster setup on a VMware datacenter with vSphere 7+ for a national deployment across 50 counties capable of supporting 20,000+ CHWs concurrently. After setup, administrators should only add VMs to the cluster or deploy CHT Core projects to be orchestrated.</p> -</div> -<h3 id="about-container-orchestration">About container orchestration</h3> -<p>A container orchestrator helps easily allocate hardware resources spread across a datacenter. For national scale projects, or a deployments with a large number of CHT Core instances, Medic recommends a lightweight Kubernetes orchestrator called <a href="https://docs.k3s.io/">k3s</a>. The orchestrator will:</p> -<ul> -<li>monitor resources across a group of virtual machines (aka &ldquo;nodes&rdquo;)</li> -<li>place CHT Core projects where there is available resource</li> -<li>migrate projects to spare resources if combined utilization is high or there are underlying issues.</li> -</ul> -<p>Instead of provisioning one VM per CHT Core project, we will provision larger VMs and deploy multiple CHT Core projects on one VM, with each project receiving optional resource limitations, like CPU and RAM.</p> -<p>In this example an orchestrator is deploying 50 CHT Core projects, one for each county. We will provision 9 large VMs and place 6 CHT Core projects on each VM. This allows for spare resources for failovers and lets the orchestrator decide on which VM projects live. Further, we get automated efficient use of datacenter resource utilization and avoids future manual allocations.</p> -<h3 id="nodes">Nodes</h3> -<p>We&rsquo;ll be using two types of k3s nodes in this deployment:</p> -<ul> -<li> -<p><a href="https://docs.k3s.io/installation/requirements#cpu-and-memory">HA control-plane</a> nodes - these enable high availability (HA) and provide access to kube API. These are containers running inside <code>kube-system</code> namespace which are often associated with the control-plane. They include coreDNS, traefik (ingress), servicelb, VMware Cloud Provisioner Interface (CPI), and VMWare Container Storage Interface (CSI)</p> -</li> -<li> -<p>Agent or worker nodes - these run the CHT Core containers and projects. They will also run services that tie in networking and storage. VMware CSI-node will be running here which enables agents to mount volumes from VMware Virtual-SAN for block data storage. Agents will also run servicelb-traefik containers which allow the nodes to route traffic to correct projects and handle load-balancing and internal networking.</p> -</li> -</ul> -<h2 id="prerequisites">Prerequisites</h2> -<h3 id="servers--virtual-machines">Servers / Virtual Machines</h3> -<p>Provision 3 Ubuntu servers (22.04 as of this writing) that meet k3s specifications for <a href="https://docs.k3s.io/installation/requirements#cpu-and-memory">HA etcd</a></p> -<p>As we&rsquo;re provisioning an example deployment here for 50 counties and over 20,000 CHWs, the RAM, CPU and storage numbers will differ for you specific deployment.</p> -<p>To support all 50 counties, provision 3 Ubuntu servers (22.04 as of this writing) with <strong>4 vCPU and 8GB Ram</strong>. Ensure they also meet k3s specifications for <a href="https://docs.k3s.io/installation/requirements#cpu-and-memory">HA etcd</a>.</p> -<p>Provision 9 Ubuntu servers (again 22.04 as of this writing) for your k3s agent/worker servers. Each should have <strong>48 vCPU, 192 GB Ram, and 50gb local storage</strong>.</p> -<p>For any additional VMs you add to the k3s cluster, you will need to ensure networking, roles, and extra configuration parameters that are noted below are configured on the VM.</p> -<p>To ensure your hardware is not over-provisioned, add more VMs to your k3s cluster when you want to deploy more CHT Core projects. This gives you flexibility of not needing to provision them initially as they can easily be added later.</p> -<h3 id="network">Network</h3> -<p>Ensure the above provisioned VMs:</p> -<ul> -<li>abide by <a href="https://docs.k3s.io/installation/requirements#inbound-rules-for-k3s-server-nodes">Inbound Rules for k3s Server Nodes</a></li> -<li>If you&rsquo;re using Ubuntu&rsquo;s ufw, follow <a href="https://docs.k3s.io/advanced#ubuntu--debian">firewall considerations for k3s on Ubuntu</a></li> -<li>are restricted to the IP addresses of the k3s nodes so only they can connect to the service ports</li> -</ul> -<h3 id="add-roles-and-permissions-to-our-vms">Add Roles and Permissions to our VMs</h3> -<p>Following the <a href="https://docs.vmware.com/en/VMware-vSphere-Container-Storage-Plug-in/2.0/vmware-vsphere-csp-getting-started/GUID-0AB6E692-AA47-4B6A-8CEA-38B754E16567.html#GUID-0AB6E692-AA47-4B6A-8CEA-38B754E16567">vSphere docs</a>, first create the following vSphere roles in vSphere for Container Storage (CSN):</p> -<ul> -<li>CNS-VM</li> -<li>CNS-DATASTORE</li> -<li>CNS-SEARCH-AND-SPBM</li> -</ul> -<p>Now, on the VM settings, we can apply these roles as described in the above document.</p> -<p>Any provisioned VM in the previous step, should receive CNS-VM role. -The top-level vCenter server will receive CNS-SEARCH-AND-SPBM role. -Virtual-SAN should receive CNS-DATASTORE. -And all servers should have the READONLY role (this may already be active)</p> -<h3 id="enable-necessary-extra-parameters-on-all-vms">Enable Necessary Extra Parameters on all VMs</h3> -<p>Following along the above document, we want to verify VM Hardware Version is 15 or greater, and that disk.EnableUUID parameter is configured.</p> -<p>On each node, through vSphere Client (GUI):</p> -<ol> -<li> -<p>disk.EnableUUID</p> -<ol> -<li>In the vSphere Client, right-click the VM and select Edit Settings.</li> -<li>Click the VM Options tab and expand the Advanced menu.</li> -<li>Click Edit Configuration next to Configuration Parameters.</li> -<li>Configure the disk.EnableUUID parameter. If the parameter exists, make sure that its value is set to True. If the parameter is not present, add it and set its value to True.</li> -</ol> -</li> -<li> -<p>Verify VM hardware version at 15 or higher, and upgrade if necessary</p> -<ol> -<li>In the vSphere Client, navigate to the virtual machine.</li> -<li>Select Actions &gt; Compatibility &gt; Upgrade VM Compatibility.</li> -<li>Click Yes to confirm the upgrade.</li> -<li>Select a compatibility and click OK.</li> -</ol> -</li> -<li> -<p>Add VMware Paravirtual SCSI storage controller to the VM</p> -<ol> -<li>In the vSphere Client, right-click the VM and select Edit Settings.</li> -<li>On the Virtual Hardware tab, click the Add New Device button.</li> -<li>Select SCSI Controller from the drop-down menu.</li> -<li>Expand New SCSI controller and from the Change Type menu, select VMware Paravirtual.</li> -<li>Click OK.</li> -</ol> -</li> -</ol> -<h3 id="identify-vsphere-provider-ids-node-ids-and-datacenter-name">Identify vSphere Provider IDs, Node IDs, and datacenter name</h3> -<p>Bootstrap parameters for k3s on VMware require UUID identification of each node that will join the cluster.</p> -<p>For each of the provisioned VMs, you can navigate to the VM in vCenter interface and retrieve the UUID.</p> -<p>Another method is to make the following calls to vCenter Server API. You may have a VPN that you connect to first before being able to access your vCenter GUI. These commands should be run from the same network that allows that access.</p> -<p>When running the commands below, be sure to replace the placeholders with your own values:</p> -<ul> -<li><code>&lt;vCenter_IP&gt;</code></li> -<li><code>&lt;USERNAME&gt;</code></li> -<li><code>&lt;PASSWORD&gt;</code></li> -<li><code>&lt;UUID_FROM_vCENTER&gt;</code></li> -</ul> -<p>And any others as well!</p> -<ul> -<li> -<p>Get an authentication-token:</p> -<pre tabindex="0"><code>curl -k -X POST https://&lt;vCenter_IP&gt;/rest/com/vmware/cis/session -u &#39;&lt;USERNAME&gt;:&lt;PASSWORD&gt;&#39; -ID=&lt;UUID_FROM_vCENTER&gt; -</code></pre></li> -<li> -<p>List all your VMs and identify the VM-number that was provisioned earlier:</p> -<pre tabindex="0"><code>curl -k -X GET -H &#34;vmware-api-session-id: $ID&#34; https://&lt;vCenter_IP&gt;/api/vcenter/vm -</code></pre></li> -<li> -<p>Retrieve your instance_uuid by first making a <code>curl</code> call:</p> -<pre tabindex="0"><code>curl -k -X GET -H &#34;vmware-api-session-id: $ID&#34; https://&lt;vCenter_IP&gt;/api/vcenter/vm/vm-&lt;number&gt; -</code></pre></li> -<li> -<p>Inside the JSON response of the <code>curl</code> call get the, <code>instance_uuid</code>, in this case it&rsquo;s <code>215cc603-e8da-5iua-3333-a2402c05121</code>, but yours will be different:</p> -<pre tabindex="0"><code>&#34;identity&#34;:{&#34;name&#34;:&#34;k3s_worker_node_4&#34;,&#34;instance_uuid&#34;:&#34;215cc603-e8da-5iua-3333-a2402c05121&#34; -</code></pre></li> -<li> -<p>Retrieve your datacenter name, to be used in configuration files for VMware CSI and CPI</p> -<pre tabindex="0"><code>curl -k -X GET -H &#34;vmware-api-session-id: $ID&#34; https://&lt;vCenter_IP&gt;/rest/vcenter/datacenter -</code></pre></li> -</ul> -<p>You will want to save the &ldquo;name&rdquo; of your datacenter.</p> -<ul> -<li>Retrieve your cluster-id, to be used in config file for VMware CSI -<pre tabindex="0"><code>curl -k -X GET -H &#34;vmware-api-session-id: $ID&#34; https://&lt;vCenter IP&gt;/api/vcenter/cluster -</code></pre></li> -</ul> -<p>You can also use the <a href="https://github.com/vmware/govmomi/blob/main/govc/README.md#binaries">govc cli tool</a> to retrieve this information:</p> -<pre tabindex="0"><code>export GOVC_INSECURE=1 -export GOVC_URL=&#39;https://&lt;USERNAME&gt;:&lt;PASSWORD&gt;@&lt;vCenter_IP&gt; -govc ls / -&lt;datacenter-name&gt;/vm \ -&lt;datacenter-name&gt;/network \ -&lt;datacenter-name&gt;/host \ -&lt;datacenter-name&gt;/datastore -#To retrieve all Node VMs -govc ls /&lt;datacenter-name&gt;/vm \ -&lt;datacenter-name&gt;/vm/&lt;vm-name1&gt; \ -&lt;datacenter-name&gt;/vm/&lt;vm-name2&gt; \ -&lt;datacenter-name&gt;/vm/&lt;vm-name3&gt; \ -&lt;datacenter-name&gt;/vm/&lt;vm-name4&gt; \ -&lt;datacenter-name&gt;/vm/&lt;vm-name5&gt; -</code></pre><h2 id="install-k3s">Install k3s</h2> -<h3 id="first-control-plane-vm">First Control-Plane VM</h3> -<p>SSH into your first control-plane VM that was provisioned and configured above and <a href="https://docs.docker.com/engine/install/ubuntu/">install docker</a>.</p> -<p>For k3s version compatibility with vCenter and vMware CPI/CSI, we will need to use k3s v1.25, cpi v1.25, and csi v2.7.2 per the <code>curl</code> call below.</p> -<p>Run the following CLI command inside the control-plane VM, filling out these two specific values:</p> -<ul> -<li><code>&lt;TOKEN&gt;</code>: Please generate a token ID, and save it. This will be required for the entirety of the k3s cluster existence and required to add additional servers to the k3s cluster</li> -<li><code>&lt;VM_UUID&gt;</code>: This was the UUID for this specific VM that we identified earlier</li> -</ul> -<pre tabindex="0"><code>curl -sfL https://get.k3s.io | K3S_KUBECONFIG_MODE=&#34;644&#34; \ -INSTALL_K3S_EXEC=&#34;server&#34; INSTALL_K3S_VERSION=&#34;v1.25.14+k3s1&#34; sh -s - \ ---docker --token &lt;TOKEN&gt; \ ---cluster-init --disable-cloud-controller \ ---kubelet-arg=&#34;cloud-provider=external&#34; \ ---kubelet-arg=&#34;provider-id=vsphere://&lt;VM_UUID&gt;&#34; -</code></pre><h3 id="second-and-third-control-plane-vms">Second and third Control-Plane VMs</h3> -<p>SSH into your second/third control-plane VM.</p> -<p>Please fill out these values below and run the cli command:</p> -<ul> -<li><code>&lt;TOKEN&gt;</code>: Required to be the same token you used in the first control-plane setup</li> -<li><code>&lt;CONTROL_PLANE_1_IP&gt;</code>: This is the IP of the first control-plane server you setup, and allows this second server to discover the initial one.</li> -<li><code>&lt;VM_UUID&gt;</code>: This is the UUID for this second VM that we identified earlier. This will be different than the one you used for control plane 1.</li> -</ul> -<pre tabindex="0"><code>curl -sfL https://get.k3s.io | K3S_KUBECONFIG_MODE=&#34;644&#34; \ -INSTALL_K3S_EXEC=&#34;server&#34; INSTALL_K3S_VERSION=&#34;v1.25.14+k3s1&#34; sh -s \ ---docker --token &lt;TOKEN&gt; \ ---server https://&lt;CONTROL_PLANE_1_IP:6443 \ ---disable-cloud-controller --kubelet-arg=&#34;cloud-provider=external&#34; \ ---kubelet-arg=&#34;provider-id=vsphere://&lt;VM_UUID&gt;&#34; -</code></pre><p>You can verify your cluster is working by running this command from inside your control plane VM:</p> -<pre tabindex="0"><code>/usr/local/bin/k3s kubectl get nodes -o wide -</code></pre><h3 id="agentworker-vms">Agent/Worker VMs</h3> -<p>Now we will add our k3s agent/worker servers that will handle cht-core projects, workloads, and containers. This process is the same for any additional Agent/Worker servers you want to add to your k3s cluster.</p> -<p>Ensure that the appropriate roles, and extra configuration parameters are set correctly.</p> -<p>Please fill out these values before running the command:</p> -<ul> -<li><code>&lt;TOKEN&gt;</code>: Required to be the same token you used above</li> -<li><code>&lt;CONTROL_PLANE_IP&gt;</code>: The IP of one of the control plane servers you set up above</li> -<li><code>&lt;VM_UUID&gt;</code>: This is the UUID of this VM that we are adding as an agent/worker server in our k3s cluster</li> -</ul> -<pre tabindex="0"><code>curl -sfL https://get.k3s.io | K3S_KUBECONFIG_MODE=&#34;644&#34; \ -INSTALL_K3S_EXEC=&#34;agent&#34; INSTALL_K3S_VERSION=&#34;v1.25.14+k3s1&#34; sh -s - \ ---docker --token &lt;TOKEN&gt; \ ---server https://&lt;CONTROL_PLANE_IP&gt;:6443 \ ---kubelet-arg=&#34;cloud-provider=external&#34; \ ---kubelet-arg=&#34;provider-id=vsphere://&lt;VM_UUID&gt;&#34; -</code></pre><h2 id="deploy-vmware-cloud-provisioner-interface-cpi-to-your-k3s-cluster">Deploy VMware Cloud Provisioner Interface (CPI) to your k3s cluster</h2> -<p>SSH into one of your control plane servers. -Download the template for CPI, ensure you are aware of your current working directory. This will be the location where the CPI template is saved.</p> -<pre tabindex="0"><code>pwd -wget https://raw.githubusercontent.com/kubernetes/cloud-provider-vsphere/release-1.25/releases/v1.25/vsphere-cloud-controller-manager.yaml -</code></pre><p>Modify the vsphere-cloud-controller-manager.yaml file downloaded above and update vCenter Server information.</p> -<ol> -<li> -<p>Add your <code>&lt;vCenter_IP&gt;</code> and <code>&lt;USERNAME&gt;</code>, <code>&lt;PASSWORD&gt;</code> to the section below inside that yaml:</p> -<pre tabindex="0"><code>apiVersion: v1 -kind: Secret -metadata: -name: vsphere-cloud-secret -labels: -vsphere-cpi-infra: secret -component: cloud-controller-manager -namespace: kube-system -# NOTE: this is just an example configuration, update with real values based on your environment -stringData: -&lt;vCenter_IP&gt;.username: &#34;&lt;USERNAME&gt;&#34; -&lt;vCenter_IP&gt;.password: &#34;&lt;PASSWORD&gt;&#34; -</code></pre></li> -<li> -<p>Please add your <code>&lt;vCenter_IP&gt;</code> and <code>&lt;USERNAME&gt;</code>, <code>&lt;PASSWORD&gt;</code> and <code>&lt;Datacenter_name_retrieved_earlier&gt;</code> to the ConfigMap section inside that yaml.</p> -<p><strong>Note:</strong> If your vCenter actively uses https with valid certificates, then inside the <code>global:</code> stanza, you will want to set <code>insecureFlag: false</code>. Most set-ups will want this to remain true with<code>insecureFlag: true</code> .</p> -<pre tabindex="0"><code>apiVersion: v1 -kind: ConfigMap -metadata: -name: vsphere-cloud-config -labels: -vsphere-cpi-infra: config -component: cloud-controller-manager -namespace: kube-system -data: -# NOTE: this is just an example configuration, update with real values based on your environment -vsphere.conf: | -# Global properties in this section will be used for all specified vCenters unless overridden in VirtualCenter section. -global: -port: 443 -# set insecureFlag to true if the vCenter uses a self-signed cert -insecureFlag: true -# settings for using k8s secret -secretName: vsphere-cloud-secret -secretNamespace: kube-system -# vcenter section -vcenter: -my-vc-name: -server: &lt;vCenter_IP&gt; -user: &lt;USERNAME&gt; -password: &lt;PASSWORD&gt; -datacenters: -- &lt;Datacenter_name_retrieved_earlier&gt; -</code></pre></li> -<li> -<p>Deploy the template!</p> -<pre tabindex="0"><code>/usr/local/bin/k3s kubectl -n kube-system apply -f vsphere-cloud-controller-manager.yaml -</code></pre></li> -<li> -<p>Verify CPI containers are running:</p> -<pre tabindex="0"><code>/usr/local/bin/k3s kubectl -n kube-system get pods -o wide -/usr/local/bin/k3s kubectl -n kube-system logs vsphere-cloud-controller-manager-&lt;id&gt; -</code></pre></li> -</ol> -<p>You will see 3 vsphere-cloud-controller-manager pods running, one per control-plane server.</p> -<p>Take a peak at all 3 vsphere-controller-manager pods logs to ensure nothing is immediately erring. Common errors are using the incorrect datacenter name, UUIDs for VMs in the k3s curl command, or invalid credentials in the configmap and secrets resources created in step 2 above. If one of these errors is displaying in the log, you will want to delete the deployment (in step 3 above, replace <code>apply</code> with <code>delete</code>, edit the yaml and re-deploy (run step 3 again).</p> -<h2 id="deploy-vmware-container-storage-interface-csi-to-your-k3s-cluster">Deploy VMware Container Storage Interface (CSI) to your k3s cluster</h2> -<p>Follow the <a href="https://docs.vmware.com/en/VMware-vSphere-Container-Storage-Plug-in/2.0/vmware-vsphere-csp-getting-started/GUID-A1982536-F741-4614-A6F2-ADEE21AA4588.html">VMware documentation for CSI</a> with these steps:</p> -<ol> -<li> -<p>Run the following command from inside a control-plane server:</p> -<pre tabindex="0"><code>/usr/local/bin/k3s kubectl create namespace vmware-system-csi -</code></pre></li> -<li> -<p>Taint your control-lane node servers by running the following command. This taint may already exist, if so, that&rsquo;s okay. Please replace <code>&lt;CONTROL_PLANE_SERVER&gt;</code> with each of your control plane servers.</p> -<pre tabindex="0"><code>You can retrieve the names by running `/usr/local/bin/k3s kubectl get nodes -o wide` -/usr/local/bin/k3s kubectl taint node &lt;CONTROL_PLANE_SERVER&gt; node-role.kubernetes.io/control-plane=:NoSchedule -</code></pre></li> -<li> -<p>Create kubernetes secret, which will map authentication credentials and datacenter name to CSI containers. First, create a file <code>/etc/kubernetes/csi-vsphere.conf</code>. Be sure to replace <code>&lt;vCenter_IP&gt;</code>, <code>&lt;USERNAME&gt;</code>, <code>&lt;PASSWORD&gt;</code> , <code>&lt;true_or_false&gt;</code>, <code>&lt;PORT&gt;</code> , <code>&lt;datacenter1-path&gt;</code> and <code>&lt;datacenter1-path&gt;</code> with your values:</p> -<pre tabindex="0"><code>[Global] -cluster-id = &#34;&lt;cluster-id&gt;&#34; -[VirtualCenter &#34;&lt;vCenter_IP&gt;&#34;] -insecure-flag = &#34;&lt;true_or_false&gt;&#34; -user = &#34;&lt;USERNAME&gt;&#34; -password = &#34;&lt;PASSWORD&gt;&#34; -port = &#34;&lt;PORT&gt;&#34; -datacenters = &#34;&lt;datacenter1-path&gt;, &lt;datacenter2-path&gt;, ...&#34; -</code></pre></li> -<li> -<p>Create the secret resource in the namespace we created in step 1 by running the following command in the same directory you created the csi-vsphere.conf file:</p> -<pre tabindex="0"><code>/usr/local/bin/k3s kubectl create secret generic vsphere-config-secret --from-file=csi-vsphere.conf --namespace=vmware-system-csi -</code></pre></li> -<li> -<p>Download the <a href="https://raw.githubusercontent.com/kubernetes-sigs/vsphere-csi-driver/v2.7.2/manifests/vanilla/vsphere-csi-driver.yaml">vSphere CSI v2.7.2 template</a></p> -</li> -</ol> -<p>There is one minor edit, typically found on line 217-218, under the deployment specification for vsphere-csi-controller.</p> -<p>Before edit (original value)</p> -<pre tabindex="0"><code> nodeSelector: -node-role.kubernetes.io/control-plane: &#34;&#34; -</code></pre><p>Please add <code>true</code> as the value for this key, seen below:</p> -<pre tabindex="0"><code> nodeSelector: -node-role.kubernetes.io/control-plane: &#34;true&#34; -</code></pre><p>Now, let&rsquo;s deploy VMware CSI by running the following command:</p> -<pre tabindex="0"><code>/usr/local/bin/k3s kubectl -n vmware-system-csi apply -f vsphere-csi-driver.yaml -</code></pre><p>Follow the <a href="https://docs.vmware.com/en/VMware-vSphere-Container-Storage-Plug-in/2.0/vmware-vsphere-csp-getting-started/GUID-54BB79D2-B13F-4673-8CC2-63A772D17B3C.html">verification steps seen here in Step 2 of Procedure</a></p> -<h3 id="create-storageclass-in-k3s-cluster">Create StorageClass in k3s cluster</h3> -<p>We&rsquo;ll need to create a global <a href="https://kubernetes.io/docs/concepts/storage/storage-classes/">StorageClass</a> resource in our k3s cluster, so CHT Core deployments will be able to ask for persistent storage volumes from the k3s cluster.</p> -<p>Inside one of the control-plane servers, please create a file <code>vmware-storageclass.yaml</code> with the following contents:</p> -<pre tabindex="0"><code>kind: StorageClass -apiVersion: storage.k8s.io/v1 -metadata: -name: vmware-sc -annotations: -storageclass.kubernetes.io/is-default-class: &#34;true&#34; -provisioner: csi.vsphere.vmware.com -parameters: -csi.storage.k8s.io/fstype: &#34;ext4&#34; #Optional Parameter -</code></pre><p>Deploy this template to the k3s cluster via:</p> -<pre tabindex="0"><code>/usr/local/bin/k3s kubectl apply -f vmware-storageclass.yaml -</code></pre><h2 id="deploying-a-cht-core-project-to-your-new-k3s-cluster-running-on-vmware">Deploying a CHT-Core Project to your new k3s Cluster running on VMware</h2> -<p>This step will neatly fit into helm chart configurations, but here are the manual steps for time being.</p> -<p>Your persistent volume (PVC) template for all CouchDB&rsquo;s should be as shown below. Note the <code>storageClassName</code> parameter should be identical to the <code>storageClass</code> we deployed earlier:</p> -<pre tabindex="0"><code># Source: cht-chart/templates/couchdb-n-claim0-persistentvolumeclaim.yaml -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: -labels: -cht.service: couchdb-1-claim0 -name: couchdb-1-claim0 -spec: -accessModes: -- ReadWriteOnce -resources: -requests: -storage: 4Gi -storageClassName: vmware-sc -status: {} -</code></pre><h2 id="kubernetes-concepts">Kubernetes Concepts</h2> -<p>Here are links to docs surrounding the kubernetes concepts that we use in a cht-core project deployed to a k3s cluster.</p> -<ul> -<li><a href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/">Deployment</a> - This is the main kubernetes resource that contains information regarding all the cht services that will be deployed.</li> -<li><a href="https://kubernetes.io/docs/concepts/configuration/configmap/">ConfigMaps</a> - This contains configuration files, or credentials that containers can retrieve. If you edit the configmap, you should delete containers, which will trigger a new container to download your new edits to any configurations for that service</li> -<li><a href="https://kubernetes.io/docs/concepts/security/service-accounts/">ServiceAccounts</a> - This is used by the upgrade-service that is running inside the cht-core pods (as a container titled upgrade-service). This serviceAccount restricts the upgrade-service from interacting with any other cht-core projects outside of its namespace, and gives the upgrade-service permissions to talk to kubernetes API to upgrade container images when a CHT ADMIN clicks <em>upgrade</em> through the Admin interface.</li> -<li><a href="https://kubernetes.io/docs/concepts/services-networking/ingress/">Ingress</a> - This is what forwards traffic to a particular project or pods. In most use-cases, there is an nginx deployed outside of the k3s cluster than contains DNS entries for existing projects, and contains a proxy_pass parameter to send traffic based on host header to any of the k3s server IPs. Inside the k3s cluster, the traefik container and servicelb-traefik containers in kube-system namespace will handle forwarding traffic to the correct cht-core containers based on url</li> -<li><a href="https://kubernetes.io/docs/concepts/storage/persistent-volumes/">Persistent Volume Claim</a> - This is where our project data will be stored. Important to ensure you have configured this correctly, with retain policies intact so the data is not deleted if the project is removed. It&rsquo;s also vital to ensure you have a backup policy either set-up in VMware vCenter GUI or you have configured the csi-snapshotter that comes with vSphere CSI.</li> -<li><a href="https://kubernetes.io/docs/concepts/services-networking/service/">Services</a> - This is utilized for CouchDB nodes to discover each other through DNS rather than internal IPs, which can change. This is also used in the COUCH_URL so API containers can discover where CouchDB is running.</li> -</ul>Hosting: Upgrading the cht-upgrade-servicehttps://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/_partial_upgrade_service/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/_partial_upgrade_service/ -<h3 id="upgrading-the-cht-upgrade-service">Upgrading the cht-upgrade-service</h3> -<p>The <a href="https://github.com/medic/cht-upgrade-service">CHT Upgrade Service</a> provides an interface between the CHT Core API and Docker to allow easy startup and one-click upgrades from the CHT Admin UI. Occasionally, the CHT Upgrade Service, itself, will need to be upgraded. If an upgrade is available, it is highly recommended that you install the upgrade for the CHT Upgrade Service before performing further upgrades on your CHT instance. This is done via the following steps:</p> -<ol> -<li>Verify that the <em>version</em> of the <code>cht-upgrade-service</code> image in your <code>./upgrade-service/docker-compose.yml</code> files is set to <code>latest</code>.</li> -<li>Pull the latest <code>cht-upgrade-service</code> image from Docker Hub and replace the current container by running the following command: -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> /home/ubuntu/cht/upgrade-service -</span></span><span style="display:flex;"><span>docker compose pull -</span></span><span style="display:flex;"><span>docker compose up --detach -</span></span></code></pre></div></li> -</ol> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Upgrading the CHT Upgrade Service will not cause a new CHT version to be installed. The CHT Core and CouchDB containers are not affected. -</div> -<p>Follow the <a href="https://forum.communityhealthtoolkit.org/c/product/releases/26">Product Releases channel</a> on the CHT forum to stay informed about new releases and upgrades.</p> \ No newline at end of file +Self Hosting in CHT 4.x on Community Health Toolkithttps://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/Recent content in Self Hosting in CHT 4.x on Community Health ToolkitHugo -- gohugo.ioenSelf Hosting in CHT 4.x - Single CouchDB Nodehttps://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/single-node/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/single-node/This for a single node CHT 4.x instance and is the recommended solution for small deployments. If you want a more powerful setup, check out the 4.x multi-node install docs. +Prerequisites Be sure you have followed the requirements document including installing Docker and Docker Compose. This guide assumes you&rsquo;re using the ubuntu user and that it has sudo-less access to Docker. +Directory Structure Create the following directory structure: +|-- /home/ubuntu/cht/ |-- compose/ |-- certs/ |-- couchdb/ |-- upgrade-service/ By calling this mkdir commands:Self Hosting in CHT 4.x - Multiple CouchDB Nodes on Docker Swarmhttps://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/multiple-nodes/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/multiple-nodes/The clustered multi-node hosting described below is recommended for deployments that need increased performance gains. These gains will increase the complexity of troubleshooting and decrease the ease ongoing maintenance. +If you are unsure which deployment to use check out Self-hosting recommendations. +About clustered deployments In a clustered CHT setup, there are multiple CouchDB nodes responding to users. The ability to horizontally scale a CHT instance was added in version CHT 4.Self Hosting in CHT 4.x - Multiple CouchDB Nodes on k3s on VMWarehttps://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/self-hosting-k3s-multinode/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/self-hosting-k3s-multinode/This page covers an example k3s cluster setup on a VMware datacenter with vSphere 7+ for a national deployment across 50 counties capable of supporting 20,000+ CHWs concurrently. After setup, administrators should only add VMs to the cluster or deploy CHT Core projects to be orchestrated. +About container orchestration A container orchestrator helps easily allocate hardware resources spread across a datacenter. For national scale projects, or a deployments with a large number of CHT Core instances, Medic recommends a lightweight Kubernetes orchestrator called k3s.Upgrading the cht-upgrade-servicehttps://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/_partial_upgrade_service/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/_partial_upgrade_service/Upgrading the cht-upgrade-service The CHT Upgrade Service provides an interface between the CHT Core API and Docker to allow easy startup and one-click upgrades from the CHT Admin UI. Occasionally, the CHT Upgrade Service, itself, will need to be upgraded. If an upgrade is available, it is highly recommended that you install the upgrade for the CHT Upgrade Service before performing further upgrades on your CHT instance. This is done via the following steps: \ No newline at end of file diff --git a/hosting/4.x/self-hosting/multiple-nodes/index.html b/hosting/4.x/self-hosting/multiple-nodes/index.html index fd5511cce7..54670b455a 100644 --- a/hosting/4.x/self-hosting/multiple-nodes/index.html +++ b/hosting/4.x/self-hosting/multiple-nodes/index.html @@ -1,9 +1,9 @@ -Self Hosting in CHT 4.x - Multiple CouchDB Nodes on Docker Swarm | Community Health Toolkit +Self Hosting in CHT 4.x - Multiple CouchDB Nodes on Docker Swarm | Community Health Toolkit

    Self Hosting in CHT 4.x - Multiple CouchDB Nodes on Docker Swarm

    Hosting the CHT on self run infrastructure with horizontally scaled CouchDB nodes

    The clustered multi-node hosting described below is recommended for deployments that need increased performance gains. These gains will increase the complexity of troubleshooting and decrease the ease ongoing maintenance.

    If you are unsure which deployment to use check out Self-hosting recommendations.

    About clustered deployments

    In a clustered CHT setup, there are multiple CouchDB nodes responding to users. The ability to horizontally scale a CHT instance was added in version CHT 4.0.0. In this document we set up a three node CouchDB cluster. We require all three CouchDB nodes to be running and healthy before installing the CHT. Our healthcheck service determines the health of the CouchDB nodes and turns off the CHT if any single node is not functional.

    Nodes

    • CHT Core (1x) - Core functionality of the CHT including API and sentinel
    • CouchDB (3x) - 3 node CouchDB cluster

    Prerequisites

    Servers

    Provision four Ubuntu servers (22.04 as of this writing) that meet our hosting requirements including installing Docker and Docker Compose on all of them. This guide assumes you’re using the ubuntu user, with a home directory of /home/ubuntu and that it has sudo-less access to Docker.

    Network

    Make sure the following ports are open for all nodes:

    • 7946 TCP/UDP - For Docker communication amongst nodes
    • 2377 TCP - Docker cluster management communication
    • 4789 UDP - Docker overlay network traffic
    • ICMP - For ping

    As a security measure, be sure to restrict the IP addresses of the four nodes only to be able to connect to these ports.

    Create an Overlay Network

    To set up a private network that only the four nodes can use, we’ll use docker swarm’s overlay network feature. You’ll first need to initialize the swarm on the CHT Core node and then join the swarm on each of the three CouchDB nodes.

    CHT Core node

    Initialize swarm mode by running:

    docker swarm init
    + Create project issue

    Self Hosting in CHT 4.x - Multiple CouchDB Nodes on Docker Swarm

    Hosting the CHT on self run infrastructure with horizontally scaled CouchDB nodes

    The clustered multi-node hosting described below is recommended for deployments that need increased performance gains. These gains will increase the complexity of troubleshooting and decrease the ease ongoing maintenance.

    If you are unsure which deployment to use check out Self-hosting recommendations.

    About clustered deployments

    In a clustered CHT setup, there are multiple CouchDB nodes responding to users. The ability to horizontally scale a CHT instance was added in version CHT 4.0.0. In this document we set up a three node CouchDB cluster. We require all three CouchDB nodes to be running and healthy before installing the CHT. Our healthcheck service determines the health of the CouchDB nodes and turns off the CHT if any single node is not functional.

    Nodes

    • CHT Core (1x) - Core functionality of the CHT including API and sentinel
    • CouchDB (3x) - 3 node CouchDB cluster

    Prerequisites

    Servers

    Provision four Ubuntu servers (22.04 as of this writing) that meet our hosting requirements including installing Docker and Docker Compose on all of them. This guide assumes you’re using the ubuntu user, with a home directory of /home/ubuntu and that it has sudo-less access to Docker.

    Network

    Make sure the following ports are open for all nodes:

    • 7946 TCP/UDP - For Docker communication amongst nodes
    • 2377 TCP - Docker cluster management communication
    • 4789 UDP - Docker overlay network traffic
    • ICMP - For ping

    As a security measure, be sure to restrict the IP addresses of the four nodes only to be able to connect to these ports.

    Create an Overlay Network

    To set up a private network that only the four nodes can use, we’ll use docker swarm’s overlay network feature. You’ll first need to initialize the swarm on the CHT Core node and then join the swarm on each of the three CouchDB nodes.

    CHT Core node

    Initialize swarm mode by running:

    docker swarm init
     

    This will output:

    1
     2
     3
    @@ -436,7 +436,8 @@
     docker compose pull
     docker compose up --detach
     

    Follow the Product Releases channel on the CHT forum to stay informed about new releases and upgrades.

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/hosting/4.x/self-hosting/self-hosting-k3s-multinode/index.html b/hosting/4.x/self-hosting/self-hosting-k3s-multinode/index.html index 103ea7be98..58356fe3be 100644 --- a/hosting/4.x/self-hosting/self-hosting-k3s-multinode/index.html +++ b/hosting/4.x/self-hosting/self-hosting-k3s-multinode/index.html @@ -1,9 +1,9 @@ -Self Hosting in CHT 4.x - Multiple CouchDB Nodes on k3s on VMWare | Community Health Toolkit +Self Hosting in CHT 4.x - Multiple CouchDB Nodes on k3s on VMWare | Community Health Toolkit

    Self Hosting in CHT 4.x - Multiple CouchDB Nodes on k3s on VMWare

    Hosting the CHT on self run VMware infrastructure for multiple CHT-Core projects that utilize horizontally scaled CouchDB nodes

    This page covers an example k3s cluster setup on a VMware datacenter with vSphere 7+ for a national deployment across 50 counties capable of supporting 20,000+ CHWs concurrently. After setup, administrators should only add VMs to the cluster or deploy CHT Core projects to be orchestrated.

    About container orchestration

    A container orchestrator helps easily allocate hardware resources spread across a datacenter. For national scale projects, or a deployments with a large number of CHT Core instances, Medic recommends a lightweight Kubernetes orchestrator called k3s. The orchestrator will:

    • monitor resources across a group of virtual machines (aka “nodes”)
    • place CHT Core projects where there is available resource
    • migrate projects to spare resources if combined utilization is high or there are underlying issues.

    Instead of provisioning one VM per CHT Core project, we will provision larger VMs and deploy multiple CHT Core projects on one VM, with each project receiving optional resource limitations, like CPU and RAM.

    In this example an orchestrator is deploying 50 CHT Core projects, one for each county. We will provision 9 large VMs and place 6 CHT Core projects on each VM. This allows for spare resources for failovers and lets the orchestrator decide on which VM projects live. Further, we get automated efficient use of datacenter resource utilization and avoids future manual allocations.

    Nodes

    We’ll be using two types of k3s nodes in this deployment:

    • HA control-plane nodes - these enable high availability (HA) and provide access to kube API. These are containers running inside kube-system namespace which are often associated with the control-plane. They include coreDNS, traefik (ingress), servicelb, VMware Cloud Provisioner Interface (CPI), and VMWare Container Storage Interface (CSI)

    • Agent or worker nodes - these run the CHT Core containers and projects. They will also run services that tie in networking and storage. VMware CSI-node will be running here which enables agents to mount volumes from VMware Virtual-SAN for block data storage. Agents will also run servicelb-traefik containers which allow the nodes to route traffic to correct projects and handle load-balancing and internal networking.

    Prerequisites

    Servers / Virtual Machines

    Provision 3 Ubuntu servers (22.04 as of this writing) that meet k3s specifications for HA etcd

    As we’re provisioning an example deployment here for 50 counties and over 20,000 CHWs, the RAM, CPU and storage numbers will differ for you specific deployment.

    To support all 50 counties, provision 3 Ubuntu servers (22.04 as of this writing) with 4 vCPU and 8GB Ram. Ensure they also meet k3s specifications for HA etcd.

    Provision 9 Ubuntu servers (again 22.04 as of this writing) for your k3s agent/worker servers. Each should have 48 vCPU, 192 GB Ram, and 50gb local storage.

    For any additional VMs you add to the k3s cluster, you will need to ensure networking, roles, and extra configuration parameters that are noted below are configured on the VM.

    To ensure your hardware is not over-provisioned, add more VMs to your k3s cluster when you want to deploy more CHT Core projects. This gives you flexibility of not needing to provision them initially as they can easily be added later.

    Network

    Ensure the above provisioned VMs:

    Add Roles and Permissions to our VMs

    Following the vSphere docs, first create the following vSphere roles in vSphere for Container Storage (CSN):

    • CNS-VM
    • CNS-DATASTORE
    • CNS-SEARCH-AND-SPBM

    Now, on the VM settings, we can apply these roles as described in the above document.

    Any provisioned VM in the previous step, should receive CNS-VM role. + Create project issue

    Self Hosting in CHT 4.x - Multiple CouchDB Nodes on k3s on VMWare

    Hosting the CHT on self run VMware infrastructure for multiple CHT-Core projects that utilize horizontally scaled CouchDB nodes

    This page covers an example k3s cluster setup on a VMware datacenter with vSphere 7+ for a national deployment across 50 counties capable of supporting 20,000+ CHWs concurrently. After setup, administrators should only add VMs to the cluster or deploy CHT Core projects to be orchestrated.

    About container orchestration

    A container orchestrator helps easily allocate hardware resources spread across a datacenter. For national scale projects, or a deployments with a large number of CHT Core instances, Medic recommends a lightweight Kubernetes orchestrator called k3s. The orchestrator will:

    • monitor resources across a group of virtual machines (aka “nodes”)
    • place CHT Core projects where there is available resource
    • migrate projects to spare resources if combined utilization is high or there are underlying issues.

    Instead of provisioning one VM per CHT Core project, we will provision larger VMs and deploy multiple CHT Core projects on one VM, with each project receiving optional resource limitations, like CPU and RAM.

    In this example an orchestrator is deploying 50 CHT Core projects, one for each county. We will provision 9 large VMs and place 6 CHT Core projects on each VM. This allows for spare resources for failovers and lets the orchestrator decide on which VM projects live. Further, we get automated efficient use of datacenter resource utilization and avoids future manual allocations.

    Nodes

    We’ll be using two types of k3s nodes in this deployment:

    • HA control-plane nodes - these enable high availability (HA) and provide access to kube API. These are containers running inside kube-system namespace which are often associated with the control-plane. They include coreDNS, traefik (ingress), servicelb, VMware Cloud Provisioner Interface (CPI), and VMWare Container Storage Interface (CSI)

    • Agent or worker nodes - these run the CHT Core containers and projects. They will also run services that tie in networking and storage. VMware CSI-node will be running here which enables agents to mount volumes from VMware Virtual-SAN for block data storage. Agents will also run servicelb-traefik containers which allow the nodes to route traffic to correct projects and handle load-balancing and internal networking.

    Prerequisites

    Servers / Virtual Machines

    Provision 3 Ubuntu servers (22.04 as of this writing) that meet k3s specifications for HA etcd

    As we’re provisioning an example deployment here for 50 counties and over 20,000 CHWs, the RAM, CPU and storage numbers will differ for you specific deployment.

    To support all 50 counties, provision 3 Ubuntu servers (22.04 as of this writing) with 4 vCPU and 8GB Ram. Ensure they also meet k3s specifications for HA etcd.

    Provision 9 Ubuntu servers (again 22.04 as of this writing) for your k3s agent/worker servers. Each should have 48 vCPU, 192 GB Ram, and 50gb local storage.

    For any additional VMs you add to the k3s cluster, you will need to ensure networking, roles, and extra configuration parameters that are noted below are configured on the VM.

    To ensure your hardware is not over-provisioned, add more VMs to your k3s cluster when you want to deploy more CHT Core projects. This gives you flexibility of not needing to provision them initially as they can easily be added later.

    Network

    Ensure the above provisioned VMs:

    Add Roles and Permissions to our VMs

    Following the vSphere docs, first create the following vSphere roles in vSphere for Container Storage (CSN):

    • CNS-VM
    • CNS-DATASTORE
    • CNS-SEARCH-AND-SPBM

    Now, on the VM settings, we can apply these roles as described in the above document.

    Any provisioned VM in the previous step, should receive CNS-VM role. The top-level vCenter server will receive CNS-SEARCH-AND-SPBM role. Virtual-SAN should receive CNS-DATASTORE. And all servers should have the READONLY role (this may already be active)

    Enable Necessary Extra Parameters on all VMs

    Following along the above document, we want to verify VM Hardware Version is 15 or greater, and that disk.EnableUUID parameter is configured.

    On each node, through vSphere Client (GUI):

    1. disk.EnableUUID

      1. In the vSphere Client, right-click the VM and select Edit Settings.
      2. Click the VM Options tab and expand the Advanced menu.
      3. Click Edit Configuration next to Configuration Parameters.
      4. Configure the disk.EnableUUID parameter. If the parameter exists, make sure that its value is set to True. If the parameter is not present, add it and set its value to True.
    2. Verify VM hardware version at 15 or higher, and upgrade if necessary

      1. In the vSphere Client, navigate to the virtual machine.
      2. Select Actions > Compatibility > Upgrade VM Compatibility.
      3. Click Yes to confirm the upgrade.
      4. Select a compatibility and click OK.
    3. Add VMware Paravirtual SCSI storage controller to the VM

      1. In the vSphere Client, right-click the VM and select Edit Settings.
      2. On the Virtual Hardware tab, click the Add New Device button.
      3. Select SCSI Controller from the drop-down menu.
      4. Expand New SCSI controller and from the Change Type menu, select VMware Paravirtual.
      5. Click OK.

    Identify vSphere Provider IDs, Node IDs, and datacenter name

    Bootstrap parameters for k3s on VMware require UUID identification of each node that will join the cluster.

    For each of the provisioned VMs, you can navigate to the VM in vCenter interface and retrieve the UUID.

    Another method is to make the following calls to vCenter Server API. You may have a VPN that you connect to first before being able to access your vCenter GUI. These commands should be run from the same network that allows that access.

    When running the commands below, be sure to replace the placeholders with your own values:

    • <vCenter_IP>
    • <USERNAME>
    • <PASSWORD>
    • <UUID_FROM_vCENTER>

    And any others as well!

    • Get an authentication-token:

      curl -k -X POST https://<vCenter_IP>/rest/com/vmware/cis/session -u '<USERNAME>:<PASSWORD>'
      @@ -435,7 +435,8 @@
         storageClassName: vmware-sc
       status: {}
       

      Kubernetes Concepts

      Here are links to docs surrounding the kubernetes concepts that we use in a cht-core project deployed to a k3s cluster.

      • Deployment - This is the main kubernetes resource that contains information regarding all the cht services that will be deployed.
      • ConfigMaps - This contains configuration files, or credentials that containers can retrieve. If you edit the configmap, you should delete containers, which will trigger a new container to download your new edits to any configurations for that service
      • ServiceAccounts - This is used by the upgrade-service that is running inside the cht-core pods (as a container titled upgrade-service). This serviceAccount restricts the upgrade-service from interacting with any other cht-core projects outside of its namespace, and gives the upgrade-service permissions to talk to kubernetes API to upgrade container images when a CHT ADMIN clicks upgrade through the Admin interface.
      • Ingress - This is what forwards traffic to a particular project or pods. In most use-cases, there is an nginx deployed outside of the k3s cluster than contains DNS entries for existing projects, and contains a proxy_pass parameter to send traffic based on host header to any of the k3s server IPs. Inside the k3s cluster, the traefik container and servicelb-traefik containers in kube-system namespace will handle forwarding traffic to the correct cht-core containers based on url
      • Persistent Volume Claim - This is where our project data will be stored. Important to ensure you have configured this correctly, with retain policies intact so the data is not deleted if the project is removed. It’s also vital to ensure you have a backup policy either set-up in VMware vCenter GUI or you have configured the csi-snapshotter that comes with vSphere CSI.
      • Services - This is utilized for CouchDB nodes to discover each other through DNS rather than internal IPs, which can change. This is also used in the COUCH_URL so API containers can discover where CouchDB is running.
      -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/hosting/4.x/self-hosting/single-node/index.html b/hosting/4.x/self-hosting/single-node/index.html index 043582a8c9..125ca631fc 100644 --- a/hosting/4.x/self-hosting/single-node/index.html +++ b/hosting/4.x/self-hosting/single-node/index.html @@ -1,9 +1,9 @@ -Self Hosting in CHT 4.x - Single CouchDB Node | Community Health Toolkit +Self Hosting in CHT 4.x - Single CouchDB Node | Community Health Toolkit

    Self Hosting in CHT 4.x - Single CouchDB Node

    Self Hosting in CHT 4.x - Single CouchDB Node

    This for a single node CHT 4.x instance and is the recommended solution for small deployments. If you want a more powerful setup, check out the 4.x multi-node install docs.

    Prerequisites

    Be sure you have followed the requirements document including installing Docker and Docker Compose. This guide assumes you’re using the ubuntu user and that it has sudo-less access to Docker.

    Directory Structure

    Create the following directory structure:

    |-- /home/ubuntu/cht/
    + Create project issue

    Self Hosting in CHT 4.x - Single CouchDB Node

    Self Hosting in CHT 4.x - Single CouchDB Node

    This for a single node CHT 4.x instance and is the recommended solution for small deployments. If you want a more powerful setup, check out the 4.x multi-node install docs.

    Prerequisites

    Be sure you have followed the requirements document including installing Docker and Docker Compose. This guide assumes you’re using the ubuntu user and that it has sudo-less access to Docker.

    Directory Structure

    Create the following directory structure:

    |-- /home/ubuntu/cht/
                       |-- compose/
                       |-- certs/
                       |-- couchdb/
    @@ -344,7 +344,8 @@
     docker compose pull
     docker compose up --detach
     

    Follow the Product Releases channel on the CHT forum to stay informed about new releases and upgrades.

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/hosting/index.html b/hosting/index.html index 6d39e8bf2f..a4e4157f01 100644 --- a/hosting/index.html +++ b/hosting/index.html @@ -1,9 +1,9 @@ -Hosting | Community Health Toolkit +Hosting | Community Health Toolkit
    \ No newline at end of file diff --git a/hosting/index.xml b/hosting/index.xml index b16c156bec..ca4eb82784 100644 --- a/hosting/index.xml +++ b/hosting/index.xml @@ -1 +1,5 @@ -Community Health Toolkit – Hostinghttps://docs.communityhealthtoolkit.org/hosting/Recent content in Hosting on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file +Hosting on Community Health Toolkithttps://docs.communityhealthtoolkit.org/hosting/Recent content in Hosting on Community Health ToolkitHugo -- gohugo.ioenCHT hosting requirementshttps://docs.communityhealthtoolkit.org/hosting/requirements/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/requirements/For production CHT deployments, Linux is recommended, with Ubuntu the most commonly used. For App Developer Hosting, Linux or macOS may be used. Windows can be used for either, but without recommendation. +Per the Kubernetes vs Docker page, CHT Core can be deployed with either Docker or Kubernetes. +CHT 3.x is End-of-Life and us no longer supported. All requirements below apply to CHT 4.x. +Docker Compose App Developer Hosting 4 GB RAM / 2 CPU / 8 GB SSD Root Access TLS certificates - Docker Helper for 3.Vertical vs Horizontal scalinghttps://docs.communityhealthtoolkit.org/hosting/vertical-vs-horizontal/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/vertical-vs-horizontal/Introduction Horizontally scaling is the ability to add more servers to an application to make it more performant. This often yields better performance than vertical scaling, which is adding more resources like RAM or CPU to a single server. +CHT Core 4.0.0 introduces a new architecture for hosting which gives it the ability to easily scale horizontally. This enables large deployments to support more concurrent users and better utilize the underlying server hardware.Kubernetes vs Dockerhttps://docs.communityhealthtoolkit.org/hosting/kubernetes-vs-docker/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/kubernetes-vs-docker/Since the release of CHT Core 4.0.0 in late 2022, Medic has been perfecting the hosting for the toolkit to balance the need for high uptimes so CHWs can always deliver care while having an easy and approachable technical back end hosting solution. While initially Docker Compose with an overlay network was thought to be our goto solution, field testing this overlay networks in production has shown them to unreliable. \ No newline at end of file diff --git a/hosting/kubernetes-vs-docker/index.html b/hosting/kubernetes-vs-docker/index.html index 341e2c16fc..75e1b67ca9 100644 --- a/hosting/kubernetes-vs-docker/index.html +++ b/hosting/kubernetes-vs-docker/index.html @@ -1,9 +1,9 @@ -Kubernetes vs Docker | Community Health Toolkit +Kubernetes vs Docker | Community Health Toolkit

    Kubernetes vs Docker

    Options for installing CHT applications

    Since the release of CHT Core 4.0.0 in late 2022, Medic has been perfecting the hosting for the toolkit to balance the need for high uptimes so CHWs can always deliver care while having an easy and approachable technical back end hosting solution. While initially Docker Compose with an overlay network was thought to be our goto solution, field testing this overlay networks in production has shown them to unreliable.

    As such, on this site you will find documentation for both Docker Compose (known as “Medic OS” in CHT 3.x) and Kubernetes. Medic is in the process of phasing out Docker Compose and will ultimately deprecate it in favor of Kubernetes, including simplified versions like k3s and cloud based solutions like Amazon’s Elastic Kubernetes Service (EKS).

    Given all this, we currently recommend:

    • Application development for both CHT 3.x and CHT 4.x should use Docker Compose.
    • Production 3.x CHT deployments should use Docker Compose - Note that 3.x is end of life and should only be used to support existing 3.x deployments.
    • All new production 4.x CHT deployments should use Kubernetes

    Kubernetes

    Kubernetes provides advantages in managing CHT Deployments:

    • Resilient network across either physical or VM nodes in the cluster. This allows strong distribution of the heavy CPU and RAM loads that CouchDB can incur under heave use.
    • Orchestration of multiple deployments ensuring to allow easy hosting of multi-tenants. For example you may opt to have a user acceptance testing (UAT), staging and production instances all in one cluster.
    • Service discovery which enables to route public requests to deployments within the cluster
    • Integration with hypervisors such as VMWare and ESX. This is possible due to CRD support in Kubernetes that allows 3rd parties to create integrations
    • Helm support and integration that makes it easy to easily deploy applications on to the cluster
    • Highly efficient snapshot backups when used with a storage area network SAN or Amazon’s Elastic Block Storage (EBS).

    The main components of a Kubernetes deployment include:

    • A pod for each CouchDB instance.
    • A CHT API pod.
    • A CHT HAProxy Healthcheck pod.
    • A CHT HAProxy pod.
    • Upgrade Service pod.
    • CHT-Sentinel pod.

    Docker Compose

    The Docker Compose based CHT Core deployment was Medic’s first attempt to make CHT 4.x cloud native. The Compose files work quite well for application developer setups on laptops and the like (check out the Docker Helper!). Additionally, we have existing published guides on how to deploy single and multi-node Compose based solutions. For small, low use instances, likely the single node Compose deployments will be fine. We do not recommend setting up a multi-node deployment on Compose with an overlay network. Please use Kubernetes instead for a more stable and horizontally scalable solution.

    The Compose documentation here is only for reference until Medic is ready to fully deprecate this content.

    Like Kubernetes above, Docker Compose deploys the same services but adds an additional 7th to allow for ingress/egress via a reverse proxy:

    • A service for each CouchDB instance.
    • A CHT API service.
    • HAProxy Healthcheck service.
    • HAProxy service.
    • Upgrade Service service.
    • A CHT Sentinel service.
    • An nginx service to act as a reverse proxy and terminate TLS connections
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/hosting/monitoring/index.html b/hosting/monitoring/index.html index 5fd6df79af..7d24fcfdd0 100644 --- a/hosting/monitoring/index.html +++ b/hosting/monitoring/index.html @@ -1,9 +1,9 @@ -Monitoring and Alerting | Community Health Toolkit +Monitoring and Alerting | Community Health Toolkit

    Monitoring and Alerting

    Using CHT Watchdog to Monitor and Alert on CHT 3.x and 4.x Applications

    Introduction to monitoring and alerting

    High level approach to monitoring and alerting with CHT applications

    CHT Watchdog Setup

    Setting up Grafana and Prometheus with the CHT

    Production CHT Watchdog

    Production considerations for CHT Watchdog

    Custom Postgres metrics in CHT Watchdog

    Adding Custom Postgres metrics into CHT Watchdog

    Integrating CHT Watchdog

    Scraping and alerting external sources with CHT Watchdog

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/hosting/monitoring/index.xml b/hosting/monitoring/index.xml index cad85ea140..8679a6842c 100644 --- a/hosting/monitoring/index.xml +++ b/hosting/monitoring/index.xml @@ -1,1011 +1,9 @@ -Community Health Toolkit – Monitoring and Alertinghttps://docs.communityhealthtoolkit.org/hosting/monitoring/Recent content in Monitoring and Alerting on Community Health ToolkitHugo -- gohugo.ioenHosting: Introduction to monitoring and alertinghttps://docs.communityhealthtoolkit.org/hosting/monitoring/introduction/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/monitoring/introduction/ -<div class="pageinfo pageinfo-primary"> -<p>This guide applies to all production instances of the CHT for both 3.x (beyond 3.9) and 4.x.</p> -<p>Be sure to see how to deploy a solution to <a href="https://docs.communityhealthtoolkit.org/hosting/monitoring/setup/">monitor and alert on production CHT instances</a>.</p> -</div> -<p>Each deployment will experience different stresses on its resources. Be sure to tune any alerting levels in the case of a false positive so that you may avoid them in the future. Any thresholds for alerts, and even what is alerted on, is just a guideline, not a guarantee of uptime.</p> -<h2 id="monitoring-vs-alerting">Monitoring vs Alerting</h2> -<p>Monitoring allows CHT admins to see statistics about their server, often over time. This can be helpful when you want to be aware of growth in your deployment (eg number of active users or number of reports per region). It should not be assumed that these will be checked regularly enough to notice a problem, for example a spike in number of feedback documents.</p> -<p>Alerting is a push mechanism designed to notify users who can act on the alert. These can go over SMS, email, Slack, WhatsApp or any other channel to notify the right users.</p> -<p>The process of setting up monitoring and alerting should be done together. Monitoring sets the baseline and then alerting tells admins when the metric has gone beyond the baseline to a critical state. Certain metrics, like uptime for example, likely do not need to have a monitoring visualization on a dashboard, but the monitoring system should still be the authority to send an alert to denote when the service has restarted unexpectedly.</p> -<h2 id="outside-the-cht">Outside the CHT</h2> -<p>Be sure to monitor important items that the CHT depends on in order to be healthy. You should alert when any of these are close to their maximum (disk space) or minimum (days left of valid TLS certificate):</p> -<ul> -<li>Domain expiration with registrar</li> -<li>TLS certificate expiration</li> -<li>Disk &amp; swap space</li> -<li>CPU utilization</li> -<li>Memory utilization</li> -<li>Network utilization</li> -<li>Process count</li> -<li>OS Uptime</li> -</ul> -<h2 id="inside-the-cht">Inside the CHT</h2> -<p>The <a href="https://docs.communityhealthtoolkit.org/apps/reference/api/#get-apiv2monitoring">monitoring API</a> was added in 3.9.0 and does not require any authentication and so can easily be used with third party tools as they do not need a CHT user account.</p> -<p>All metrics need to be monitored over time so that you can easily see longitudinal patterns when debugging an outage or slow down.</p> -<h3 id="specific-of-monitoring">Specific of monitoring</h3> -<h4 id="explosive-growth">Explosive Growth</h4> -<p>Many of the values in the monitoring API do not mean much in isolation. For example if an instance has 10,714,278 feedback docs, is that bad? If it&rsquo;s years old and has thousands of users, then this is normal. If it is 4 months old and has 100 users, this is a dire problem!</p> -<p>You should monitor these metrics for unexpected growth as measured by percent change over 24 hours. Ideally this can be subjectively calculated when it is more than 5% growth than the prior day. They&rsquo;re marked as <code>growth</code> in the table below.</p> -<h4 id="non-zero-values">Non-Zero Values</h4> -<p>Other values should always be zero, and you should alert when they are not. You may opt to alert only when they are non-zero for more than 24 hours. These are marked as <code>non-zero</code> in the table below.</p> -<h4 id="zero-or-near-zero-values">Zero or Near Zero Values</h4> -<p>Finally, these values should always be <em>not</em> zero, and you should alert when are zero or very close to it. You may opt to alert only when they are zero for more than 24 hours. They&rsquo;re marked with <code>zero</code> below.</p> -<h4 id="elements-types-and-samples">Elements, types and samples</h4> -<p>The names below are extrapolated from the paths in the JSON returned by the API and should be easy to find when viewing the Monitoring API URL on your CHT instance:</p> -<table> -<thead> -<tr> -<th>Name</th> -<th>Type</th> -<th>Example Value</th> -</tr> -</thead> -<tbody> -<tr> -<td>Conflict Count</td> -<td><code>growth</code></td> -<td>23,318</td> -</tr> -<tr> -<td>CouchDB Medic Doc Count</td> -<td><code>growth</code></td> -<td>16,254,271</td> -</tr> -<tr> -<td>CouchDB Medic Fragmentation</td> -<td><code>growth</code></td> -<td>1.4366029665729645</td> -</tr> -<tr> -<td>CouchDB Sentinel Doc Count</td> -<td><code>growth</code></td> -<td>15,756,449</td> -</tr> -<tr> -<td>CouchDB Sentinel Fragmentation</td> -<td><code>growth</code></td> -<td>2.388733774539664</td> -</tr> -<tr> -<td>CouchDB Users Doc Count</td> -<td><code>growth</code></td> -<td>535</td> -</tr> -<tr> -<td>CouchDB Users Fragmentation</td> -<td><code>growth</code></td> -<td>2.356411021364134</td> -</tr> -<tr> -<td>CouchDB Users Meta Doc Count</td> -<td><code>growth</code></td> -<td>10,761,549</td> -</tr> -<tr> -<td>Feedback Count</td> -<td><code>growth</code></td> -<td>10,714,368</td> -</tr> -<tr> -<td>Messaging Outgoing State Due</td> -<td><code>growth</code></td> -<td>3,807</td> -</tr> -<tr> -<td>Messaging Outgoing State Failed</td> -<td><code>non-zero</code></td> -<td>0</td> -</tr> -<tr> -<td>Outbound Push Backlog</td> -<td><code>non-zero</code></td> -<td>0</td> -</tr> -<tr> -<td>Sentinel Backlog</td> -<td><code>non-zero</code></td> -<td>0</td> -</tr> -<tr> -<td>Date Uptime</td> -<td><code>zero</code></td> -<td>1,626,508.148</td> -</tr> -</tbody> -</table>Hosting: CHT Watchdog Setuphttps://docs.communityhealthtoolkit.org/hosting/monitoring/setup/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/monitoring/setup/ -<div class="pageinfo pageinfo-primary"> -<p>These instructions apply to both CHT 3.x (beyond 3.12) and CHT 4.x.</p> -</div> -<p>Medic maintains CHT Watchdog which is an opinionated configuration of <a href="https://prometheus.io/">Prometheus</a> (including <a href="https://github.com/prometheus-community/json_exporter">json_exporter</a>) and <a href="https://grafana.com/grafana/">Grafana</a> which can easily be deployed using Docker. It is supported on CHT 3.12 and later, including CHT 4.x. By using this solution a CHT deployment can easily get longitudinal monitoring and push alerts using Email, Slack or other mechanisms. All tools are open source and have no licensing fees.</p> -<p>The solution provides both an overview dashboard as well as a detail dashboard. Here is a portion of the overview dashboard:</p> -<p><img src="monitoring.and.alerting.screenshot.png" alt="Screenshot of Grafana Dashboard showing data from Prometheus"></p> -<p><a href="https://prometheus.io/docs/concepts/metric_types/">Prometheus supports</a> four metric types: Counter, Gauge, Histogram, and Summary. Currently, the CHT only provides Counter and Gauge type metrics. When building panels for Grafana dashboards, <a href="https://prometheus.io/docs/prometheus/latest/querying/functions/">Prometheus Functions</a> can be used to manipulate the metric data. Refer to the <a href="https://grafana.com/docs/grafana/latest/dashboards/build-dashboards/best-practices/">Grafana Documentation</a> for best practices on building dashboards.</p> -<h3 id="prerequisites">Prerequisites</h3> -<ul> -<li><a href="https://docs.docker.com/install/">Docker</a></li> -<li><a href="https://docs.docker.com/compose/install/">Docker Compose</a></li> -<li><a href="https://git-scm.com/book/en/v2/Getting-Started-Installing-Git">git</a></li> -<li>URL(s) of the CHT instance(s)</li> -</ul> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Always run Watchdog on a different server than the CHT Core. This ensures Watchdog doesn&rsquo;t fail if the CHT Core server fails and alerts will always be sent. The instructions assume you&rsquo;re connecting over the public Internet and no special VPN or routing is required. -</div> -<h3 id="setup">Setup</h3> -<p>These instructions have been tested against Ubuntu, but should work against any OS that meets the prerequisites. They follow a happy path assuming you need to only set a secure password and specify the URL(s) to monitor:</p> -<ol> -<li> -<p>Run the following commands to clone this repository, initialize your <code>.env</code> file, create a secure password and create your data directories:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~ -</span></span><span style="display:flex;"><span>git clone https://github.com/medic/cht-watchdog.git -</span></span><span style="display:flex;"><span><span style="color:#204a87">cd</span> cht-watchdog -</span></span><span style="display:flex;"><span>cp cht-instances.example.yml cht-instances.yml -</span></span><span style="display:flex;"><span>cp grafana/grafana.example.ini grafana/grafana.ini -</span></span><span style="display:flex;"><span>mkdir -p grafana/data <span style="color:#ce5c00;font-weight:bold">&amp;&amp;</span> mkdir -p prometheus/data -</span></span><span style="display:flex;"><span>sudo apt install -y wamerican <span style="color:#8f5902;font-style:italic"># ensures /usr/share/dict/words is present for shuf call below </span> -</span></span><span style="display:flex;"><span>cp .env.example .env -</span></span><span style="display:flex;"><span><span style="color:#000">password</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#204a87;font-weight:bold">$(</span>shuf -n7 /usr/share/dict/words --random-source<span style="color:#ce5c00;font-weight:bold">=</span>/dev/random <span style="color:#000;font-weight:bold">|</span> tr <span style="color:#4e9a06">&#39;\n&#39;</span> <span style="color:#4e9a06">&#39;-&#39;</span> <span style="color:#000;font-weight:bold">|</span> tr -d <span style="color:#4e9a06">&#34;&#39;&#34;</span> <span style="color:#000;font-weight:bold">|</span> cut -d<span style="color:#4e9a06">&#39;-&#39;</span> -f1,2,3,4,5,6,7<span style="color:#204a87;font-weight:bold">)</span> -</span></span><span style="display:flex;"><span>sed -i -e <span style="color:#4e9a06">&#34;s/password/</span><span style="color:#000">$password</span><span style="color:#4e9a06">/g&#34;</span> .env -</span></span><span style="display:flex;"><span>echo<span style="color:#000;font-weight:bold">;</span><span style="color:#204a87">echo</span> <span style="color:#4e9a06">&#34;Initial project structure created! To log into Grafana in the browser:&#34;</span><span style="color:#000;font-weight:bold">;</span><span style="color:#204a87">echo</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87">echo</span> <span style="color:#4e9a06">&#34; username: medic&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#204a87">echo</span> <span style="color:#4e9a06">&#34; password: </span><span style="color:#4e9a06">${</span><span style="color:#000">password</span><span style="color:#4e9a06">}</span><span style="color:#4e9a06">&#34;</span><span style="color:#000;font-weight:bold">;</span><span style="color:#204a87">echo</span> -</span></span></code></pre></div><p>If you&rsquo;re using docker-compose v2.x, it doesn&rsquo;t support relative paths and you&rsquo;ll have to edit your <code>.env</code> file to update paths to absolute path.</p> -<p>Note that in step 4 below you&rsquo;ll need the username and password which is printed after you run the above command.</p> -</li> -<li> -<p>Edit the <code>cht-instances.yml</code> file to have the URLs of your CHT instances. You may include as many URLs of CHT instances as you like.</p> -<p>Here is an example:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yml" data-lang="yml"><span style="display:flex;"><span>- <span style="color:#204a87;font-weight:bold">targets</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#000">https://subsub.sub.example.com</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#000">https://cht.domain.com</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#000">https://website.org</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span></code></pre></div></li> -<li> -<p>Run the following command to deploy the stack:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/cht-watchdog -</span></span><span style="display:flex;"><span>docker compose up -d -</span></span></code></pre></div></li> -<li> -<p>Grafana is available at <a href="http://localhost:3000">http://localhost:3000</a>. See the output from step 1 for your username and password.</p> -</li> -</ol> -<p>If you would like to do more customizing of your deployment, see <a href="#additional-configuration">&ldquo;Additional Configuration&rdquo;</a>.</p> -<h3 id="upgrading">Upgrading</h3> -<p>Before upgrading, you should back up both your current configuration settings as well as your Prometheus/Grafana data directories.</p> -<h4 id="prometheus-grafana-and-json-exporter">Prometheus, Grafana and JSON Exporter</h4> -<p>To upgrade these dependencies, update the version numbers set in your <code>.env</code> file (or leave them set to <code>latest</code>). Then run the following commands:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>docker compose pull -</span></span><span style="display:flex;"><span>docker compose up -d -</span></span></code></pre></div><h4 id="cht-watchdog">CHT Watchdog</h4> -<p>When you see a new version in the <a href="https://github.com/medic/cht-watchdog">GitHub repository</a>, first review the release notes and upgrade instructions. Then, run the following commands to deploy the new configuration (be sure to replace <code>TAG</code> with the tag name associated with the release (e.g. <code>1.1.0</code>)):</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/cht-watchdog -</span></span><span style="display:flex;"><span>git fetch -</span></span><span style="display:flex;"><span>git -c advice.detachedHead<span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#204a87">false</span> checkout TAG -</span></span><span style="display:flex;"><span>docker compose pull -</span></span><span style="display:flex;"><span>docker compose down -</span></span><span style="display:flex;"><span>docker compose up -d --remove-orphans -</span></span></code></pre></div><h3 id="additional-configuration">Additional Configuration</h3> -<p>When making any changes to your CHT Watchdog configuration (e.g. adding/removing instances from the <code>cht-instances.yml</code> file) make sure to restart all services to pick up the changes:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/cht-watchdog -</span></span><span style="display:flex;"><span>docker compose down -</span></span><span style="display:flex;"><span>docker compose up -d -</span></span></code></pre></div><h4 id="couch2pg-data">couch2pg Data</h4> -<p>With the <a href="https://github.com/medic/cht-watchdog/releases/tag/1.1.0">release of 1.1.0</a>, Watchdog now supports easily ingesting <a href="https://docs.communityhealthtoolkit.org/apps/tutorials/couch2pg-setup/">couch2pg</a> data read in from a Postgres database (supports Postgres <code>&gt;= 9.x</code>).</p> -<ol> -<li> -<p>Copy the example config file, so you can add the correct contents in them:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/cht-watchdog -</span></span><span style="display:flex;"><span>cp exporters/postgres/sql_servers_example.yml exporters/postgres/sql_servers.yml -</span></span></code></pre></div></li> -<li> -<p>Edit <code>sql_servers.yml</code> you just created and add your target postgres connection URL. For example, if your postgres server was <code>db.example.com</code>, your user was <code>db_user</code> and your password was <code>db_password</code>, the config would be:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#204a87;font-weight:bold">targets</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">&#34;db-example-com&#34;: </span><span style="color:#4e9a06">&#39;postgres://db_user:db_password@db.example.com:5432/cht?sslmode=disable&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#8f5902;font-style:italic"># //NOSONAR - password is safe to commit</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span></code></pre></div><p>You may add as many targets as you would like here - one for each CHT Core instance in your <code>cht-instances.yml</code> file. Be sure to give each entry a unique name based of the Postgres server (eg <code>db-example-com</code> as shown).</p> -</li> -<li> -<p>Start your instance up, being sure to include both the existing <code>docker-compose.yml</code> and the <code>docker-compose.postgres-exporter.yml</code> file:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/cht-watchdog -</span></span><span style="display:flex;"><span>docker compose -f docker-compose.yml -f exporters/postgres/compose.yml up -d -</span></span></code></pre></div></li> -</ol> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Always run this longer version of the <code>docker compose</code> command which specifies both compose files for all future <a href="#upgrading">upgrades</a>. -</div> -<h4 id="couch2pg-data-remote">couch2pg Data (Remote)</h4> -<p>While not the default setup, and not what most deployments need, you may want to set up a way to monitor couch2pg data without sharing any Postgres credentials. Instead of sharing credentials, you expose an HTTP endpoint that requires no login or password. Of course, similar to CHT Core&rsquo;s <a href="https://docs.communityhealthtoolkit.org/apps/reference/api/#get-apiv2monitoring">Monitoring API</a>, this endpoint should be configured to not share sensitive information (since it will be publicly accessible).</p> -<p>To run a remote instance of only the SQL Exporter on your Postgres server:</p> -<ol> -<li>Clone this repo: <code>git clone git@github.com:medic/cht-watchdog.git</code> and <code>cd</code> into <code>cht-watchdog</code></li> -<li>Copy <code>exporters/postgres/sql_servers_example.yml</code> to <code>exporters/postgres/sql_servers.yml</code></li> -<li>Edit the new <code>exporters/postgres/sql_servers.yml</code> file to have the correct credentials for your server. You need to update the <code>USERNAME</code> and <code>PASSWORD</code>. You may need to update the IP address and port also, but likely the default values are correct.</li> -<li>Copy <code>.env.example</code> to <code>.env</code></li> -<li>In the new <code>.env</code> file, edit <code>SQL_EXPORTER_IP</code> to be public IP of the Posgtres server</li> -<li>Start the service with these two compose files*: -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>docker compose --env-file .env -f exporters/postgres/compose.yml -f exporters/postgres/compose.stand-alone.yml up -d -</span></span></code></pre></div></li> -<li>Verify that you see the SQL Exporters metrics: If <code>SQL_EXPORTER_IP</code> was set to <code>10.220.249.15</code>, then this would be: <code>http://10.220.249.15:9399/metrics</code>. The last line starting with <code>up{job=&quot;db_targets&quot;...</code> should end in a <code>1</code> denoting the system is working. If it ends in <code>0</code> - check your docker logs for errors.</li> -<li>On your watchdog instance, create a custom scrape definition file: <code>cp exporters/postgres/scrape.yml ./exporters/postgres/scrape-custom.yml</code></li> -<li>Edit <code>scrape-custom.yml</code> so that it has the ip address of <code>SQL_EXPORTER_IP</code> from step 7 above. If that was <code>10.220.249.15</code>, then you file would look like: -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">scrape_configs</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#204a87;font-weight:bold">job_name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">sql_exporter</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">static_configs</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#204a87;font-weight:bold">targets</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;10.220.249.15:9399&#39;</span><span style="color:#000;font-weight:bold">]</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span></code></pre></div></li> -<li>Finally, on your watchdog instance, start (or restart) your server including the <code>compose.scrape-only.yml</code> compose file: -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose --env-file .env -f exporters/postgres/compose.yml -f exporters/postgres/compose.scrape-only.yml up -d -</span></span></code></pre></div></li> -</ol> -<p>* <em>The <code>compose.stand-alone.yml</code> and <code>compose.scrape-only.yml</code> compose files override some services. This is done so that no manual edits are needed to any compose files.</em></p> -<h4 id="prometheus-retention-and-storage">Prometheus Retention and Storage</h4> -<p>By default, historical monitoring data will be stored in Prometheus (<code>PROMETHEUS_DATA</code> directory) for 60 days (configurable by <code>PROMETHEUS_RETENTION_TIME</code>). A longer retention time can be configured to allow for longer-term analysis of the data. However, this will increase the size of the Prometheus data volume. See the <a href="https://prometheus.io/docs/prometheus/latest/storage/">Prometheus documentation</a> for more information.</p> -<p>Local storage is not suitable for storing large amounts of monitoring data. If you intend to store multiple years worth of metrics, you should consider integrating Prometheus with a <a href="https://prometheus.io/docs/operating/integrations/#remote-endpoints-and-storage/">Remote Storage</a>.</p> -<h4 id="alerts">Alerts</h4> -<p>This configuration includes number of pre-provisioned alert rules. Additional alerting rules (and other contact points) can be set in the Grafana UI.</p> -<p>See both the Grafana <a href="https://grafana.com/docs/grafana/latest/alerting/">high level alert Documentation</a> and <a href="https://grafana.com/docs/grafana/latest/alerting/set-up/provision-alerting-resources/file-provisioning/#provision-alert-rules">provisioning alerts in the UI</a> for more information.</p> -<h5 id="deleting-provisioned-alert-rules">Deleting provisioned alert rules</h5> -<p>The provisioned alert rules shipped with CHT Watchdog are intended to be the generally applicable for most CHT deployments. However, not all the alert rules will necessarily be useful for everyone. If you would like to delete any of the provisioned alert rules, you can do so with the following steps:</p> -<ol> -<li> -<p>In Grafana, navigate to &ldquo;Alerting&rdquo; and then &ldquo;Alert Rules&rdquo; and click the eye icon for the rule you want to delete. Copy the <code>Rule UID</code> which can be found on the right and is a 10 character value like <code>mASYtCQ2j</code>.</p> -</li> -<li> -<p>Create a <code>delete-rules.yml</code> file</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/cht-watchdog -</span></span><span style="display:flex;"><span>cp grafana/provisioning/alerting/delete-rules.example.yml grafana/provisioning/alerting/delete-rules.yml -</span></span></code></pre></div></li> -<li> -<p>Update your new <code>delete-rules.yml</code> file to include the Rule UID(s) of the alert rule(s) you want to delete</p> -</li> -<li> -<p>Restart Grafana</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>docker compose restart grafana -</span></span></code></pre></div></li> -</ol> -<p>If you ever want to re-enable the alert rules you deleted, you can simply remove the Rule UID(s) from the <code>delete-rules.yml</code> file and restart Grafana again.</p> -<h5 id="modifying-provisioned-alert-rules">Modifying provisioned alert rules</h5> -<p>The provisioned alert rules cannot be modified directly. Instead, you can copy the configuration of a provisioned alert into a new custom alert with the desired changes. Then, remove the provisioned alert.</p> -<ol> -<li>Open the alert rule you would like to modify in the Grafana alert rules UI and select the &ldquo;Copy&rdquo; button.</li> -<li>Update the copied alert rule with the desired changes and save it into a new Evaluation group.</li> -<li><a href="#deleting-provisioned-alert-rules">Remove the provisioned alert</a>.</li> -</ol> -<h5 id="configuring-contact-points">Configuring Contact Points</h5> -<p>Grafana supports sending alerts via a number of different methods. Two likely options are Email and Slack.</p> -<h6 id="email">Email</h6> -<p>To support sending email alerts from Grafana, you must update the <code>smtp</code> section of your <code>grafana/grafana.ini</code> file with your SMTP server configuration. Then, in the web interface, add the desired recipient email addresses in the <code>grafana-default-email</code> contact point settings.</p> -<h6 id="slack">Slack</h6> -<p>Slack alerts can be configured within the Grafana web GUI for the specific rules you would like to alert on.</p> -<h3 id="configuration-reference">Configuration Reference</h3> -<h4 id="environment-variables">Environment Variables</h4> -<p>All the variables in the <code>.env</code> file:</p> -<table> -<thead> -<tr> -<th>Name</th> -<th>Default</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>GRAFANA_ADMIN_USER</code></td> -<td><code>medic</code></td> -<td>Username for the Grafana admin user</td> -</tr> -<tr> -<td><code>GRAFANA_ADMIN_PASSWORD</code></td> -<td><code>password</code></td> -<td>Password for the Grafana admin user</td> -</tr> -<tr> -<td><code>GRAFANA_VERSION</code></td> -<td><code>latest</code></td> -<td>Version of the <code>grafana/grafana-oss</code> image</td> -</tr> -<tr> -<td><code>GRAFANA_PORT</code></td> -<td><code>3000</code></td> -<td>Port on the host where Grafana will be available</td> -</tr> -<tr> -<td><code>GRAFANA_BIND</code></td> -<td><code>127.0.0.1</code></td> -<td>Interface Grafana will bind to. Change to <code>0.0.0.0</code> if you want to expose to all interfaces.</td> -</tr> -<tr> -<td><code>GRAFANA_DATA</code></td> -<td><code>./grafana/data</code></td> -<td>The host directory where Grafana data will be stored</td> -</tr> -<tr> -<td><code>GRAFANA_PLUGINS</code></td> -<td><code>grafana-discourse-datasource</code></td> -<td>Comma separated list of plugins to install (e.g: <code>grafana-clock-panel,grafana-simple-json-datasource</code>)</td> -</tr> -<tr> -<td><code>JSON_EXPORTER_VERSION</code></td> -<td><code>latest</code></td> -<td>Version of the <code>prometheuscommunity/json-exporter</code> image</td> -</tr> -<tr> -<td><code>PROMETHEUS_VERSION</code></td> -<td><code>latest</code></td> -<td>Version of the <code>prom/prometheus</code> image</td> -</tr> -<tr> -<td><code>PROMETHEUS_DATA</code></td> -<td><code>./prometheus/data</code></td> -<td>The host directory where Prometheus data will be stored</td> -</tr> -<tr> -<td><code>PROMETHEUS_RETENTION_TIME</code></td> -<td><code>60d</code></td> -<td>Length of time that Prometheus will store data (e.g. <code>15d</code>, <code>6m</code>, <code>1y</code>)</td> -</tr> -</tbody> -</table> -<h4 id="cht-metrics">CHT Metrics</h4> -<p>All CHT metrics in Prometheus:</p> -<table> -<thead> -<tr> -<th>OpenMetrics name</th> -<th>Type</th> -<th>label(s)</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td><code>cht_api_*</code></td> -<td>N/A</td> -<td></td> -<td>API server metrics (see <a href="https://www.npmjs.com/package/prometheus-api-metrics">prometheus-api-metrics</a>). Requires CHT Core 4.3.0 or later. Includes stats like server response time in seconds and response size in bytes.</td> -</tr> -<tr> -<td><code>cht_conflict_count</code></td> -<td>Gauge</td> -<td></td> -<td>Number of doc conflicts which need to be resolved manually.</td> -</tr> -<tr> -<td><code>cht_connected_users_count</code></td> -<td>Gauge</td> -<td></td> -<td>Number of users that have connected to the api recently. By default the time interval is 7 days. Otherwise it is equal to the connected_user_interval parameter value used when making the /monitoring request.</td> -</tr> -<tr> -<td><code>cht_couchdb_doc_del_total</code></td> -<td>Counter</td> -<td><code>medic</code>, <code>sentinel</code>, <code>medic-users-meta</code>, <code>_users</code></td> -<td>The number of deleted docs in the db.</td> -</tr> -<tr> -<td><code>cht_couchdb_doc_total</code></td> -<td>Counter</td> -<td><code>medic</code>, <code>sentinel</code>, <code>medic-users-meta</code>, <code>_users</code></td> -<td>The number of docs in the db.</td> -</tr> -<tr> -<td><code>cht_couchdb_fragmentation</code></td> -<td>Gauge</td> -<td><code>medic</code>, <code>sentinel</code>, <code>medic-users-meta</code>, <code>_users</code></td> -<td>The fragmentation of the db, lower is better, “1” is no fragmentation.</td> -</tr> -<tr> -<td><code>cht_couchdb_update_sequence</code></td> -<td>Counter</td> -<td><code>medic</code>, <code>sentinel</code>, <code>medic-users-meta</code>, <code>_users</code></td> -<td>The number of changes in the db.</td> -</tr> -<tr> -<td><code>cht_date_current_millis</code></td> -<td>Counter</td> -<td></td> -<td>The current server date in millis since the epoch, useful for ensuring the server time is correct.</td> -</tr> -<tr> -<td><code>cht_date_uptime_seconds</code></td> -<td>Counter</td> -<td></td> -<td>How long API has been running.</td> -</tr> -<tr> -<td><code>cht_feedback_total</code></td> -<td>Counter</td> -<td></td> -<td>Number of feedback docs created usually indicative of client side errors.</td> -</tr> -<tr> -<td><code>cht_messaging_outgoing_last_hundred</code></td> -<td>Gauge</td> -<td><code>group</code>, <code>status</code></td> -<td>Counts of last 100 messages that have received status updates.</td> -</tr> -<tr> -<td><code>cht_messaging_outgoing_total</code></td> -<td>Counter</td> -<td><code>status</code></td> -<td>Counts of the total number of messages.</td> -</tr> -<tr> -<td><code>cht_outbound_push_backlog_count</code></td> -<td>Gauge</td> -<td></td> -<td>Number of changes yet to be processed by Outbound Push.</td> -</tr> -<tr> -<td><code>cht_replication_limit_count</code></td> -<td>Gauge</td> -<td></td> -<td>Number of users that exceeded the replication limit of documents.</td> -</tr> -<tr> -<td><code>cht_sentinel_backlog_count</code></td> -<td>Gauge</td> -<td></td> -<td>Number of changes yet to be processed by Sentinel.</td> -</tr> -<tr> -<td><code>cht_version</code></td> -<td>N/A</td> -<td><code>app</code>, <code>node</code>, <code>couchdb</code></td> -<td>Version information for the CHT instance (recorded in labels)</td> -</tr> -<tr> -<td><code>couch2pg_progress_sequence</code></td> -<td>Counter</td> -<td><code>medic</code>, <code>medic-logs</code>, <code>medic-sentinel</code>, <code>medic-users-meta</code>, <code>_users</code></td> -<td>The number of db changes that have been processed by couch2pg. Requires <a href="#couch2pg-data">couch2pg metrics</a> be enabled.</td> -</tr> -</tbody> -</table>Hosting: Production CHT Watchdoghttps://docs.communityhealthtoolkit.org/hosting/monitoring/production/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/monitoring/production/ -<div class="pageinfo pageinfo-primary"> -<p>These instructions apply to both CHT 3.x (beyond 3.12) and CHT 4.x.</p> -</div> -<h2 id="what-it-means-to-run-in-production">What it means to run in production</h2> -<p>When you run CHT Watchdog in production, and it is publicly accessible on the Internet, and has mission-critical data on it, you should take extra precautions around security and backup. This mainly consists of:</p> -<ul> -<li>using TLS for all HTTP connections</li> -<li>using VPN or SSH for insecure protocols like <code>ssl=false</code> in Postgres</li> -<li>ensuring if the server were to fail, you can recover the data</li> -</ul> -<p>This guide assumes you have already <a href="https://docs.communityhealthtoolkit.org/hosting/4.x/adding-tls-certificates/">set up TLS</a> on your CHT instance and have gone through <a href="https://docs.communityhealthtoolkit.org/hosting/monitoring/setup/">the Setup steps</a> to deploy an instance of CHT Watchdog on server with a static IP and DNS entry, <code>monitor.example.com</code> for example.</p> -<div class="alert alert-primary" role="alert"> -<h4 class="alert-heading">Note</h4> -Always run Watchdog on a different server than the CHT Core. This ensures Watchdog doesn&rsquo;t fail if the CHT Core server fails and alerts will always be sent. The instructions assume you&rsquo;re connecting over the public Internet and no special VPN or routing is required. -</div> -<h2 id="monitoring-over-tls">Monitoring over TLS</h2> -<p>All monitoring should happen over TLS. This means the <code>cht-instances.yml</code> file should have all the URLs in it start with <code> - https</code>.</p> -<h2 id="accessing-grafana-over-tls">Accessing Grafana over TLS</h2> -<p>By default, the <code>docker-compose.yml</code> has the service bind to <code>127.0.0.1</code>. This means if you deploy it on a remote server you can not access Grafana&rsquo;s web UI because you are not on the localhost. The best solution to expose it to the Internet is to use a reverse proxy. Medic recommends using <a href="https://caddyserver.com/">Caddy</a> for this, but any reverse proxy will suffice. A big benefit with Caddy is that with just two files you ensure all traffic, and critically, all login credentials, are always encrypted when being sent and it handles all TLS certificate management tasks for you.</p> -<h3 id="reverse-proxy-and-docker-files">Reverse Proxy and Docker files</h3> -<p>Assuming you have the DNS entry of <code>monitor.example.com</code> pointing to your server, you would create the <code>Caddyfile</code> file with this code.</p> -<pre tabindex="0"><code>cat &gt; /root/Caddyfile &lt;&lt; EOF -monitor.example.com { -reverse_proxy grafana:3000 -} -EOF -</code></pre><p>Using the awesome secure defaults of Caddy, this file will tell Caddy to:</p> -<ol> -<li>Create a free certificate for <code>monitor.example.com</code> using <a href="https://letsencrypt.org/">Let&rsquo;s Encrypt</a> (and renew it!)</li> -<li>Redirect any requests to <code>HTTP</code> to go to the <code>HTTPS</code> port</li> -<li>Reverse proxy all traffic to the <code>grafana</code> docker instance.</li> -</ol> -<p>The reverse proxy will only work if the Caddy container is on the same docker network as Grafana. That&rsquo;s where the <code>caddy-compose.yml</code> file comes in, specifically using the <code>cht-watchdog-net</code> network. Create the file with this code</p> -<pre tabindex="0"><code>cat &gt; /root/caddy-compose.yml &lt;&lt; EOF -version: &#34;3.9&#34; -services: -caddy: -image: caddy:2-alpine -restart: unless-stopped -ports: -- &#34;80:80&#34; -- &#34;443:443&#34; -volumes: -- /root/Caddyfile:/etc/caddy/Caddyfile -networks: -- cht-watchdog-net -EOF -</code></pre><h3 id="running">Running</h3> -<p>To start the reverse proxy, us the following command. Note that on first run it will provision your certificates:</p> -<pre tabindex="0"><code>cd ~/cht-watchdog -docker compose -f docker-compose.yml -f ../caddy-compose.yml up -d -</code></pre><p>Because both the CHT Watchdog and Caddy compose files have the <code>restart: unless-stopped</code> setting, the services will start when the server first boots.</p> -<h3 id="upgrades">Upgrades</h3> -<p>Upgrades can be done along with upgrades to your CHT Watchdog docker images:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/cht-watchdog -</span></span><span style="display:flex;"><span>docker compose -f docker-compose.yml -f ../caddy-compose.yml pull -</span></span><span style="display:flex;"><span>docker compose -f docker-compose.yml -f ../caddy-compose.yml up -d -</span></span></code></pre></div><h2 id="backup">Backup</h2> -<p>When you deployed your CHT Watchdog instance, you created two directories:</p> -<ul> -<li><code>~/cht-watchdog/grafana/data</code></li> -<li><code>~/cht-watchdog/prometheus/data</code></li> -</ul> -<p>These are the only directories you need to back up. Whether you use something as simple as <code>zip</code> + <code>scp</code> + <code>cron</code> or a more full-featured solution like <a href="https://www.borgbackup.org/">borgbackup</a> or <a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/snapshot-lifecycle.html">AWS Data Lifecycle Manager</a>, be sure you follow <a href="https://en.wikipedia.org/wiki/Backup#Storage">the 3-2-1 backup rule</a>:</p> -<blockquote> -<p>The 3-2-1 rule can aid in the backup process. It states that there should be at least 3 copies of the data, stored on 2 different types of storage media, and one copy should be kept offsite, in a remote location</p> -</blockquote> -<h2 id="default-grafana-url">Default Grafana URL</h2> -<p>The default URL that Grafana uses is <code>http://localhost:3000</code>. In a production environment, specifically when alerts are being sent, you need to tell Grafana what its URL is. Do this by editing the <code>./grafana/grafana.ini</code> you created at install and set the <code>root_url</code> value. In this example, we&rsquo;ll set it to <code>monitor.example.com</code>:</p> -<pre tabindex="0"><code>#################################### Server ############################## -[server] -root_url = https://monitor.example.com/ -</code></pre>Hosting: Custom Postgres metrics in CHT Watchdoghttps://docs.communityhealthtoolkit.org/hosting/monitoring/postgres-ingest/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/monitoring/postgres-ingest/ -<div class="pageinfo pageinfo-primary"> -<p>These instructions apply to both CHT 3.x (beyond 3.12) and CHT 4.x.</p> -</div> -<h2 id="introduction">Introduction</h2> -<p>After <a href="https://docs.communityhealthtoolkit.org/hosting/monitoring/setup/">setting up</a> your Watchdog instance and <a href="https://docs.communityhealthtoolkit.org/hosting/monitoring/production/">making it production ready</a>, you can include additional custom metrics from your deployment. These metrics should be ingested by Prometheus and then can be used to create new Grafana dashboards and alerts. Example use cases include monitoring and alerting on health metrics like CHW visits per county or household registration rates, etc.</p> -<p>This guide will walk you through adding a custom metric from Postgres <em>data</em>. The following naming convention is used throughout to reference the relevant server instances: CHT Core (<code>cht.example.com</code>), CHT Watchdog (<code>watchdog.example.com</code>) and a Postgres server (<code>db.example.com</code>).</p> -<h3 id="base-flow">Base Flow</h3> -<p>This is the initial basic flow of data from a CHT instance to Watchdog:</p> -<pre class="mermaid">flowchart LR -subgraph core[&#34;cht.example.com&#34;] -mon_api[&#34;Monitoring API (443)&#34;] -end -subgraph watchdog[&#34;watchdog.example.com&#34;] -json[JSON Exporter] --&gt; Prometheus -Prometheus --&gt; Grafana -end -mon_api --&gt; json</pre> -<h3 id="postgres-flow">Postgres Flow</h3> -<p>This guide will have you deploy a <a href="https://github.com/prometheus-community/postgres_exporter">Postgres Exporter</a> on your Watchdog server (<code>watchdog.example.com</code>). This, in turn, will query your Postgres server (<code>db.example.com</code>):</p> -<pre class="mermaid">flowchart LR -subgraph core[&#34;cht.example.com&#34;] -mon_api[&#34;Monitoring API (443)&#34;] -end -subgraph watchdog[&#34;watchdog.example.com&#34;] -json[JSON Exporter] --&gt; Prometheus -postgres-exp[Postgres Exporter] --&gt; Prometheus -Prometheus --&gt; Grafana -end -subgraph db[&#34;db.example.com&#34;] -postgres[&#34;Postgres (5431)&#34;] -end -mon_api --&gt; json -postgres --&gt; postgres-exp</pre> -<p>Adding a custom Postgres metric</p> -<p>The following steps are all performed on the CHT Watchdog instance and assume you installed Watchdog in <code>~/cht-watchdog</code>. Note that user credentials with READ access to your Postgres server are required.</p> -<ol> -<li><a href="#prepare-query-in-config-file">Prepare query in config file</a></li> -<li><a href="#adding-new-scrape-config">Adding new scrape config</a></li> -<li><a href="#add-new-postgres-exporter">Add new Postgres Exporter</a></li> -<li><a href="#configure-the-dashboard">Configure the dashboard</a></li> -<li><a href="#optional-add-dashboard-to-cht-dropdown-in-grafana">Optional: Add Dashboard to CHT Dropdown in Grafana</a></li> -</ol> -<h3 id="prepare-query-in-config-file">Prepare query in config file</h3> -<p>Add a YAML file for with your query called <code>~/custom-sql-queries.yml</code>. In this example we&rsquo;ll be using a query from the <a href="https://github.com/medic/cht-app-monitoring-data-ingest/">App Monitoring Data Ingestion repo</a>, but it can be any query as long as the user you&rsquo;re using in the next step has access to the database and table:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">dwh_impact_replication_failure</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">query</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000;font-weight:bold">|</span><span style="color:#8f5902;font-style:italic"> -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"> SELECT -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"> metric as reason, -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"> count as total -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"> FROM -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"> public.app_monitoring_replication_failure_reasons -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"> WHERE -</span></span></span><span style="display:flex;"><span><span style="color:#8f5902;font-style:italic"> partner_name IN (&#39;partner_name_here&#39;)</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">metrics</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#204a87;font-weight:bold">reason</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">usage</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#34;LABEL&#34;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">description</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#34;Name of the failure&#34;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#204a87;font-weight:bold">total</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">usage</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#34;GAUGE&#34;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">description</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#34;Replication failure reasons&#34;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span></code></pre></div><p>This configuration will generate a metric named <code>dwh_impact_replication_failure_total</code> with a label named <code>reason</code> which contains the string key value identifying the aggregated reason for the given replication failures. These metric/label names are fully customizable, but to avoid confusion you should follow the Prometheus <a href="https://prometheus.io/docs/practices/naming/">best practices</a> when choosing names.</p> -<h3 id="adding-new-scrape-config">Adding new scrape config</h3> -<p>Create the <code>~/scrape_config.custom-sql.yml</code> file and point the config to our new Postgres Exporter (<code>custom_sql_exporter:9187</code>). This will tell Prometheus to scrape the new data every 1 minute:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">scrape_configs</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#204a87;font-weight:bold">job_name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#39;custom-sql&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">scrape_interval</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">1m</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">static_configs</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#204a87;font-weight:bold">targets</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;custom_sql_exporter:9187&#39;</span><span style="color:#000;font-weight:bold">]</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span></code></pre></div><h3 id="add-new-postgres-exporter">Add new Postgres Exporter</h3> -<p>In a new file, <code>~/docker-compose.custom-sql.yml</code>, define your new Postgres exporter as well as add a mount to the existing Grafana and Prometheus services. Note that you will need to add the following environment variables to your Watchdog <code>~/cht-watchdog/.env</code> file:</p> -<ul> -<li><code>CUSTOM_SQL_USER</code> - Postgres user to use when logging in</li> -<li><code>CUSTOM_SQL_PASS</code> - Password for <code>CUSTOM_SQL_USER</code> above</li> -<li><code>CUSTOM_SQL_SERVER</code> - URL or IP for your Postgres server</li> -<li><code>CUSTOM_SQL_PORT</code> - Port of server, defaults to <code>5432</code> it not declared.</li> -<li><code>CUSTOM_SQL_DATABASE</code> - Actual string of database name (eg <code>extra_monitoring</code> or <code>health_stats</code>), will be different for each install.</li> -</ul> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">services</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">prometheus</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">volumes</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#000">./scrape_config.custom-sql.yml:/etc/prometheus/scrape_configs/custom-sql.yml:ro</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">custom_sql_exporter</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">image</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">prometheuscommunity/postgres-exporter:latest</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">command</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#8f5902;font-style:italic"># disables the collection of all metrics except for custom queries </span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#4e9a06">&#39;--no-collector.database&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#4e9a06">&#39;--no-collector.postmaster&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#4e9a06">&#39;--no-collector.process_idle&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#4e9a06">&#39;--no-collector.replication&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#4e9a06">&#39;--no-collector.replication_slot&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#4e9a06">&#39;--no-collector.stat_bgwriter&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#4e9a06">&#39;--no-collector.stat_database&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#4e9a06">&#39;--no-collector.statio_user_tables&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#4e9a06">&#39;--no-collector.stat_statements&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#4e9a06">&#39;--no-collector.stat_user_tables&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#4e9a06">&#39;--disable-default-metrics&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#4e9a06">&#39;--disable-settings-metrics&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">volumes</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#000">../custom-sql-queries.yml:/custom-sql-queries.yml</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">environment</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">DATA_SOURCE_NAME</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#34;postgresql://${CUSTOM_SQL_USER:-NO DB USER SPECIFIED}:${CUSTOM_SQL_PASS:-NO DB PASSWORD SPECIFIED}@${CUSTOM_SQL_SERVER:-.NO DB SERVER SPECIFIED}:${CUSTOM_SQL_PORT:-5432}/${CUSTOM_SQL_DATABASE:-.NO DB SPECIFIED}?sslmode=disable&#34;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">PG_EXPORTER_EXTEND_QUERY_PATH</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#34;/custom-sql-queries.yml&#34;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">restart</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">always</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">networks</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#000">cht-watchdog-net</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span></code></pre></div><p>Launch Watchdog with the new compose file</p> -<p>Now that you&rsquo;ve added the new configuration files, we can load it alongside the existing ones. Assuming you&rsquo;ve followed the <a href="https://docs.communityhealthtoolkit.org/hosting/monitoring/setup/">Watchdog Setup</a>, this would be:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/cht-watchdog -</span></span><span style="display:flex;"><span>docker compose -f docker-compose.yml -f ../docker-compose.custom-sql.yml up -d -</span></span></code></pre></div><h3 id="configure-the-dashboard">Configure the dashboard</h3> -<p>Now that the new Postgres Exporter is running on your Watchdog instance and CHT Watchdog&rsquo;s Prometheus has additional scrape configs to ingest the new metrics, we can now visualize it in a Grafana Dashboard and then alert on it:</p> -<ol> -<li>In the &ldquo;Metric&rdquo; field enter <code>dwh_impact_replication_failure_total</code> from the step above where we defined <code>custom-sql-queries.yml</code></li> -<li>Click the blue &ldquo;Run query&rdquo; in the upper right.</li> -<li>We&rsquo;ll make this a table, but you can configure the dashboard as desired.</li> -<li>Click &ldquo;Add to dashboard&rdquo;</li> -</ol> -<p><img src="explore.png" alt="Grafana showing data data explorer"></p> -<h3 id="optional-add-dashboard-to-cht-dropdown-in-grafana">Optional: Add Dashboard to CHT Dropdown in Grafana</h3> -<p>An additional optional step is to make your dashboard a peer of the existing &ldquo;Admin Details&rdquo; and &ldquo;Admin Overview&rdquo;. Do this by editing the JSON by finding the line with <code>&quot;graphTooltip&quot;: 0,</code> and add this JSON after it:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;links&#34;</span><span style="color:#a40000">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;asDropdown&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;icon&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;external link&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;includeVars&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;keepTime&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;tags&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;targetBlank&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;title&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;CHT Admin Extra SQL&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;tooltip&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;dashboards&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;url&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span><span style="color:#a40000">,</span> -</span></span></code></pre></div><p>This will make your new dashboard show up natively with the two existing CHT dashboards:</p> -<p><img src="menu.png" alt="Grafana with a third &ldquo;Admin Extra SQL&rdquo; option showing in the existing CHT navigation menu"></p> -<h4 id="full-dashboard-json">Full Dashboard JSON</h4> -<p>For reference, here is the full JSON of the dashboard we created above as shown in the &ldquo;Save&rdquo; modal:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-JSON" data-lang="JSON"><span style="display:flex;"><span><span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;annotations&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;list&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;builtIn&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;datasource&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;grafana&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;uid&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;-- Grafana --&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;enable&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;hide&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;iconColor&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;rgba(0, 211, 255, 1)&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;name&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Annotations &amp; Alerts&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;dashboard&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;editable&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;fiscalYearStartMonth&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;graphTooltip&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;links&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;asDropdown&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;icon&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;external link&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;includeVars&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;keepTime&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;tags&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;targetBlank&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;title&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;CHT Admin Extra SQL&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;tooltip&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;dashboards&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;url&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;liveNow&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;panels&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;datasource&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;prometheus&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;uid&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;PBFA97CFB590B2093&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;fieldConfig&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;defaults&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;custom&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;align&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;auto&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;cellOptions&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;auto&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;inspect&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;mappings&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;thresholds&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;mode&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;absolute&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;steps&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;color&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;green&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;value&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">null</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;color&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;red&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;value&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">80</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;unit&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;short&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;overrides&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;matcher&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;byName&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;options&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;__name__&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;properties&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;custom.hidden&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;value&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;matcher&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;byName&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;options&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;instance&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;properties&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;custom.hidden&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;value&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;matcher&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;byName&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;options&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;job&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;properties&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;custom.hidden&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;value&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;matcher&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;byName&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;options&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;server&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;properties&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;custom.hidden&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;value&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;matcher&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;byName&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;options&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Time&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;properties&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;custom.hidden&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;value&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;matcher&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;byName&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;options&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;failure&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;properties&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;custom.width&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;value&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">462</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;gridPos&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;h&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">10</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;w&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">18</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;x&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;y&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">0</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;id&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;options&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;cellHeight&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;sm&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;footer&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;countRows&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;fields&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;reducer&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#4e9a06">&#34;sum&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;show&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;showHeader&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;sortBy&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;desc&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;displayName&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Value&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;pluginVersion&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;10.0.1&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;targets&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;datasource&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;prometheus&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;uid&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;PBFA97CFB590B2093&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;editorMode&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;builder&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;exemplar&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;expr&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;dwh_impact_replication_failure_total&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;format&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;table&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;instant&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">true</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;key&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Q-e238fdbd-aed6-4215-a3e8-c611c6586c64-0&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;legendFormat&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;range&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#204a87;font-weight:bold">false</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;refId&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;A&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;title&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;Replication failure reason&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;type&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;table&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">}</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;refresh&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;5s&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;schemaVersion&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">38</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;style&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;dark&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;tags&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[],</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;templating&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;list&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">[]</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;time&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;from&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;now-5m&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;to&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;now&#34;</span> -</span></span><span style="display:flex;"><span> <span style="color:#000;font-weight:bold">},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;timepicker&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#000;font-weight:bold">{},</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;timezone&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;title&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;CHT Admin Extra SQL&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;uid&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;a71db640-cc40-452c-aa92-222a9b49d43b&#34;</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;version&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#0000cf;font-weight:bold">8</span><span style="color:#000;font-weight:bold">,</span> -</span></span><span style="display:flex;"><span> <span style="color:#204a87;font-weight:bold">&#34;weekStart&#34;</span><span style="color:#000;font-weight:bold">:</span> <span style="color:#4e9a06">&#34;&#34;</span> -</span></span><span style="display:flex;"><span><span style="color:#000;font-weight:bold">}</span> -</span></span></code></pre></div>Hosting: Integrating CHT Watchdoghttps://docs.communityhealthtoolkit.org/hosting/monitoring/integration/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/monitoring/integration/ -<div class="pageinfo pageinfo-primary"> -<p>These instructions apply to both CHT 3.x (beyond 3.12) and CHT 4.x.</p> -</div> -<h2 id="going-beyond-basic-setup">Going beyond basic setup</h2> -<p>After you have done the <a href="https://docs.communityhealthtoolkit.org/hosting/monitoring/setup/">setup of CHT Watchdog</a> and configured it to run <a href="https://docs.communityhealthtoolkit.org/hosting/monitoring/production/">with TLS and have backups enabled</a>, you may want to extend it to scrape other Prometheus data sources so that Grafana can send alerts on non-CHT Core metrics.</p> -<p>This guide uses example instances of CHT Core (<code>cht.example.com</code>) and CHT Watchdog (<code>watchdog.example.com</code>). When deploying, be sure to replace with your own hostnames.</p> -<h3 id="default-flow">Default Flow</h3> -<p>Let&rsquo;s look at how the default deployment of Watchdog works when configured to only gather metrics from <a href="https://docs.communityhealthtoolkit.org/apps/reference/api/#get-apiv2monitoring">CHT Core&rsquo;s monitoring API</a>:</p> -<pre class="mermaid">flowchart LR -subgraph core[&#34;cht.example.com&#34;] -mon_api[&#34;Monitoring API (443)&#34;]:::client_node -end -subgraph watchdog[&#34;watchdog.example.com&#34;] -json[JSON Exporter] --&gt; Prometheus -Prometheus --&gt; Grafana -end -mon_api --&gt; json</pre> -<h3 id="additional-flows">Additional Flows</h3> -<p>Your Prometheus instance from CHT Watchdog can ingest data from any <a href="https://prometheus.io/docs/instrumenting/exporters/">supported data source</a> accessible via an HTTPS request. These data sources might be hosted on the same server as CHT Core or on a completely different server.</p> -<p>The focus of this guide is to collect metrics on Docker container usage and performance from the server hosting our CHT Core instance using <a href="https://prometheus.io/docs/guides/cadvisor/">cAdvisor</a>.</p> -<pre class="mermaid">flowchart LR -subgraph core[&#34;cht.example.com&#34;] -mon_api[&#34;Monitoring API (443)&#34;]:::client_node -cAdvisor:::client_node -end -subgraph watchdog[&#34;watchdog.example.com&#34;] -json[JSON Exporter] --&gt; Prometheus -Prometheus --&gt; Grafana -end -mon_api --&gt; json -cAdvisor[&#34;cAdvisor (8443)&#34;] --&gt; Prometheus</pre> -<p>Note that because CHT Core is listening on port 443 already, we&rsquo;ll have cAdvisor listen on port 8443.</p> -<p>By reading this guide you should not only be able to set up cAdvisor, but also be familiar with extending CHT Watchdog to support any other vital metrics.</p> -<h3 id="steps-to-new-integrations">Steps to new integrations</h3> -<p>While this is a specific example for cAdvisor, these same steps will be taken to extend Watchdog for other metrics:</p> -<ol> -<li>CHT Core: <a href="#cadvisor-compose-file">Create both cAdvisor and Caddy Docker Compose files</a></li> -<li>CHT Core: <a href="#start-cadvisor-caddy-and-cht-core-with-docker">Start the Caddy and a cAdvisor containers along with the CHT Core</a></li> -<li>CHT Watchdog: <a href="#scrape-config">Adding new scrape and compose configs</a></li> -<li>CHT Watchdog: <a href="#load-new-compose-files-with-existing-ones">Restart the Prometheus and Grafana server to include the new scrape config mounts</a></li> -<li>CHT Watchdog: <a href="#on-cht-watchdog-import-grafana-dashboard">Importing an existing cAdvisor dashboard from <code>grafana.com</code></a></li> -</ol> -<p>After completing these steps, we now have Docker metrics we can alert on:</p> -<p><a href="cadvisor.screenshot.png"><img src="cadvisor.screenshot.png" alt="Screenshot of Grafana Dashboard showing data from Prometheus"></a></p> -<p>Read on below on how to set this up!</p> -<h2 id="integrating-with-cadvisor">Integrating with cAdvisor</h2> -<h3 id="on-cht-core">On CHT Core</h3> -<h4 id="cadvisor-compose-file">cAdvisor Compose file</h4> -<p>On your CHT instance, add a Docker composer file for the new cAdvisor service. Note this also includes a Redis caching layer. Also note that we&rsquo;re reducing cAdvisors CPU usage by adding 3 extra flags in the <code>command</code> stanza. In our example, we&rsquo;ve put this file in <code>/home/ubuntu/cht/compose/cadvisor_compose.yml</code> with these contents:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">version</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#39;3.9&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">services</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">cadvisor</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">image</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">gcr.io/cadvisor/cadvisor:latest</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">container_name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">cadvisor</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">volumes</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#000">/:/rootfs:ro</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#000">/var/run:/var/run:rw</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#000">/sys:/sys:ro</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#000">/var/lib/docker/:/var/lib/docker:ro</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">depends_on</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#000">redis</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">networks</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#000">cht-net</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">command</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#4e9a06">&#34;--housekeeping_interval=30s&#34;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#4e9a06">&#34;--docker_only=true&#34;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#4e9a06">&#34;--disable_metrics=percpu,sched,tcp,udp,disk,diskIO,accelerator,hugetlb,referenced_memory,cpu_topology,resctrl&#34;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">redis</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">image</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">redis:latest</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">container_name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">redis</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">networks</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#000">cht-net</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span></code></pre></div><h4 id="caddy-config-and-compose-files">Caddy Config and Compose files</h4> -<p>Like we did in the <a href="https://docs.communityhealthtoolkit.org/hosting/monitoring/production/#accessing-grafana-over-tls">TLS section</a>, we&rsquo;ll add both a <code>/home/ubuntu/Caddyfile</code> and a <code>/home/ubuntu/cht/compose/caddy-compose.yml</code>.</p> -<p>Starting with the <code>Caddyfile</code>, let&rsquo;s assume your server&rsquo;s DNS entry is <code>cht.example.com</code>. We can expose cAdvisor&rsquo;s service running on localhost port <code>8443</code> with this compose file. This tells Caddy to reverse proxy requests to the public interface to the private Docker network interface on port <code>8080</code> where cAdvisor is running:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#000">cht.example.com:8443 {</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">reverse_proxy cadvisor:8080</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span>}<span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span></code></pre></div><p>Then we can add the compose file to run Caddy. Note that it&rsquo;s mounting the config file we just created:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">version</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#34;3.9&#34;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">services</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">caddy</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">image</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">caddy:2-alpine</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">restart</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">unless-stopped</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">ports</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#4e9a06">&#34;8443:8443&#34;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">volumes</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#000">/home/ubuntu/Caddyfile:/etc/caddy/Caddyfile</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">networks</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#000">cht-net</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span></code></pre></div><h4 id="start-cadvisor-caddy-and-cht-core-with-docker">Start cAdvisor, Caddy and CHT Core with Docker</h4> -<p>Now that we have all the config files in place, you need to have Docker start everything together. This is so that the containers can see each other on the same <code>CHT Net</code> Docker network. You will need to specify each of the compose files every time you start, stop or restart CHT instance so all the services stay running and connected.</p> -<p>Assuming you followed the <a href="https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/">production steps</a> to install the CHT, you use this Compose call to first stop all containers and then start them all up, including the new services:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> /home/ubuntu/cht/upgrade-service -</span></span><span style="display:flex;"><span>docker stop <span style="color:#204a87;font-weight:bold">$(</span>docker ps --quiet<span style="color:#204a87;font-weight:bold">)</span> -</span></span><span style="display:flex;"><span>docker compose up --detach -</span></span></code></pre></div><p>Note that the CHT Upgrade Service will process all Docker Compose file in the <code>/home/ubuntu/cht/compose</code> directory for us and we don&rsquo;t need to explicitly specify them in the <code>docker compose up</code> command.</p> -<h3 id="on-cht-watchdog">On CHT Watchdog</h3> -<h4 id="scrape-config">Scrape config</h4> -<p>We&rsquo;ll first create the <code>~/cadvisor-prometheus-conf.yml</code> file and point the config to our CHT Core URL:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">scrape_configs</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#204a87;font-weight:bold">job_name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#39;cadvisor&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">scrape_interval</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000">5s</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">scheme</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#39;https&#39;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">static_configs</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#204a87;font-weight:bold">targets</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#000;font-weight:bold">[</span><span style="color:#4e9a06">&#39;cht.example.com:8443&#39;</span><span style="color:#000;font-weight:bold">]</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span></code></pre></div><p>CHT Watchdog allows you to use additional Docker Compose files to add as many additional Prometheus scrape configs as are needed. Here, we&rsquo;ll create one in <code>~/cadvisor-compose.yml</code> pointing to our <code>cadvisor-prometheus-conf.yml</code> file from above.</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#204a87;font-weight:bold">version</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#4e9a06">&#34;3.9&#34;</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">services</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">prometheus</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">volumes</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span><span style="display:flex;"><span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#000">/root/cadvisor-prometheus-conf.yml:/etc/prometheus/scrape_configs/cadvisor.yml:ro</span><span style="color:#f8f8f8;text-decoration:underline"> -</span></span></span></code></pre></div><h4 id="load-new-compose-files-with-existing-ones">Load new Compose files with existing ones</h4> -<p>Now that you&rsquo;ve added the new configuration files, we can load it alongside the existing ones. Assuming you&rsquo;ve followed the <a href="https://docs.communityhealthtoolkit.org/hosting/monitoring/setup/">Watchdog Setup</a>, this would be:</p> -<div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#204a87">cd</span> ~/cht-monitoring -</span></span><span style="display:flex;"><span>docker compose -f docker-compose.yml -f ../cadvisor-compose.yml up -d -</span></span></code></pre></div><h4 id="import-grafana-dashboard">Import Grafana Dashboard</h4> -<p>Now that cAdvisor is running on your CHT Core instance and CHT Watchdog&rsquo;s Prometheus has additional scrape configs to ingest the cAdvisor metrics, we can now visualize it in a Grafana Dashboard and then alert on it.</p> -<ol> -<li>Log into your Watchdog instance</li> -<li>Click the upper left hamburger menu and click &ldquo;Dashboards&rdquo;</li> -<li>Find the &ldquo;New&rdquo; button on the left, click and choose &ldquo;Import&rdquo; from the drop down</li> -<li>On the next page, scroll down to find &ldquo;Import via grafana.com&rdquo;, enter ID <code>193</code> (for the <a href="https://grafana.com/grafana/dashboards/193-docker-monitoring/">Docker monitoring</a> dashboard) and click &ldquo;Load&rdquo;</li> -<li>Confirm the &ldquo;Name&rdquo; and &ldquo;Folder&rdquo; values and select &ldquo;Prometheus&rdquo; as the data source in the dropdown. Finally, click the &ldquo;Import&rdquo; button at the bottom of the page.</li> -</ol> -<p>That&rsquo;s it! After following these steps, you should be looking at the cAdvisor dashboard <a href="#steps-to-new-integrations">as shown above</a>. From here, you can both customize this dashboard as well as add alerts as needed.</p> \ No newline at end of file +Monitoring and Alerting on Community Health Toolkithttps://docs.communityhealthtoolkit.org/hosting/monitoring/Recent content in Monitoring and Alerting on Community Health ToolkitHugo -- gohugo.ioenIntroduction to monitoring and alertinghttps://docs.communityhealthtoolkit.org/hosting/monitoring/introduction/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/monitoring/introduction/This guide applies to all production instances of the CHT for both 3.x (beyond 3.9) and 4.x. +Be sure to see how to deploy a solution to monitor and alert on production CHT instances. +Each deployment will experience different stresses on its resources. Be sure to tune any alerting levels in the case of a false positive so that you may avoid them in the future. Any thresholds for alerts, and even what is alerted on, is just a guideline, not a guarantee of uptime.CHT Watchdog Setuphttps://docs.communityhealthtoolkit.org/hosting/monitoring/setup/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/monitoring/setup/These instructions apply to both CHT 3.x (beyond 3.12) and CHT 4.x. +Medic maintains CHT Watchdog which is an opinionated configuration of Prometheus (including json_exporter) and Grafana which can easily be deployed using Docker. It is supported on CHT 3.12 and later, including CHT 4.x. By using this solution a CHT deployment can easily get longitudinal monitoring and push alerts using Email, Slack or other mechanisms. All tools are open source and have no licensing fees.Production CHT Watchdoghttps://docs.communityhealthtoolkit.org/hosting/monitoring/production/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/monitoring/production/These instructions apply to both CHT 3.x (beyond 3.12) and CHT 4.x. +What it means to run in production When you run CHT Watchdog in production, and it is publicly accessible on the Internet, and has mission-critical data on it, you should take extra precautions around security and backup. This mainly consists of: +using TLS for all HTTP connections using VPN or SSH for insecure protocols like ssl=false in Postgres ensuring if the server were to fail, you can recover the data This guide assumes you have already set up TLS on your CHT instance and have gone through the Setup steps to deploy an instance of CHT Watchdog on server with a static IP and DNS entry, monitor.Custom Postgres metrics in CHT Watchdoghttps://docs.communityhealthtoolkit.org/hosting/monitoring/postgres-ingest/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/monitoring/postgres-ingest/These instructions apply to both CHT 3.x (beyond 3.12) and CHT 4.x. +Introduction After setting up your Watchdog instance and making it production ready, you can include additional custom metrics from your deployment. These metrics should be ingested by Prometheus and then can be used to create new Grafana dashboards and alerts. Example use cases include monitoring and alerting on health metrics like CHW visits per county or household registration rates, etc.Integrating CHT Watchdoghttps://docs.communityhealthtoolkit.org/hosting/monitoring/integration/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/monitoring/integration/These instructions apply to both CHT 3.x (beyond 3.12) and CHT 4.x. +Going beyond basic setup After you have done the setup of CHT Watchdog and configured it to run with TLS and have backups enabled, you may want to extend it to scrape other Prometheus data sources so that Grafana can send alerts on non-CHT Core metrics. +This guide uses example instances of CHT Core (cht.example.com) and CHT Watchdog (watchdog. \ No newline at end of file diff --git a/hosting/monitoring/integration/index.html b/hosting/monitoring/integration/index.html index fe23689087..d31df4d75c 100644 --- a/hosting/monitoring/integration/index.html +++ b/hosting/monitoring/integration/index.html @@ -1,9 +1,9 @@ -Integrating CHT Watchdog | Community Health Toolkit +Integrating CHT Watchdog | Community Health Toolkit

    Integrating CHT Watchdog

    Scraping and alerting external sources with CHT Watchdog

    These instructions apply to both CHT 3.x (beyond 3.12) and CHT 4.x.

    Going beyond basic setup

    After you have done the setup of CHT Watchdog and configured it to run with TLS and have backups enabled, you may want to extend it to scrape other Prometheus data sources so that Grafana can send alerts on non-CHT Core metrics.

    This guide uses example instances of CHT Core (cht.example.com) and CHT Watchdog (watchdog.example.com). When deploying, be sure to replace with your own hostnames.

    Default Flow

    Let’s look at how the default deployment of Watchdog works when configured to only gather metrics from CHT Core’s monitoring API:

    flowchart LR
    + Create project issue

    Integrating CHT Watchdog

    Scraping and alerting external sources with CHT Watchdog

    These instructions apply to both CHT 3.x (beyond 3.12) and CHT 4.x.

    Going beyond basic setup

    After you have done the setup of CHT Watchdog and configured it to run with TLS and have backups enabled, you may want to extend it to scrape other Prometheus data sources so that Grafana can send alerts on non-CHT Core metrics.

    This guide uses example instances of CHT Core (cht.example.com) and CHT Watchdog (watchdog.example.com). When deploying, be sure to replace with your own hostnames.

    Default Flow

    Let’s look at how the default deployment of Watchdog works when configured to only gather metrics from CHT Core’s monitoring API:

    flowchart LR
     
     subgraph core["cht.example.com"]
       mon_api["Monitoring API (443)"]:::client_node
    @@ -378,7 +378,63 @@
     

    Load new Compose files with existing ones

    Now that you’ve added the new configuration files, we can load it alongside the existing ones. Assuming you’ve followed the Watchdog Setup, this would be:

    cd ~/cht-monitoring
     docker compose -f docker-compose.yml -f ../cadvisor-compose.yml up -d
     

    Import Grafana Dashboard

    Now that cAdvisor is running on your CHT Core instance and CHT Watchdog’s Prometheus has additional scrape configs to ingest the cAdvisor metrics, we can now visualize it in a Grafana Dashboard and then alert on it.

    1. Log into your Watchdog instance
    2. Click the upper left hamburger menu and click “Dashboards”
    3. Find the “New” button on the left, click and choose “Import” from the drop down
    4. On the next page, scroll down to find “Import via grafana.com”, enter ID 193 (for the Docker monitoring dashboard) and click “Load”
    5. Confirm the “Name” and “Folder” values and select “Prometheus” as the data source in the dropdown. Finally, click the “Import” button at the bottom of the page.

    That’s it! After following these steps, you should be looking at the cAdvisor dashboard as shown above. From here, you can both customize this dashboard as well as add alerts as needed.

    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/hosting/monitoring/introduction/index.html b/hosting/monitoring/introduction/index.html index a332b10cd7..47e238f748 100644 --- a/hosting/monitoring/introduction/index.html +++ b/hosting/monitoring/introduction/index.html @@ -1,9 +1,9 @@ -Introduction to monitoring and alerting | Community Health Toolkit +Introduction to monitoring and alerting | Community Health Toolkit

    Introduction to monitoring and alerting

    High level approach to monitoring and alerting with CHT applications

    This guide applies to all production instances of the CHT for both 3.x (beyond 3.9) and 4.x.

    Be sure to see how to deploy a solution to monitor and alert on production CHT instances.

    Each deployment will experience different stresses on its resources. Be sure to tune any alerting levels in the case of a false positive so that you may avoid them in the future. Any thresholds for alerts, and even what is alerted on, is just a guideline, not a guarantee of uptime.

    Monitoring vs Alerting

    Monitoring allows CHT admins to see statistics about their server, often over time. This can be helpful when you want to be aware of growth in your deployment (eg number of active users or number of reports per region). It should not be assumed that these will be checked regularly enough to notice a problem, for example a spike in number of feedback documents.

    Alerting is a push mechanism designed to notify users who can act on the alert. These can go over SMS, email, Slack, WhatsApp or any other channel to notify the right users.

    The process of setting up monitoring and alerting should be done together. Monitoring sets the baseline and then alerting tells admins when the metric has gone beyond the baseline to a critical state. Certain metrics, like uptime for example, likely do not need to have a monitoring visualization on a dashboard, but the monitoring system should still be the authority to send an alert to denote when the service has restarted unexpectedly.

    Outside the CHT

    Be sure to monitor important items that the CHT depends on in order to be healthy. You should alert when any of these are close to their maximum (disk space) or minimum (days left of valid TLS certificate):

    • Domain expiration with registrar
    • TLS certificate expiration
    • Disk & swap space
    • CPU utilization
    • Memory utilization
    • Network utilization
    • Process count
    • OS Uptime

    Inside the CHT

    The monitoring API was added in 3.9.0 and does not require any authentication and so can easily be used with third party tools as they do not need a CHT user account.

    All metrics need to be monitored over time so that you can easily see longitudinal patterns when debugging an outage or slow down.

    Specific of monitoring

    Explosive Growth

    Many of the values in the monitoring API do not mean much in isolation. For example if an instance has 10,714,278 feedback docs, is that bad? If it’s years old and has thousands of users, then this is normal. If it is 4 months old and has 100 users, this is a dire problem!

    You should monitor these metrics for unexpected growth as measured by percent change over 24 hours. Ideally this can be subjectively calculated when it is more than 5% growth than the prior day. They’re marked as growth in the table below.

    Non-Zero Values

    Other values should always be zero, and you should alert when they are not. You may opt to alert only when they are non-zero for more than 24 hours. These are marked as non-zero in the table below.

    Zero or Near Zero Values

    Finally, these values should always be not zero, and you should alert when are zero or very close to it. You may opt to alert only when they are zero for more than 24 hours. They’re marked with zero below.

    Elements, types and samples

    The names below are extrapolated from the paths in the JSON returned by the API and should be easy to find when viewing the Monitoring API URL on your CHT instance:

    NameTypeExample Value
    Conflict Countgrowth23,318
    CouchDB Medic Doc Countgrowth16,254,271
    CouchDB Medic Fragmentationgrowth1.4366029665729645
    CouchDB Sentinel Doc Countgrowth15,756,449
    CouchDB Sentinel Fragmentationgrowth2.388733774539664
    CouchDB Users Doc Countgrowth535
    CouchDB Users Fragmentationgrowth2.356411021364134
    CouchDB Users Meta Doc Countgrowth10,761,549
    Feedback Countgrowth10,714,368
    Messaging Outgoing State Duegrowth3,807
    Messaging Outgoing State Failednon-zero0
    Outbound Push Backlognon-zero0
    Sentinel Backlognon-zero0
    Date Uptimezero1,626,508.148
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/hosting/monitoring/postgres-ingest/index.html b/hosting/monitoring/postgres-ingest/index.html index c9a33b6ed9..e7afcc0baa 100644 --- a/hosting/monitoring/postgres-ingest/index.html +++ b/hosting/monitoring/postgres-ingest/index.html @@ -1,9 +1,9 @@ -Custom Postgres metrics in CHT Watchdog | Community Health Toolkit +Custom Postgres metrics in CHT Watchdog | Community Health Toolkit

    Custom Postgres metrics in CHT Watchdog

    Adding Custom Postgres metrics into CHT Watchdog

    These instructions apply to both CHT 3.x (beyond 3.12) and CHT 4.x.

    Introduction

    After setting up your Watchdog instance and making it production ready, you can include additional custom metrics from your deployment. These metrics should be ingested by Prometheus and then can be used to create new Grafana dashboards and alerts. Example use cases include monitoring and alerting on health metrics like CHW visits per county or household registration rates, etc.

    This guide will walk you through adding a custom metric from Postgres data. The following naming convention is used throughout to reference the relevant server instances: CHT Core (cht.example.com), CHT Watchdog (watchdog.example.com) and a Postgres server (db.example.com).

    Base Flow

    This is the initial basic flow of data from a CHT instance to Watchdog:

    flowchart LR
    + Create project issue

    Custom Postgres metrics in CHT Watchdog

    Adding Custom Postgres metrics into CHT Watchdog

    These instructions apply to both CHT 3.x (beyond 3.12) and CHT 4.x.

    Introduction

    After setting up your Watchdog instance and making it production ready, you can include additional custom metrics from your deployment. These metrics should be ingested by Prometheus and then can be used to create new Grafana dashboards and alerts. Example use cases include monitoring and alerting on health metrics like CHW visits per county or household registration rates, etc.

    This guide will walk you through adding a custom metric from Postgres data. The following naming convention is used throughout to reference the relevant server instances: CHT Core (cht.example.com), CHT Watchdog (watchdog.example.com) and a Postgres server (db.example.com).

    Base Flow

    This is the initial basic flow of data from a CHT instance to Watchdog:

    flowchart LR
     
     subgraph core["cht.example.com"]
       mon_api["Monitoring API (443)"]
    @@ -602,7 +602,63 @@
       "weekStart": ""
     }
     
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/hosting/monitoring/production/index.html b/hosting/monitoring/production/index.html index efb2fbea2c..831dd741c0 100644 --- a/hosting/monitoring/production/index.html +++ b/hosting/monitoring/production/index.html @@ -1,9 +1,9 @@ -Production CHT Watchdog | Community Health Toolkit +Production CHT Watchdog | Community Health Toolkit

    Production CHT Watchdog

    Production considerations for CHT Watchdog

    These instructions apply to both CHT 3.x (beyond 3.12) and CHT 4.x.

    What it means to run in production

    When you run CHT Watchdog in production, and it is publicly accessible on the Internet, and has mission-critical data on it, you should take extra precautions around security and backup. This mainly consists of:

    • using TLS for all HTTP connections
    • using VPN or SSH for insecure protocols like ssl=false in Postgres
    • ensuring if the server were to fail, you can recover the data

    This guide assumes you have already set up TLS on your CHT instance and have gone through the Setup steps to deploy an instance of CHT Watchdog on server with a static IP and DNS entry, monitor.example.com for example.

    Monitoring over TLS

    All monitoring should happen over TLS. This means the cht-instances.yml file should have all the URLs in it start with - https.

    Accessing Grafana over TLS

    By default, the docker-compose.yml has the service bind to 127.0.0.1. This means if you deploy it on a remote server you can not access Grafana’s web UI because you are not on the localhost. The best solution to expose it to the Internet is to use a reverse proxy. Medic recommends using Caddy for this, but any reverse proxy will suffice. A big benefit with Caddy is that with just two files you ensure all traffic, and critically, all login credentials, are always encrypted when being sent and it handles all TLS certificate management tasks for you.

    Reverse Proxy and Docker files

    Assuming you have the DNS entry of monitor.example.com pointing to your server, you would create the Caddyfile file with this code.

    cat > /root/Caddyfile << EOF
    + Create project issue

    Production CHT Watchdog

    Production considerations for CHT Watchdog

    These instructions apply to both CHT 3.x (beyond 3.12) and CHT 4.x.

    What it means to run in production

    When you run CHT Watchdog in production, and it is publicly accessible on the Internet, and has mission-critical data on it, you should take extra precautions around security and backup. This mainly consists of:

    • using TLS for all HTTP connections
    • using VPN or SSH for insecure protocols like ssl=false in Postgres
    • ensuring if the server were to fail, you can recover the data

    This guide assumes you have already set up TLS on your CHT instance and have gone through the Setup steps to deploy an instance of CHT Watchdog on server with a static IP and DNS entry, monitor.example.com for example.

    Monitoring over TLS

    All monitoring should happen over TLS. This means the cht-instances.yml file should have all the URLs in it start with - https.

    Accessing Grafana over TLS

    By default, the docker-compose.yml has the service bind to 127.0.0.1. This means if you deploy it on a remote server you can not access Grafana’s web UI because you are not on the localhost. The best solution to expose it to the Internet is to use a reverse proxy. Medic recommends using Caddy for this, but any reverse proxy will suffice. A big benefit with Caddy is that with just two files you ensure all traffic, and critically, all login credentials, are always encrypted when being sent and it handles all TLS certificate management tasks for you.

    Reverse Proxy and Docker files

    Assuming you have the DNS entry of monitor.example.com pointing to your server, you would create the Caddyfile file with this code.

    cat > /root/Caddyfile << EOF
     monitor.example.com {
         reverse_proxy grafana:3000
     }
    @@ -328,7 +328,8 @@
     [server]
     root_url = https://monitor.example.com/
     
    -

    \ No newline at end of file + Create project issue \ No newline at end of file diff --git a/hosting/monitoring/setup/index.html b/hosting/monitoring/setup/index.html index 9d481715b0..d31cd4271a 100644 --- a/hosting/monitoring/setup/index.html +++ b/hosting/monitoring/setup/index.html @@ -1,9 +1,9 @@ -CHT Watchdog Setup | Community Health Toolkit +CHT Watchdog Setup | Community Health Toolkit

    CHT Watchdog Setup

    Setting up Grafana and Prometheus with the CHT

    These instructions apply to both CHT 3.x (beyond 3.12) and CHT 4.x.

    Medic maintains CHT Watchdog which is an opinionated configuration of Prometheus (including json_exporter) and Grafana which can easily be deployed using Docker. It is supported on CHT 3.12 and later, including CHT 4.x. By using this solution a CHT deployment can easily get longitudinal monitoring and push alerts using Email, Slack or other mechanisms. All tools are open source and have no licensing fees.

    The solution provides both an overview dashboard as well as a detail dashboard. Here is a portion of the overview dashboard:

    Screenshot of Grafana Dashboard showing data from Prometheus

    Prometheus supports four metric types: Counter, Gauge, Histogram, and Summary. Currently, the CHT only provides Counter and Gauge type metrics. When building panels for Grafana dashboards, Prometheus Functions can be used to manipulate the metric data. Refer to the Grafana Documentation for best practices on building dashboards.

    Prerequisites

    Setup

    These instructions have been tested against Ubuntu, but should work against any OS that meets the prerequisites. They follow a happy path assuming you need to only set a secure password and specify the URL(s) to monitor:

    1. Run the following commands to clone this repository, initialize your .env file, create a secure password and create your data directories:

      CHT Watchdog Setup

      Setting up Grafana and Prometheus with the CHT

      These instructions apply to both CHT 3.x (beyond 3.12) and CHT 4.x.

      Medic maintains CHT Watchdog which is an opinionated configuration of Prometheus (including json_exporter) and Grafana which can easily be deployed using Docker. It is supported on CHT 3.12 and later, including CHT 4.x. By using this solution a CHT deployment can easily get longitudinal monitoring and push alerts using Email, Slack or other mechanisms. All tools are open source and have no licensing fees.

      The solution provides both an overview dashboard as well as a detail dashboard. Here is a portion of the overview dashboard:

      Screenshot of Grafana Dashboard showing data from Prometheus

      Prometheus supports four metric types: Counter, Gauge, Histogram, and Summary. Currently, the CHT only provides Counter and Gauge type metrics. When building panels for Grafana dashboards, Prometheus Functions can be used to manipulate the metric data. Refer to the Grafana Documentation for best practices on building dashboards.

      Prerequisites

      Setup

      These instructions have been tested against Ubuntu, but should work against any OS that meets the prerequisites. They follow a happy path assuming you need to only set a secure password and specify the URL(s) to monitor:

      1. Run the following commands to clone this repository, initialize your .env file, create a secure password and create your data directories:

        cd ~
         git clone https://github.com/medic/cht-watchdog.git
         cd cht-watchdog
         cp cht-instances.example.yml cht-instances.yml
        @@ -350,7 +350,8 @@
         CHT Core

        The different pieces of a CHT project, how they interact, and what they’re used for

        CHT Core Framework > Overview > CHT Watchdog

        An open source monitoring system using Grafana and Prometheus

      -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/hosting/requirements/index.html b/hosting/requirements/index.html index a9376d9096..7de2b5e634 100644 --- a/hosting/requirements/index.html +++ b/hosting/requirements/index.html @@ -1,9 +1,9 @@ -CHT hosting requirements | Community Health Toolkit +CHT hosting requirements | Community Health Toolkit

    CHT hosting requirements

    Requirements for hosting CHT applications

    For production CHT deployments, Linux is recommended, with Ubuntu the most commonly used. For App Developer Hosting, Linux or macOS may be used. Windows can be used for either, but without recommendation.

    Per the Kubernetes vs Docker page, CHT Core can be deployed with either Docker or Kubernetes.

    CHT 3.x is End-of-Life and us no longer supported. All requirements below apply to CHT 4.x.

    Docker Compose App Developer Hosting

    Kubernetes Production Hosting

    This guide refers to “Kubernetes”, but Medic recommends a lightweight orchestrator called K3s for bare-metal hosts. The requirements below refer to K3s deployments but can be translated to other Kubernetes hosting. For example, for cloud hosting, we recommend Amazon Elastic Kubernetes Service (EKS) and we’ve also assisted in a large K3s deployment based on VMWare.

    Be sure to see the cht-deploy script that leverage the helm application.

    • 1 x HA control-plane nodes: 2 GB RAM / 2 CPU / 20 GB SSD
    • 3 x worker servers: 16 GB RAM / 8 CPU / 50 GB SSD
    • 500GB storage area network (SAN)* - Will host Persistent Volume Claims
    • Root Access
    • Static IP with DNS entry - Kubernetes will use this to provision a valid TLS certificate
    • helm application
    • K3s
    • Current version of docker (used to bootstrap K3s)

    * During some upgrades, up to 3x current space used by CouchDB can be needed

    Required skills

    In addition to the hosting requirements, system administrators should have a basic understanding of command line interface, Kubernetes, docker, container orchestration, deployment, databases (CouchDB, Postgres), networking components (TLS, IP addresses, DNS).


    Hosting > + Create project issue

    CHT hosting requirements

    Requirements for hosting CHT applications

    For production CHT deployments, Linux is recommended, with Ubuntu the most commonly used. For App Developer Hosting, Linux or macOS may be used. Windows can be used for either, but without recommendation.

    Per the Kubernetes vs Docker page, CHT Core can be deployed with either Docker or Kubernetes.

    CHT 3.x is End-of-Life and us no longer supported. All requirements below apply to CHT 4.x.

    Docker Compose App Developer Hosting

    Kubernetes Production Hosting

    This guide refers to “Kubernetes”, but Medic recommends a lightweight orchestrator called K3s for bare-metal hosts. The requirements below refer to K3s deployments but can be translated to other Kubernetes hosting. For example, for cloud hosting, we recommend Amazon Elastic Kubernetes Service (EKS) and we’ve also assisted in a large K3s deployment based on VMWare.

    Be sure to see the cht-deploy script that leverage the helm application.

    • 1 x HA control-plane nodes: 2 GB RAM / 2 CPU / 20 GB SSD
    • 3 x worker servers: 16 GB RAM / 8 CPU / 50 GB SSD
    • 500GB storage area network (SAN)* - Will host Persistent Volume Claims
    • Root Access
    • Static IP with DNS entry - Kubernetes will use this to provision a valid TLS certificate
    • helm application
    • K3s
    • Current version of docker (used to bootstrap K3s)

    * During some upgrades, up to 3x current space used by CouchDB can be needed

    Required skills

    In addition to the hosting requirements, system administrators should have a basic understanding of command line interface, Kubernetes, docker, container orchestration, deployment, databases (CouchDB, Postgres), networking components (TLS, IP addresses, DNS).


    Hosting > 3.x > Self Hosting

    Hosting the CHT on self run infrastructure

    Hosting > 3.x > AWS Hosting

    Hosting the CHT on Amazon EC2

    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/hosting/vertical-vs-horizontal/index.html b/hosting/vertical-vs-horizontal/index.html index 5c9db1319c..b9c5adca19 100644 --- a/hosting/vertical-vs-horizontal/index.html +++ b/hosting/vertical-vs-horizontal/index.html @@ -1,9 +1,9 @@ -Vertical vs Horizontal scaling | Community Health Toolkit +Vertical vs Horizontal scaling | Community Health Toolkit

    Vertical vs Horizontal scaling

    The power of clustered CouchDB to horizontally scale the CHT

    Introduction

    Horizontally scaling is the ability to add more servers to an application to make it more performant. This often yields better performance than vertical scaling, which is adding more resources like RAM or CPU to a single server.

    CHT Core 4.0.0 introduces a new architecture for hosting which gives it the ability to easily scale horizontally. This enables large deployments to support more concurrent users and better utilize the underlying server hardware.

    Vertical scaling in 3.x and 4.x

    Before getting into how the CHT horizontally scales, it should be well understood the importance of vertical scaling and what it is. This is the ability of the CHT to support more users by adding more RAM and CPU to either the bare-metal or virtual machine host. This ensures key services like API, Sentinel and, most importantly, CouchDB, can operate without performance degradation.

    When thousands of users are simultaneously trying to synchronize with the CHT, the load can overwhelm CouchDB. As discovered through extensive research and large production deployments, administrators will start to see errors in their logs and end users will complain of slow sync times. Before moving to more CouchDB nodes, administrators should consider adding more RAM and CPU to the single server where the CHT is hosted. This applies to both CHT 3.x and CHT 4.x. Given the ease of allocating more resources, presumably in virtualized environment like EC2, Proxmox or ESXi, this is much easier than moving from a single to multi-node CouchDB instance.

    Here we see a normal deployment following the bare minimum hosting requirements for the CHT. We’ll call this a “short” deployment because it is not yet vertically scaled:

    flowchart TD
    + Create documentation issue
    + Create project issue

    Vertical vs Horizontal scaling

    The power of clustered CouchDB to horizontally scale the CHT

    Introduction

    Horizontally scaling is the ability to add more servers to an application to make it more performant. This often yields better performance than vertical scaling, which is adding more resources like RAM or CPU to a single server.

    CHT Core 4.0.0 introduces a new architecture for hosting which gives it the ability to easily scale horizontally. This enables large deployments to support more concurrent users and better utilize the underlying server hardware.

    Vertical scaling in 3.x and 4.x

    Before getting into how the CHT horizontally scales, it should be well understood the importance of vertical scaling and what it is. This is the ability of the CHT to support more users by adding more RAM and CPU to either the bare-metal or virtual machine host. This ensures key services like API, Sentinel and, most importantly, CouchDB, can operate without performance degradation.

    When thousands of users are simultaneously trying to synchronize with the CHT, the load can overwhelm CouchDB. As discovered through extensive research and large production deployments, administrators will start to see errors in their logs and end users will complain of slow sync times. Before moving to more CouchDB nodes, administrators should consider adding more RAM and CPU to the single server where the CHT is hosted. This applies to both CHT 3.x and CHT 4.x. Given the ease of allocating more resources, presumably in virtualized environment like EC2, Proxmox or ESXi, this is much easier than moving from a single to multi-node CouchDB instance.

    Here we see a normal deployment following the bare minimum hosting requirements for the CHT. We’ll call this a “short” deployment because it is not yet vertically scaled:

    flowchart TD
     
     subgraph couch1[" CouchDB - Single ''short'' Node "]
         couchInner1["2 CPU/4 GB RAM"]
    @@ -320,7 +320,7 @@
         couchInner6["6 CPU/6 GB RAM "]
     end
     
    -API["API"] --> HAProxy -->  couch4

    To read up on how to migrate your data from a single to multi-node, please see the data migration guide.

    It should be noted that, unlike vertical scaling, horizontal scaling of a large, existing dataset can take a while to prepare the transfer (hours to days) and may involve a brief service outage. This should be taken into consideration when planning a move of a CHT instance with a lot of data.


    Hosting > +API["API"] --> HAProxy --> couch4

    To read up on how to migrate your data from a single to multi-node, please see the data migration guide.

    It should be noted that, unlike vertical scaling, horizontal scaling of a large, existing dataset can take a while to prepare the transfer (hours to days) and may involve a brief service outage. This should be taken into consideration when planning a move of a CHT instance with a lot of data.


    Hosting > 4.x > Self Hosting > Multiple Nodes - Docker

    Hosting the CHT on self run infrastructure with horizontally scaled CouchDB nodes

    Hosting > @@ -328,7 +328,63 @@ Data migration to 4.x

    Guide to migrate existent data from CHT 3.x to CHT 4.x

    CHT Core Framework > Overview > CHT Core

    The different pieces of a CHT project, how they interact, and what they’re used for

    -

    \ No newline at end of file +

    \ No newline at end of file diff --git a/index.html b/index.html index 45fd5e4f77..2947c1623a 100644 --- a/index.html +++ b/index.html @@ -1,5 +1,5 @@ -Community Health Toolkit -

    Welcome to the documentation for the CHT

    The Community Health Toolkit is a collection of open-source technologies and open-access resources developed by a community focused on global health equity. We envision a world where primary health care is equitable, accessible, and delivered by people who are trusted in their communities. Start with the CHT overview, and join our community forum!

    This documentation site is being actively updated to make it as easy as possible to deploy CHT apps. Please notify us if you find any errors or inconsistencies.

    Why work with the CHT?

    Community health systems can dramatically improve the accessibility, quality, speed, and equity of primary health care, but only if health workers are effectively equipped and supported. Advances in open source technology are making it easier and more affordable than ever to deliver impactful, dignified care in even the hardest-to-reach communities.

    With more than 41,000 health workers using these tools to support a million home visits every month, the CHT is the most full-featured, mature, and widely-used open source software toolkit designed specifically for community health systems. Hundreds of individuals contribute to the CHT as designers, developers, researchers, health policy experts, health system implementers, and frontline health workers. For more about the unique strengths of our open source community and the technology we’re building together, see Why the CHT ?


    What can you build with the CHT?

    The CHT provides you with resources to design, build, deploy, and monitor digital tools for community health. It includes open source software frameworks and applications, guides to help design and use them, and a community forum for collaboration and support. The resources provided through the Community Health Toolkit can be used to build digital health apps used at the community, health facility, and health system level:

    At the community level, community health workers (CHWs) use apps built with the CHT to register patients, conduct guided health assessments, screen for specific conditions and danger signs, and refer patients to health facilities.

    At the health facility level, nurses and CHW supervisors use apps and admin consoles built with the CHT to coordinate care for patients with the CHWs, promote health practices in the community, and report health and service delivery statistics to health system officials

    At the health system level, data managers and others use apps and admin consoles built with the CHT to collate and report on key community and health system data. Their work often involves following up with supervisors and nurses to verify data for accuracy and completion.


    Getting Started

    Why the CHT?

    A great place to start for high-level context on what our community is building together. To explore the diverse kinds of digital health apps you can build with the CHT, you might also find it helpful to read about the ANC Reference App, or watch demo videos for contact tracing, covid education, or covid symptom checking apps. If you want to try out the software for yourself, feel free to request a demo account.

    Read More: Why the Community Health Toolkit?

    CHT Applications

    Comprehensive reference material on CHT concepts and features, useful for anyone interested in understanding what features and configuration options are available for CHT apps. This section also includes quick guides on focused app development topics, and a growing collection of thorough step-by-step tutorials for developing and deploying digital health apps with the Community Health Toolkit. If you’re a developer and want to dive right into developing your own app, this is the place to start.

    Read More: CHT Applications

    CHT Core Framework

    An overview and reference for development of the Core Framework of the Community Health Toolkit (CHT). Most CHT App developers are able to build great experiences for their users without extending CHT Core, but you might still find this section useful for its overview of CHT components.

    Read More: CHT Core Framework

    Design System

    An overview of key end user personas, notes on the CHT icon library, and configuration best practices for forms, tasks, targets, and contact profiles. These materials include more detail than you will need when you’re just beginning to explore the CHT, but they become increasingly helpful when designing your own community health app for a live deployment.

    Read More: Design System

    Have Questions?

    Want to learn alongside a community of users? Join our community forum and let us know how we can help! And if you like what you see, don’t forget to star our Github repo :)


    -

    \ No newline at end of file +

    \ No newline at end of file diff --git a/index.xml b/index.xml index c5e5b8e1f6..995b90af01 100644 --- a/index.xml +++ b/index.xml @@ -1 +1,510 @@ -Community Health Toolkit – Welcome to the documentation for the CHThttps://docs.communityhealthtoolkit.org/Recent content in Welcome to the documentation for the CHT on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file +Welcome to the documentation for the CHT on Community Health Toolkithttps://docs.communityhealthtoolkit.org/Recent content in Welcome to the documentation for the CHT on Community Health ToolkitHugo -- gohugo.ioenAccessing CHT Appshttps://docs.communityhealthtoolkit.org/apps/concepts/access/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/concepts/access/Apps built with the Core Framework run on most modern computers with the newest versions of Google Chrome or Mozilla Firefox. +Hardware &amp; Software Requirements Hardware procurement, ownership, and management is the responsibility of each implementing organization. We strongly urge all organizations to procure hardware locally to ensure ease of replacement, repair, sustainability, and hardware support when needed. +Accessing on Desktop On desktop devices, there is no need to download anything.Androidhttps://docs.communityhealthtoolkit.org/apps/features/integrations/android/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/integrations/android/The CHT Android application can be launched by clicking on a link or invoking an intent in another Android app. This is useful for enabling login by SMS, directing a user to a specific page, and integrating between Android applications. +Sending a URL When the user clicks on a link to a CHT instance from an SMS, email, WhatsApp, or any other app, Android will prompt the user to choose whether to open the URL in the Android app or the browser.API to interact with CHT Applicationshttps://docs.communityhealthtoolkit.org/apps/reference/api/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/api/This page covers the endpoints to use when integrating with the CHT server. If there isn&rsquo;t an endpoint that provides the function or data you need, direct access to the database is possible via the CouchDB API. Access to the PostgreSQL database may also prove useful for data analysis. If additional endpoints would be helpful, please make suggestions via a GitHub issue. +Settings GET /api/v1/settings PUT /api/v1/settings Query Parameters Export GET /api/v2/export/dhis GET /api/v2/export/reports Query parameters GET /api/v2/export/messages Output Examples GET /api/v2/export/feedback Query Parameters GET /api/v2/export/contacts Output Query parameters GET /api/v2/export/user-devices Output Forms GET /api/v1/forms Headers Examples GET /api/v1/forms/{{id}}.Architecture of CHT Instanceshttps://docs.communityhealthtoolkit.org/core/overview/architecture/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/architecture/Overview Server CHT Core Framework The cht-core product is the primary component of the CHT. The server comes with authentication, role based authorization, data security, and a range of protected data access endpoints. Read more detail in cht-core GitHub repository. +API A NodeJS service which runs on the server and provides security and APIs for browsers and integrations. It also includes a custom implementation of filtered replication to allow it to support more concurrent users.CHT Core dev environment setuphttps://docs.communityhealthtoolkit.org/contribute/code/core/dev-environment/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/core/dev-environment/Note This guide assumes you are a CHT Core developer wanting to run the CHT Core from source code to make commits to the public GitHub repository. To set up your environment for developing apps, see the app guide. +To deploy the CHT in production, see either AWS hosting or Self hosting +Note These steps apply to both 3.x and 4.x CHT core development, unless stated otherwise. The Happy Path Installation CHT Core development can be done on Linux, macOS, or Windows (using the Windows Subsystem for Linux (WSL2)).CHT Watchdoghttps://docs.communityhealthtoolkit.org/core/overview/watchdog/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/watchdog/Overview CHT Watchdog is deployed on a separate server so that you can watch for, and alert on, any critical issues with the CHT Core. Read more about setting up CHT Watchdog. +Grafana Grafana is a dashboard visualization and alerting software. It is open source and an industry standard for this task. There is an free repository of pre-existing dashboards which greatly reduce the time to create new dashboards and alerts.The Professionalized Community Health Worker (CHW), Janethttps://docs.communityhealthtoolkit.org/design/personas/chw-janet/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/chw-janet/&ldquo;It took a while to be trusted. We had to prove our worth, but now we are well appreciated.&rdquo; +About Janet is a mother and a farmer. She takes care of her daily BRAC work after finishing her farm work. She has poor eyesight. Janet has a feature phone. She has seen smartphones but never used one. She has no electricity at her home. She charges her phone at her friend’s house.Continuous Discovery Overviewhttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/continuous-discovery-overview/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/continuous-discovery-overview/The continuous discovery mindset is one that acknowledges that digital products are never &ldquo;done&rdquo; and can always be made better. The &ldquo;discovery&rdquo; aspect is that those working on the product should empathize with those who use it and operate with those people in mind, both for finding the most impactful areas of work and in how to measure impact. +Teammates choose work with impact in mind and develop solutions with an expectation that people will use what is built and we can measure a positive change.Customhttps://docs.communityhealthtoolkit.org/apps/features/integrations/custom/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/integrations/custom/The CHT Core Framework includes functionality that allows sharing data with any API-based system. Developers have configured CHT integrations with OpenMRS, KenyaEMR, Bahmni, DHIS2, RapidPro, Apache NiFi, OpenHIM, custom electronic medical records (EMR), and several other systems. +Overview Integrating a CHT App into your digital health ecosystem starts with identifying an integration use case. It&rsquo;s important to first understand all the components present in the ecosystem (EMR, laboratory system, community health information system, etc) and then plan out what the workflow will look like operationally.DHIS2https://docs.communityhealthtoolkit.org/apps/features/integrations/dhis2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/integrations/dhis2/Most health systems have regular reporting requirements for community-level activities. Health workers often carry around heavy logbooks to manually record all relevant activities. When it is time to submit their data, community health workers (CHWs) summarize what was recorded in their logbooks and share this information with their supervisors, who in turn create paper records of these totals across entire community units or health facilities. This paper record is often passed to yet another individual whose responsibility is to manually key in the data into a health information management system, such as DHIS2.Feature Releaseshttps://docs.communityhealthtoolkit.org/contribute/code/releasing/feature_releases/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/releasing/feature_releases/To build and iterate on new features at a pace that is faster than our regular release cycle, some features are released in a Feature Release. Feature Releases (FRs) are based on the most recent release and only include improvements related to a feature being developed. These releases are tested to be production-ready so that new features can be studied with CHT partners in a live deployment, with the aim of getting the feature ready for wider use in an upcoming release.Focused Working Groupshttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/focused-groups/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/focused-groups/The Product team as a whole is focused on digital health tools helping to achieve Universal Health Coverage, and care reaching everyone when and where they need it. The team is made up of 4 different groups, with each group focusing on outcomes to serve different people. +See the overall Product Team Roadmap. +Care Teams Focused on building effective tools to provide care in the hardest-to-reach communities. +Key users: Patients, Families, Caregivers, Community Health Workers, Supervisors.Getting started building a CHT apphttps://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/This tutorial will take you through setting up a local environment to build and test CHT applications on CHT version 4.x. This includes setting up the necessary tools to download and run the CHT public docker image as well as a command line interface tool to manage and build CHT apps. +By the end of the tutorial you should be able to: +View the login page to CHT webapp on localhost Upload default settings to localhost Note This guide will only work with CHT 4.Implementing Partnershttps://docs.communityhealthtoolkit.org/design/personas/partners/implementers/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/partners/implementers/Characteristics and Strengths May have influence and reach with the government to adopt and scale-up the new model of care Have a good understanding of the health needs of communities Have an appreciation for the role of digital technology in healthcare May have deployed, or are planning to deploy, digital health tools in the community health space Support government’s CHW networks, and may have fielded their own cadre of CHWs Are leveraging data and data science to innovate on new approaches to achieving desired health outcomes Have access to health systems data from the deployment of a new model of care May possess existing capacity, or are looking to build, the in-house capacity to deploy and implement digital health tools May have already invested significantly in proprietary locked-in software Keen to attract funding from large funders May have a presence in multiple countries Values Equitable access to quality health care Developing national and local capacities Cost-effectiveness Government and donor relations Harmonization and alignment to the national health strategies Integration with national digital health platforms Government adoption and ownership of their model of care Needs Government support and buy-in Funder backing and resources to design and deploy digital health programs HCD and data science capabilities May need further understanding and expertise in working with CHT and opensource toolsIntroduction & Prerequisites to data synchronization and analyticshttps://docs.communityhealthtoolkit.org/apps/guides/data/analytics/introduction/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/analytics/introduction/The pages in this section apply to both CHT 3.x (beyond 3.12) and CHT 4.x. +CHT Sync schema differs from CHT Couch2pg. +Most CHT deployments require some sort of analytics so that stakeholders can make data driven decisions. CouchDB, which is the database used by the CHT, is not designed for analytics. It is a document database, which means that it is optimized for storing and retrieving documents, and not for aggregating data.Key Conceptshttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/key-concepts/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/key-concepts/There are a number of Baserow key concepts to understand before diving in. These are specific to how we’ve chosen to implement the repository. +Interview Subject This is the person that was interviewed or otherwise provided information/feedback. +Example: Julius Nyerere +Organization The company that the “Interview Subject” belongs to or associates with. +Example: MoH Furahi +Samples These are links to, and metadata about, the “Evidence”. These are typically audio or video recordings stored on google drive, but can also be links to PDFs, forum posts, etc.Maternal and Newborn Health Reference Apphttps://docs.communityhealthtoolkit.org/apps/examples/anc/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/anc/This &ldquo;reference application&rdquo; for maternal and newborn health provides a template for structuring and organizing your Community Health Toolkit digital health app, its configuration, and test code. It can be used as is, or serve as a great way to learn about the CHT&rsquo;s foundation for forms, data fields, and analytics that can be easily customized to fit your context. +Problem Being Addressed Access to quality maternal and newborn care is the cornerstone of many community health programs.Product Teamhttps://docs.communityhealthtoolkit.org/contribute/medic/onboarding/product-team/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/product-team/The goal of this page is to convey a general “lay of the land” so someone starting can see a lot of what’s out there related to Product Team without having to be surprised each day as new things pop up. +About Product Team The Product Team manages the entire software development life-cycle to understand problems, capture requirements, design and build modular software systems, and document everything along the way. We achieve this by working with CHT partners and health workers to design, build, and maintain the Community Health Toolkit and its open source tools.Product Trio Activitieshttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/product-trio/product-trio-activities/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/product-trio/product-trio-activities/The following items represent common activities that members of a Product Trio will do in the Product Development Process. Depending on what is underway, some of these may happen each week, but many parts happen on-demand. For example, interviewing is an ongoing process of gathering information, but the group may not start building a new solution each week. +The following activities should be performed in order, by all trio members:CHT hosting requirementshttps://docs.communityhealthtoolkit.org/hosting/requirements/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/requirements/For production CHT deployments, Linux is recommended, with Ubuntu the most commonly used. For App Developer Hosting, Linux or macOS may be used. Windows can be used for either, but without recommendation. +Per the Kubernetes vs Docker page, CHT Core can be deployed with either Docker or Kubernetes. +CHT 3.x is End-of-Life and us no longer supported. All requirements below apply to CHT 4.x. +Docker Compose App Developer Hosting 4 GB RAM / 2 CPU / 8 GB SSD Root Access TLS certificates - Docker Helper for 3.CHT training resourceshttps://docs.communityhealthtoolkit.org/running-programs/training/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/running-programs/training/CHT training process In the implementation of CHT supported community health programs, health care workers such as CHWs, CHW supervisors and facility based health care providers need training to help equip them with the required knowledge and skills to effectively carry out their roles and responsibilities. For most deployments, program leads and ministry of health officials may also need training for them to perform their roles, oversee and supervise the community health programs.Designing User Interviewshttps://docs.communityhealthtoolkit.org/design/guides/designing-interviews/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/guides/designing-interviews/Purpose Of the Guide Understanding the people you are designing for is a paramount step in the design process, and so one way to achieve that is through conducting user interviews. This guide will help design thinkers to understand users’ experiences from their own point of view in terms of: +Why they use products in a certain way How they feel about something How they perform various actions Background (Current Situation, Problem, and/or Opportunity) Interviews can be a great way to empathize with your users because interviews can give you an in-depth understanding of the users’ values, perceptions, and experiences.Why the Community Health Toolkit?https://docs.communityhealthtoolkit.org/why-the-cht/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/why-the-cht/The Community Health Toolkit is a collection of open-source technologies and open-access resources developed by a community focused on global health equity. We envision a world where primary health care is equitable, accessible, and delivered by people who are trusted in their communities. Start with the CHT overview, and join our community forum! +Community health systems can dramatically improve the accessibility, quality, speed, and equity of primary health care, but only if health workers are effectively equipped and supported.Documentation Workflowhttps://docs.communityhealthtoolkit.org/contribute/docs/workflow/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/docs/workflow/Getting Started Anyone can contribute to CHT documentation by opening an issue in the cht-docs repo or by using the “Edit this page” or “Create documentation issue” links in the upper right corner of your window. +Basics It is helpful to be comfortable with git and GitHub to contribute to the CHT community. The documentation source is in GitHub. The content pages are in the /content/en/ directory. Documentation is written in Markdown.About Interviewshttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/about-interviews/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/about-interviews/ Be sure to record the interview (audio and/or video) Name the file like “2022-06-05 Interview with Julius Nyerere from MoH Furahi” Upload the file to Google Drive here (private link).All The Thingshttps://docs.communityhealthtoolkit.org/contribute/medic/onboarding/all-the-things/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/all-the-things/This page is meant to serve as a point of conversation, with a wide range of topics to be discussed when joining Medic or starting as a contributor. Many things are not in any particular order. The goal is to convey a general “lay of the land” so someone starting can see a lot of what’s out there without having to be surprised each day as new things pop up.Android Dev Environmenthttps://docs.communityhealthtoolkit.org/contribute/code/android/development-setup/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/android/development-setup/The following instructions allows you to setup a development environment for the CHT Android apps, and the CHT Gateway app as well. +Finally, you will learn how to assemble the app, run the tests, and how to choose the right artifacts when installing or publishing the apps. +Requirements Java 17+ (OpenJDK versions work). Android SDK, and optionally Android Studio. The adb command for debugging and get the logs. The source code.Build commandshttps://docs.communityhealthtoolkit.org/contribute/code/core/build-commands/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/core/build-commands/CHT Core build commands These commands are defined in the package.json and can be executed with npm run &lt;command&gt; from the cht-core repository directory. +Development build commands For developers (humans) to execute to build cht-core. +Command Description build-ddocs Compiles all the DDocs and outputs them into /api/build/ddocs ready for deployment. build-dev Updates dependencies and builds all the applications. build-dev-watch Same as build-dev, but keeps watching for any code changes and automatically deploys on change.CHT App Configurerhttps://docs.communityhealthtoolkit.org/core/overview/cht-conf/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/cht-conf/Installation Read more about setting up CHT Conf. +Currently Supported The different items that are supported by CHT Conf include: +Settings Compile app settings from: tasks rules schedules contact-summary purge App settings can also be defined in a more modular way by having the following files in app_settings folder: base_settings.json forms.json schedules.json Backup app settings from server Upload app settings to server Upload resources to server Upload custom translations to the server Upload privacy policies to server Upload branding to server Upload partners to server Forms Fetch from Google Drive and save locally as .CHT Sync and CHT Pipelinehttps://docs.communityhealthtoolkit.org/core/overview/cht-sync/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/cht-sync/Overview CHT Sync is an integrated solution designed to enable data synchronization between CouchDB and PostgreSQL for the purpose of analytics. It combines several technologies to achieve this synchronization and provides an efficient workflow for data processing and visualization. The synchronization occurs in real-time, ensuring that the data displayed on dashboards is up-to-date. +Read more about setting up CHT Sync. +CHT Sync uses couch2pg to replicate data from CouchDB to PostgreSQL in a real-time manner.CHW Supervisor, Annhttps://docs.communityhealthtoolkit.org/design/personas/chw-supervisor-ann/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/chw-supervisor-ann/“During refresher trainings, when I go there, it takes me 3 hours because I copy from one book to another.” +About Married with two school-aged children Has one year of training in a health-related field Not originally from the community, but communicates effectively with the CHWs Salaried MOH employee Has a personal mobile phone that ranges from a feature phone to a smart phone, used for communication Has an email account primarily accessed via mobile phoneLocal Couch2pg Setuphttps://docs.communityhealthtoolkit.org/apps/tutorials/couch2pg-setup/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/couch2pg-setup/This tutorial will take you through setting up a Couch2pg service. +By the end of the tutorial you should be able to: +Set up a Couch2pg service Run the Couch2pg service CHT Couch2pg is a background process that moves data from Couchdb to Postgres through one way replication. It therefore, needs to have full read and write access to both the Postgres Database and Couchdb upstream. It is built in nodejs and can be set up as a background process using systemd.Database schema conventionshttps://docs.communityhealthtoolkit.org/core/overview/db-schema/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/db-schema/CouchDB (and PouchDB in the browser) is a JSON-based NoSQL datastore that we use to store our data. While unlike SQL databases there is no enforced schema, code still follows conventions, and this document aims to describe the schema as defined by how our code operates. +In this document &ldquo;record&rdquo; means a JSON object that resides in CouchDB or PouchDB. +General record data structure Property Description Required by _id CouchDB&rsquo;s unique identifier of the record all records _rev CouchDB&rsquo;s revision marker all records type The general type of the document, see below all user-created* documents reported_date Numerical timestamp of when the document is first created all user-created documents User-created documents here generally means contacts and reports, but may extend further.Deploy CHT Core on Medic hosted EKShttps://docs.communityhealthtoolkit.org/contribute/code/core/deploy-on-eks/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/core/deploy-on-eks/While not directly available to the public who might be doing CHT Core development, having Medic&rsquo;s process for using our Amazon Elastic Kubernetes Service (AWS EKS) publicly documented will help Medic employees new to EKS. As well, hopefully external developers looking to re-use Medic tools and process to use EKS will find it helpful. +While these instructions assume you work at Medic and have access to private GitHub repositories, many of the tools are fully open source.Database document hydrationhttps://docs.communityhealthtoolkit.org/apps/guides/data/hydration/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/hydration/Documents are connected with each other via their document _id. For example: +a contact document is connected to its parent by storing their _id in the parent property +a report document is connected to its submitter by storing their _id in the contact property +See Also: DB Schema +To optimize database storage, documents are &ldquo;minified&rdquo; when stored and are &ldquo;hydrated&rdquo; when they are used by the app. +Minification Minification means replacing a linked document&rsquo;s content with an object that only contains its uuid.Local CHT Sync Setuphttps://docs.communityhealthtoolkit.org/apps/guides/data/analytics/setup/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/analytics/setup/Before setting up CHT Sync in production, it&rsquo;s very handy to be able to run it locally. This will allow you to experiment with the data flow and easily query development data quickly and locally. +These instructions assume you&rsquo;re running CHT Sync, CHT Core and PostgreSQL either locally on your workstation or on a local server. They are not meant to be used to deploy a secure, always on production instance.Local and Sub-National Governmentshttps://docs.communityhealthtoolkit.org/design/personas/partners/local-governments/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/partners/local-governments/Characteristics and Strengths They are often led by locally elected political leaders and are motivated to demonstrate health impacts for continued political support Tend to be more accountable to local communities Have a thorough understanding of the local context and health needs of local communities They often have the legal and administrative mandate to set local health priorities, plan, allocate and mobilize resources, and deliver primary health care to their respective communities They are guided by national health strategy and community health strategy They are responsible for managing CHWs They are often motivated to adopt digital health technologies May have deployed, or are planning to deploy, digital health tools in the community health space Comparatively, they have less cumbersome bureaucratic processes and red-tape in building partnerships Values Health and well-being of their citizens Equitable access to quality health care services Social health protection of their constituencies Local leadership and decentralization in health Innovation and digital technologies in community health space High impact door-step health care Community participation Partnerships with non-state actors Cost-effectiveness Sustainability Needs Health systems strengthening support to keep up with growing population needs and tackle emerging public health challenges, e.Navigating CHT Appshttps://docs.communityhealthtoolkit.org/apps/concepts/navigation/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/concepts/navigation/Summary of Page Tabs Page tabs are the primary way to navigate apps built with the Core Framework. The number of tabs is variable depending on the user’s role and place in the hierarchy. For example, non-admin users don’t have Messages. The Reports tab is accessible to CHWs but often located inside the secondary menu drawer. +Messages​: A place for community-based staff to send and exchange messages Tasks​: This is a list of upcoming visits, follow-ups, or other required tasks Reports​: A detailed history of all forms submitted by CHWs and other staff People​: This is where profiles of districts, staff, CHWs and patients live Targets: Displays real-time visualizations of key activity and impact indicators The Menu Drawer Tap the menu icon in the upper right corner of the header to access other pages, edit personal settings, view sync status and more.Offline-First in the CHThttps://docs.communityhealthtoolkit.org/core/overview/offline-first/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/offline-first/Introduction CHT applications are designed to be used equally well in areas with no internet connectivity, slow or unreliable internet connectivity, and good internet connectivity. Achieving reliable performance and powerful features requires diligence and strict adherence to the principles of Offline-First development. +In this page we&rsquo;ll cover why and how we achieve this in the CHT. +Why this is important The CHT is designed to improve healthcare in the hardest to reach communities.OpenMRShttps://docs.communityhealthtoolkit.org/apps/features/integrations/openmrs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/integrations/openmrs/OpenMRS is an open source electronic medical record system used in dozens of countries. Integrating CHT apps with OpenMRS can open up opportunities to improve health outcomes for patients by promoting better coordination of care. For example, referrals by CHWs in the community can be sent electronically to health facilities using OpenMRS so that nurses and clinicians can prepare for their visit and have full access to the assessment done by a CHW.Installation as a Progressive Web Apphttps://docs.communityhealthtoolkit.org/core/overview/pwa/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/pwa/What is a Progressive Web App (PWA)? A PWA is a web application that can be used like a website in the browser, but the user can choose to &ldquo;install&rdquo; it. This means a shortcut is added to the home screen of the device, and when the application is run it doesn&rsquo;t have the usual browser address bar and tabs so it looks like a regular application. +The CHT Core webapp has been developed to be a PWA to give users more choice about how applications are installed.resources/https://docs.communityhealthtoolkit.org/apps/reference/resources/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/resources/Icons Apps can be customized by defining the icons to use for tasks, targets, and contacts. +Add icons to the resources folder, and include them by name in the resources.json file as the following example: +{ &#34;icon-risk&#34;: &#34;icon-healthcare-warning@2x.png&#34;, &#34;icon-treatment&#34;: &#34;icon-healthcare-medicine@2x.png&#34;, &#34;medic-clinic&#34;: &#34;medic-family.svg&#34;, &#34;medic-district-hospital&#34;: &#34;medic-family.svg&#34;, &#34;medic-health-center&#34;: &#34;medic-chw-area.svg&#34;, &#34;medic-person&#34;: &#34;medic-person.svg&#34; } See Also: Icon Library +The folder and files structure would look like this: +./ resources.json /resources icon-healthcare-warning@2x.png icon-healthcare-medicine@2x.png medic-family.svg medic-family.svg medic-chw-area.svg medic-person.Schedule of Activitieshttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/schedule-of-events/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/schedule-of-events/Annually Set goals (strategies and measures) Identify desired outcomes the team will work towards Quarterly Choose outcome(s) for Focused Working Groups to work towards Outcome kickoff Monthly Communicate status and progress of work towards current outcome(s) Recognize and establish certainty around time sensitive, project-initiated tasks Weekly Conduct interviews Continued iterative work on identifying opportunities, weighing possible solutions, building, and measuring Triage potential side-loaded opportunitiestranslations/https://docs.communityhealthtoolkit.org/apps/reference/translations/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/translations/Given that CHT apps are used around the world, the Core Framework was designed with localization in mind. The Core Framework itself is available in English, French, Hindi, Nepali, Spanish, Swahili, and Indonesian. +In the app_settings.json file the default language for the application is set by the locale property, along with a separate default language for outgoing messages that are sent via SMS with the locale_outgoing property. +Additionally, languages available to the user can be enabled and disabled through the languages property which contains an array of objects.Vertical vs Horizontal scalinghttps://docs.communityhealthtoolkit.org/hosting/vertical-vs-horizontal/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/vertical-vs-horizontal/Introduction Horizontally scaling is the ability to add more servers to an application to make it more performant. This often yields better performance than vertical scaling, which is adding more resources like RAM or CPU to a single server. +CHT Core 4.0.0 introduces a new architecture for hosting which gives it the ability to easily scale horizontally. This enables large deployments to support more concurrent users and better utilize the underlying server hardware.Adding Nuggetshttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/adding-nuggets/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/adding-nuggets/The majority of research observations will come from interviews with CHT users. Here’s how you will enter these observations, called “Nuggets”, into the UX Research Repo. +After conducting your interview, copy the video recording to Google Drive (more info here). You’ll need to reference this later. +High Level Steps Add the Organization (of the person you interviewed) if it doesn’t already exist Create the Interview Subject if this is the first time we’ve interviewed or entered observations from this person.Contact and User Management - Part 1https://docs.communityhealthtoolkit.org/apps/tutorials/contact-and-users-1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/contact-and-users-1/In this tutorial you will learn how to create and edit contacts and their associated users in and application built with the CHT using the default contact creation forms. This will help you get familiar with the UI of the webapp as well as some features and functionality. If you are already comfortable with this, you can skip to part 2, which covers manipulating contacts and their associated documents using cht-conf.Data Flows for Analyticshttps://docs.communityhealthtoolkit.org/core/overview/data-flows-for-analytics/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/data-flows-for-analytics/In this section, we focus on how data flows through the various components of the Community Health Toolkit. The CHT is built to support the delivery of quality community health care at the last mile. The CHT is designed to work in areas with low connectivity, which means it is an Offline-First toolkit for care provision. The architectural and technology choices in the stack are mostly guided by this principle, which will be evident in the discussion of the data management pipeline.Creating an Empathy Maphttps://docs.communityhealthtoolkit.org/design/guides/empathy-map/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/guides/empathy-map/Purpose Of the Guide This guide will take you through the process of creating an empathy map as a way to synthesize the insights gathered during the discovery phase. +Background An empathy map is a visualization tool which helps you sum up what you learned from design research to help you better understand your users and articulate what you know to colleagues and stakeholders. +The map provides four major areas in which to focus your attention on, thus providing an overview of a person’s experience.Formshttps://docs.communityhealthtoolkit.org/apps/concepts/forms/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/concepts/forms/Forms are a building block of all CHT apps. They are used when creating or editing contacts, and when completing a care guide or survey within the app. Forms are also used to interpret SMS interactions with the CHT. +When a completed form is submitted, it is treated as a Report in the app. All reports can be viewed in the Reports tab by those with the proper access within the hierarchy.Kubernetes vs Dockerhttps://docs.communityhealthtoolkit.org/hosting/kubernetes-vs-docker/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/kubernetes-vs-docker/Since the release of CHT Core 4.0.0 in late 2022, Medic has been perfecting the hosting for the toolkit to balance the need for high uptimes so CHWs can always deliver care while having an easy and approachable technical back end hosting solution. While initially Docker Compose with an overlay network was thought to be our goto solution, field testing this overlay networks in production has shown them to unreliable.Ministry of Health and National Governmentshttps://docs.communityhealthtoolkit.org/design/personas/partners/national-governments/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/partners/national-governments/Characteristics and Strengths Set national health priorities and design large-scale national public health programs Lead health sector reform agenda Guide the production, recruitment, and deployment of human resource for health, including community health workforce Regulate both public and private health service providers Set development cooperation policies and priorities in the health sector Provide financial resources to sub-national governments Work closely with other ministries and sub-national governments to execute national health policies Compete with other ministries and government line agencies for resources Have access to large-scale funding from multilateral and bilateral international agencies Have an appreciation for the role of digital technology in healthcare May have developed and rolled-out national digital health strategy Responsible for setting digital technology and data standards Often have an institutional home to anchor national digital health programs May have some in-house capacity to deploy and implement digital health tools Are often looking to establish partnerships for leveraging digital health technologies Are leveraging data and data science to innovate on new approaches to achieving desired health outcomes Have access to health systems data from the deployment of a new model of care May have already invested significantly in proprietary locked-in software Values Safeguarding the health rights of the citizens International commitments in health, e.OppiaMobilehttps://docs.communityhealthtoolkit.org/apps/features/integrations/oppia/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/integrations/oppia/OppiaMobile is an open source mobile learning platform built by Digital Campus specially designed for delivering learning content, multimedia, and quizzes in low connectivity settings. All the content and activities can be accessed and used when no internet connection is available, and users can earn points and badges for completing activities and watching videos. To learn more about the platform, check out the overview, OppiaMobile on Github, and their documentation site.Production CHT Sync Setuphttps://docs.communityhealthtoolkit.org/apps/guides/data/analytics/production/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/analytics/production/This is under active development. The page will be updated accordingly once it&rsquo;s validated that the current approach is suited for being used in production. For the time being, you can try out and test CHT Sync locally.RapidProhttps://docs.communityhealthtoolkit.org/apps/features/integrations/rapidpro/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/features/integrations/rapidpro/RapidPro is a software product that allows you to visually build logic to support interactive messaging flows. The flows support a variety of technologies such as: SMS, USSD, IVR, Telegram, Facebook Messenger, and WhatsApp. RapidPro is open source and provides an API to integrate with other applications. To learn more about the platform, check out RapidPro on GitHub, their Community website, or join their Google Group. +Overview CHT-based SMS workflows can be configured to support registering of new patients or pregnancies, recording outcomes of visits, confirmation via auto-responses, and scheduling reminders.Regional Manager, Christinahttps://docs.communityhealthtoolkit.org/design/personas/regional-manager-christina/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/regional-manager-christina/ +“I heard that some branches have already had challenges with mobile phones.” +About Christina been working at BRAC for six years. She manages four branches and works closely with the CHW Managers there. She makes sure CHW Managers are monitoring CHWs. She occasionally does random checks, driving out to villages and observing Managers with CHWs. +Values Visionary Integrity Good communication Respect Responsibility Wisdom Empathy Responsibilities Overall responsibility of the Region’s activities Oversees implementation of organisational goals Communicates organisational goals and strategies to CHW managers Liaise and ensure compliance with Ministries, NGO boards and regulatory bodies Promote advocacy efforts of the program Supervises and supports staff to attain their career goals Approval of quarterly logistic plans for CHW managers Reviews and approves CHW’s continuous education programs Evaluates programs impact and advises on key priority areas of focus Realignment of programs to changing ecosystem Budgetary planning for region’s activities Tracking branches’ and CHW manager’s performance Monthly indicators tracking to ensure realization of organisational goals Needs Real time access of data on supervision activities ongoing in their region Access to summary statistics on project indicators Timely submission of monthly reports on retention and turnover rates for CHWs Escalation of CHW retraining needs, challenges and proposed solutions Opportunities to conduct random checks to assess the CHWs perceptions of their managers support Motivations Desire to improve lives of communities Desire to practice community health strategies learnt at school Organisational goals align well with personal goals Strengths and Assets Provided with a company car and computer Has a welcoming heart and accessible to employees Team playerReleasinghttps://docs.communityhealthtoolkit.org/contribute/code/android/releasing/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/android/releasing/All Medic&rsquo;s Android projects automatically build, sign, and release builds via GitHub Actions. The following guide applies to any of these apps, although the last 2 are in maintenance mode (links pointing to the release sections): +cht-android cht-gateway medic-collect rdt-capture Alpha for release testing Ensure all issues for this release have passed AT and been merged into master. You can also create an alpha release from a feature branch, to provide the needed .CHT Technology Radarshttps://docs.communityhealthtoolkit.org/contribute/tech-radar/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/tech-radar/It is essential for a development toolkit such as the Community Health Toolkit to constantly improve and keep track with the latest useful innovations. It is important to openly look for innovations and new technologies and to question established technologies and methods every now and then. +To enhance visibility and clarity on the technology choices, the technological strategy, and the available CHT features and tools, we leverage a framework called Technology Radar.Technical Resourceshttps://docs.communityhealthtoolkit.org/contribute/medic/onboarding/technical-resources/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/technical-resources/Note This page also contains Medic internal documentation links that are only accessible to Medic team members. This page contains materials that will help a CHT Contributor to master all the technical details they need to be successful when building tools related to the CHT. For Medic team members, this content is complimentary on a technical level to the Onboarding documents shared by the Internal Operations team. Keep in mind that this is a living document, and every contributor is encouraged to add to it when identifying learning opportunities that can set them up for success with the CHT.Care Guideshttps://docs.communityhealthtoolkit.org/apps/concepts/care-guides/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/concepts/care-guides/Care Guides Forms are used to build “Care Guides” that take health workers through care protocols and provide decision support for their interactions with patients. App designers can use the basic form building functionality in a variety of ways. +Care Guides also allow CHWs to register new families and people, assess a sick child, and enroll a new pregnancy into an antenatal care schedule. Care Guides can be located in many parts of your app, including the Tasks, People, and Reports tabs.Code of Conducthttps://docs.communityhealthtoolkit.org/contribute/code-of-conduct/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code-of-conduct/All maintainers and contributors in this community are required to act according to the following Code of Conduct. These guidelines help steer our interactions and help us provide and ensure a safe environment for everyone. +Our Standards Examples of behavior that contributes to creating a positive environment include: +Using welcoming and inclusive language Being respectful of differing viewpoints and experiences Gracefully accepting constructive criticism Focusing on what is best for the community Showing empathy towards other community members Examples of unacceptable behavior by participants include:Contact and User Management - Part 2https://docs.communityhealthtoolkit.org/apps/tutorials/contact-and-users-2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/contact-and-users-2/In this tutorial you will learn how to create and edit contacts and their associated users in the CHT application using cht-conf. If you haven&rsquo;t already, have a look at part 1 of this tutorial for a useful overview of key concepts. +Brief Overview of Key Concepts cht-conf is a command-line interface tool to manage and configure your apps built using the Core Framework of the Community Health Toolkit.Environment Variableshttps://docs.communityhealthtoolkit.org/apps/guides/data/analytics/environment-variables/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/analytics/environment-variables/There are three environment variable groups in the .env file. To successfully set up CHT Sync, it is important to understand the difference between them. +POSTGRES_: Used by PostgreSQL to establish the PostgreSQL database to synchronize CouchDB data to. They also define the schema and table names to store the CouchDB data. The main objective is to define the environment where the raw CouchDB data will be copied. DBT_: Exclusive to the DBT configuration.Nurse, Maryhttps://docs.communityhealthtoolkit.org/design/personas/nurse-mary/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/nurse-mary/About Mary has formal clinical training but a limited ability to conduct complex surgical procedures. She sees +/- 50 patients a day, sometimes in their homes. She is literate in English and understands local dialects. She is sometimes stationed far from her rural town/family in spurts and makes regular trips to districts to hand-deliver reports. +Mary lives on $5/day and depends mostly on solar power at the clinic as she may not have electricity at home.Creating an Actionable Problem Statementhttps://docs.communityhealthtoolkit.org/design/guides/problem-statement/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/guides/problem-statement/Purpose Of the Guide This guide will take you through how to create an actionable problem statement in order to generate a greater quantity and higher quality solutions when you start generating ideas during ideation sessions. +Background Defining your design challenge is probably one of the most important steps in the design thinking process as it sets the tone and guides all of the activities that follow. +In the define stage, you should end up creating an actionable problem statement which is commonly known as the point of view (POV) in design thinking.Publishinghttps://docs.communityhealthtoolkit.org/apps/guides/android/publishing/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/android/publishing/Once the flavor is built there are many different ways to publish the binaries for installation. +Google Play Store The Play Store has the advantage of being installed on all Android phones by default. This makes it very easy for users to install your app, which makes it the approach we recommend for most applications. +One of the downsides is it can be more difficult to get your app published and it may be removed in future if it&rsquo;s found to not comply with future requirements.Publishing Docker Imageshttps://docs.communityhealthtoolkit.org/contribute/code/releasing/publish-docker-image/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/releasing/publish-docker-image/Docker images for CHT projects can be published to the medicmobile Docker Hub organization, so they are easily accessible to the community. This process can be automated using GitHub actions. +Create repository on Docker Hub First, create a repository for your new image on Docker Hub. +Use the admin Docker account to create a new repository in the medicmobile organization. For your new repository, update the permissions to give the developers team the ability to Read &amp; Write.Sentinel Transitionshttps://docs.communityhealthtoolkit.org/core/overview/transitions/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/transitions/A transition is javascript code that runs when a document is changed. A transition can edit the changed doc or do anything server side code can do for that matter. +Transitions are run in series, not in parallel: +For a given change, you can expect one transition to be finished before the next runs. +You can expected one change to be fully processed by all transitions before the next starts being processed.Synthesizing Nuggetshttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/synthesizing-nuggets/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/synthesizing-nuggets/ Product owners review new Nuggets weekly Deal with duplicates Product trio and stakeholders review Nuggets quarterly Facilitator creates a new &ldquo;Grid&rdquo; on the Nuggets table with the date of the workshop Facilitator applies filters to identify Nuggets to be reviewed Date (last synthesis workshop to current date) Focused group (Care or Allies) Not already associated to an insight Export filtered Nuggets to CSV Make copy of latest Miro board and import CSV as stickies Product trio and stakeholders discuss and sort stickies into existing or new themes Define problem statements per theme as insights Return to Baserow and add insights to corresponding nuggetsTeam Meetingshttps://docs.communityhealthtoolkit.org/contribute/medic/onboarding/team-meetings/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/team-meetings/Product Team Monthly Call Why Getting to see each other all in one place at the same time, even if only online, lets people get to be around each other, learn from each other, and hear other perspectives. We&rsquo;re real people! &hellip;terrific people too. This is a small space to spend some time around each other as a group. We&rsquo;re mostly trying to learn more about each other, celebrate recent successes, and cover the occasional big news or team-wide changes..accept_case_reportshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/accept_case_reports/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/accept_case_reports/The accept_case_reports key contains the actions to take when reports about cases are received. +app_settings.json .accept_case_reports[] property description required form Form ID of the case form. yes silence_type A comma separated list of schedules to mute. no silence_for Duration from when the report was submitted for which messages should be muted. It is structured as a string with an integer value followed by a space and the time unit. For instance 8 weeks or 2 days..assetlinkshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/assetlinks/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/assetlinks/Requires CHT Core 4.7.0+, CHT Conf 3.22.0+, and CHT Android 1.3.0+ +When using a custom flavor of cht-android to connect to your CHT instance, the ecosystem supports using deep links to open specific content in the app. (E.g. token login links). Security measures in Android require these deep links be verified either automatically or manually. This assetlinks configuration enables auto-verification for your CHT links in your Android app. The provided JSON file will be served at https://&lt;your CHT instance&gt;/..contact_typeshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/hierarchy/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/hierarchy/From 3.7.0 it is possible to configure what types of places and people are available by modifying the contact_types array in the app settings. Each type has the following properties. +Note Prior to version 3.7.0, CHT Core supported 4 contact types - 3 place types (clinic, health_center, district_hospital) and one person type (person). app_settings.json .contact_types[] Property Description Required id String identifier for the type. At times this will be used to sort the contacts in the UI so it is recommended to using a number prefix with gaps between numbers, eg: 10-district, 20-region, etc..dhis_data_setshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/dhis2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/dhis2/From 3.9.0 it is possible to integrate with DHIS2 by modifying the dhis_data_sets property in app_settings.json. +See Also: DHIS2 Integration +app_settings.js .dhis_data_sets[] Property Type Description Required id string The data set id from DHIS2 with which to integrate Yes translation_key string The translation key of the DHIS2 data set name to be displayed Yes Code samples Configure the id and translation_key of the DHIS2 data set. The id corresponds to the id of the data set in the DHIS2 instance you want to integrate with..formshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/forms/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/forms/JSON forms are defined in either the base_settings.json or the app_settings/forms.json file and compiled in to the app_settings.json file with the compile-app-settings action in the cht-conf tool. JSON Forms are used for parsing reports from formatted SMS, SIM applications, and Medic Collect. JSON form definitions are also used for interoperability with third-party systems. Each form is defined as an JSON form object according to the following schema. The key for each object must be unique and all characters must be uppercase..header_tabshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/header_tabs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/header_tabs/app_settings.js .header_tabs As of 3.10.0, app header tabs icons can be configured by modifying the header_tabs section in the app settings. The header_tabs section consists of key:value pairs, where the key is the name of the tab to configure. These values can also be changed from the Admin console, on the Images page under the &ldquo;Header tabs icons&rdquo; tab. +{ &#34;messages&#34;: { &#34;icon&#34;: &#34;fa-user&#34; }, &#34;tasks&#34;: { &#34;resource_icon&#34;: &#34;medic-health-center&#34; }, &#34;analytics&#34;: { &#34;icon&#34;: &#34;fa-flag&#34;, &#34;resource_icon&#34;: &#34;icon-treatment&#34; } } Available tabs tab name default FontAwesome icon messages fa-envelope tasks fa-flag reports fa-list-alt contacts fa-user analytics fa-bar-chart-o app_settings..outboundhttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/outbound/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/outbound/Outbound is only available in CHT Core 3.5.0 and above +Outbound push allows configurers to have the creation or editing of CouchDB documents trigger outbound REST requests using the data in that document. For example, upon receiving a referral report you could send that referral to an external facility system that will manage and process that event. +These triggers can apply to all document types (not just common types such as reports or contacts) and as such care should be taken to only send the documents you intend (see configuration of relevant_to below)..patient_reportshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/patient_reports/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/patient_reports/The patient_reports key contains the actions to take when reports about people are received. +app_settings.json .patient_reports[] property description required form Form ID of the form. yes name Descriptive name of the form. This is not currently used in the app, but can be a helpful annotation. no format Guide of how the form can be used. This is not currently used in the app, but can be a helpful annotation. no silence_type A comma separated list of schedules to mute..permissionshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-permissions/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-permissions/Permissions are defined by the permissions object in the base_settings.json file. The list below illustrates the available system defined permissions. To utilize a permission, you will need to first add the permission as a property of the permissions object, and then associate the permission to user role(s). +Permissions can be assigned to user roles either directly in base_settings.json as an array of user role identifiers, or configured in the App Management app..registrationshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/registrations/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/registrations/The registrations key contains actions that need to be performed for incoming reports of the specified form. +app_settings.json .registrations[] property description required form Form ID that should trigger the schedule. yes events An array of event object definitions of what should happen when this form is received. yes event[].name Name of the event that has happened. The only supported event is on_create which happens when a form is received. yes event[]..remindershttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/reminders/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/reminders/Configure SMS reminders to notify primary contacts to submit reports for their places. +app_settings.js .reminders[] Property Type Description Required form string If a report with this ID is submitted for this place then the SMS will not be sent. Yes translation_key string The translation key to use to look up the SMS message content. Yes message array or string Deprecated. The SMS content. Use translation_key instead. No text_expression string The later text expression to use to set the frequency of this reminder..replication_depthhttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/replication_depth/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/replication_depth/Replication depth is defined under replication_depth as an array of objects. It grants the ability to limit document replication depending on user roles. +app_settings.json .replication_depth property description required role The configured user role the depth applies to. yes depth The replication depth value. Must be a positive integer or 0. yes report_depth As of 3.10. Replication depth applied to reports submitted by other users no Code sample: { &#34;replication_depth&#34;: [ { &#34;role&#34;: &#34;district_manager&#34;, &#34;depth&#34;: 1, &#34;report_depth&#34;: 1 }, { &#34;role&#34;: &#34;national_manager&#34;, &#34;depth&#34;: 2 } ] }.replications [deprecated]https://docs.communityhealthtoolkit.org/apps/reference/app-settings/replications/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/replications/Deprecated The replications field is only available in versions 3.5.0 to 3.9.0. As of 3.10.0 this field is ignored, and replication happens nightly for user meta databases to a central meta data database. Replications are defined under the app_settings.replications key as an array of replication objects. The definition takes the typical form below: +&#34;replications&#34;: [ { &#34;fromSuffix&#34;: &#34;user-[^\\-]+-meta&#34;, &#34;toSuffix&#34;: &#34;users-meta&#34;, &#34;text_expression&#34;: &#34;&#34;, &#34;cron&#34;: &#34;0 2 * * *&#34; } ] property description required fromSuffix The suffix of the source table(s)..roleshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-roles/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-roles/Each user is assigned one of the defined roles. Roles can be defined using the App Management app, which is represented by the roles object of the app-settings.json file. Each role is defined by an identifier as the key, and an object with the following properties: +app_settings.json .roles{} Property Description Required name The translation key for this role Yes offline Determines if user will be an online or offline user. Set to false for users to be &ldquo;online&rdquo; users..scheduleshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/schedules/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/schedules/Schedules are defined in either the base_settings.json or the app_settings/schedules.json file and compiled in to the app_settings.json file with the compile-app-settings action in the cht-conf tool. +The schedules key contains an array of schedule objects, each representing the messages to send based on a registration. +app_settings.json .schedules[] property description required name A unique string label that is used to identify the schedule. Spaces are allowed. yes summary Short description of the of the schedule..smshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/sms/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/sms/SMS settings are defined under the sms key, as an object supporting the following properties: +app_settings.json .sms property default description outgoing_service medic-gateway Defines the service to use to send SMS messages. Currently supports &ldquo;medic-gateway&rdquo;, &ldquo;africas-talking&rdquo; or &ldquo;rapidpro&rdquo;. For more information read the documentation on &ldquo;africas-talking&rdquo; configuration and &ldquo;rapidpro&rdquo; configuration. duplicate_limit 5 The number of identical sms message allowed to be sent to the same recipient. Code sample The definition takes the typical form below:.token_loginhttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/token_login/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/token_login/Login via SMS settings are defined under the token_login key, as an object supporting the following properties: +app_settings.json .token_login property type required description enabled Boolean yes Enables or disables token_login deployment-wide. When this is false, users can&rsquo;t be updated to use token_login and any requests to login with a token link will fail. translation_key String yes Translation key for the information (helper) sms message that the user receives, along with their token-login link Code sample The definition takes the typical form below:.transitionshttps://docs.communityhealthtoolkit.org/apps/reference/app-settings/transitions/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/app-settings/transitions/When sentinel detects a document has changed it runs transitions against the doc. These transitions can be used to generate a short form patient id or assign a report to a facility. +Configuration By default all transitions are disabled. They can be enabled by configuring the transitions property to have a key with the transitions name and a truthy value. As of version 3.12.0 some transitions will partially run on the client for offline users.apphttps://docs.communityhealthtoolkit.org/apps/reference/forms/app/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/forms/app/App forms are used for care guides within the web app, whether accessed in browser or via the Android app. When a user completes an app form, the contents are saved in the database with the type data_record. These docs are known in the app as Reports. +App forms are defined by the following files: +A XML form definition using a CHT-enhanced ODK XForm format A XLSForm form definition, easier to write and converts to the XForm (optional) Meta information in the {form_name}.collecthttps://docs.communityhealthtoolkit.org/apps/reference/forms/collect/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/forms/collect/ODK XForms are used to render forms in the Medic Collect Android app. These forms cannot use any CHT-specific XForm notations. All Medic Collect forms are processed as SMS (even when submitted over a wifi) therefore a corresponding JSON form with matching fields is used to interpret the incoming report. +Collect forms must be in the forms/collect folder to be processed by cht-conf&rsquo;s convert-collect-forms and upload-collect-forms actions. Once uploaded to the server, they can be downloaded by the Medic Collect app.contacthttps://docs.communityhealthtoolkit.org/apps/reference/forms/contact/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/forms/contact/Contact forms are used to create and edit contacts (persons and places). Each contact-type should ideally have two forms; one for creation, and another for editing. +These forms are stored in the forms/contact sub-folder of the project config directory. The naming convention used should be &lt;contact_type_id-{create|edit}&gt;.xlsx. +Form details Survey sheet To collect information about the contact, use a top-level group with the id of the contact_type as the name of the group (e.contact-summary.templated.jshttps://docs.communityhealthtoolkit.org/apps/reference/contact-page/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/contact-page/Contact profile pages display basic information about the contact along with their history and upcoming tasks. A contact&rsquo;s profile page is defined by the Fields, Cards, and Care Guides available. +Helper variables and functions for the contact summary can be defined in contact-summary-extras.js. There are several variables available to inspect to generate the summary information: +Variable Description contact The currently selected contact. This has minimal stubs for the contact.parent, so if you want to refer to a property on the parent use lineage below.Daily Updateshttps://docs.communityhealthtoolkit.org/contribute/medic/onboarding/daily-updates/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/daily-updates/What In our fully remote environment, staying connected and informed is crucial. One of the ways we can ensure transparency, and collaboration, and keep everyone in the loop is by utilizing the #product-dailies channel in Slack for sharing daily updates. +Who All the Product Team members are expected to share daily updates in Slack (#product-dailies channel). Due to the nature of their work, Leadership Product colleagues might share less regular, but more consolidated updates.Data Manager, Paulhttps://docs.communityhealthtoolkit.org/design/personas/data-manager-paul/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/data-manager-paul/“It’s all about training them. The Community Health Workers are just villagers and farmers, so it’s training and more training.” +About Paul has worked at BRAC for two years. He’s very focused on his career and has lots of IT projects running. He is responsible for training the branches on how to use the mobile phones. Right now Paul is looking for the right quality of phones. He also wants to understand how the database will ensure reliable syncing.extension-libs/https://docs.communityhealthtoolkit.org/apps/reference/extension-libs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/extension-libs/Introduced in v4.2.0 +Introduction Extension libraries are blocks of code that are cached with the CHT web application giving app developers a powerful tool to extend the CHT. This is an advanced feature and requires an app developer with some software development experience. +An example of a use for this feature is to provide a function to calculate a risk score based on a machine learning model. The function can then be called passing in values from app forms and return the result to be stored with the report.Mapping User on the CHT Hierarchyhttps://docs.communityhealthtoolkit.org/design/guides/mapping-hierarchy/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/guides/mapping-hierarchy/Purpose Of the Guide This guide will take you through mapping of users on CHT hierarchy, including: +defining the hierarchy (reporting structure) defining user roles mapping user personas to the CHT hierarchy Brief Overview of Key Concepts A user persona is a generalized character that embodies a particular type of user. +User roles are the activities that the user personas are expected to carry out. +A hierarchy represents the reporting structure.Publishing Insightshttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/publishing-insights/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/publishing-insights/Product owners and UXR review insights dashboard for each Focused Working Group in Klipfolio on a quarterly basis to share: Number of interviews conducted Common themes Infographics of interviews to date Table of insights and number of stories associated with each Emerging themes Share on platforms Internal All hands Slack External CHT Round Up Call Forum Here’s an example forum post.Quality Assistancehttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/quality-assistance/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/quality-assistance/Goals Software developers should have full ownership of what they are building, including quality. The team still benefits from having the QA engineering mindset of QA engineers. QA engineers automate more tests and processes. In Short A software developer writes code and performs testing on that code where a QA engineer assists by recommending tests to perform and adding additional end-to-end tests. +In Detail A developer should be able to write code and release it when done.Building SMS Formshttps://docs.communityhealthtoolkit.org/apps/tutorials/sms-forms/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/sms-forms/SMS forms allow users to submit reports from any device including feature phones without internet access. SMS forms are ideal in scenarios where targeted users have no way of accessing internet or where they are restricted to using feature phones. +This tutorial will take you through how to build SMS forms for CHT applications, including: +Defining SMS forms Setting validation rules for SMS forms Setting automatic responses to SMS reports You will be building a pregnancy registration workflow that allows Community Health Workers to register households, register household members, and register new pregnancies for the household members.targets.jshttps://docs.communityhealthtoolkit.org/apps/reference/targets/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/targets/All targets are defined in the targets.js file as an array of objects according to the Targets schema defined below. Each object corresponds to a target widget that shows in the app. The order of objects in the array defines the display order of widgets on the Targets tab. The properties of the object are used to define when the target should appear, what it should look like, and the values it will display.tasks.jshttps://docs.communityhealthtoolkit.org/apps/reference/tasks/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/tasks/Task generation is configured in the tasks.js file. This file is a JavaScript module which defines an array of objects conforming to the Task schema detailed below. When defining tasks, all the data about contacts on the device (both people and places) along with all their reports are available. Tasks are available only for users of type &ldquo;restricted to their place&rdquo;. Tasks can pull in fields from reports and pass data as inputs to the form that opens when the task is selected, enabling richer user experiences.How To Manage Translationshttps://docs.communityhealthtoolkit.org/core/overview/translations/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/translations/Apps built with CHT Core are localized so that users can use them in the language of their choice. Languages supported by default are English, French, Nepali, Spanish, and Swahili. The goal of this doc is to help the community manage these and future translations. +Overview Like the rest of the code the translation files live in the GitHub repo. These translation files are properties files, which are a series of keys and their corresponding values.Configuring UHC Modehttps://docs.communityhealthtoolkit.org/apps/guides/forms/uhc-mode/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/uhc-mode/Introduced in v2.18.0 +The CHT&rsquo;s UHC Mode empowers CHWs to provide equitable and timely care to families in their catchment area. The Community Health Toolkit supports this use-case by displaying the number of visits made to a household and highlighting households which haven&rsquo;t met their visit goal in red at the top of the contact list. +The date last visited is colored red whenever the date is 30 days or more in the past.Building Workflowshttps://docs.communityhealthtoolkit.org/apps/concepts/workflows/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/concepts/workflows/Workflows can be defined within apps built with the Core Framework to connect actions and data with people. Forms are the main building block of tasks and messaging workflows, and are useful in creating reminders for follow-up visits or referrals. +Tasks Tasks within the app can drive a workflow, ensuring that the right actions are taken for people at the right time. Tasks indicate a recommended action to the user. They indicate who the user should perform the action with, and the recommended timeframe of that action.App Buildershttps://docs.communityhealthtoolkit.org/design/personas/app-builder/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/design/personas/app-builder/About App builders and technical organizations have in-house or contracted software developers. They deploy health technology solutions for implementing partners such an iNGOs, Governments, UN agencies etc in the community health space. They have limited familiarity with end-users such as CHWs and HCD. +Values Aligned with CHT principles and willing to contribute back to the CHT in the future Believe in building open-source technologies Integrity Likeable personality Open-mindedness Strong work ethic Responsibilities Build and deliver user experiences centered on the CHT Steward the CHT app developer’s experience CHT Capacity Builders Technical support Speak the language of the business Needs Capacity and expertise with the CHT Flexible technology (and tooling) to help build holistic digital health interventions Motivations Build awesome and bug-free user experiences using the CHT Adoption of the CHT as the technology of choice for building community health apps Delivering effective interventions Novel projects or interventions Developing skills Strengths and Assets Diversity in skills sets: HCD, software development, database management, dashboard development Resourcefulness: bend the CHT&rsquo;s capabilities to meet a given need Passion for problem solving An inquisitive mindCHT Application Settingshttps://docs.communityhealthtoolkit.org/apps/tutorials/application-settings/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/application-settings/This tutorial will take you through how to manage the CHT application settings, including; +Setting user roles and permissions Enabling and disabling transitions Configuring contact hierarchy and configuring replication. App settings allow you to both persist information that is critical to the application outside the code, and to create profiles that store the preferences for project deployments. +Brief Overview of Key Concepts The settings which control CHT apps are defined in the app_settings.Code Healthhttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/code-health/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/code-health/Healthy code is code that is easy to understand and update. Over time all code becomes less healthy as dependencies become out of date, new language features or code style rules are introduced, and changes are made that add complexity. This is sometimes referred to as technical debt as it makes it progressively harder to make new code changes. No project is ever completely healthy, so we need an ongoing process to improve the least healthy parts of the code so the CHT can be sustainable long term.Configurable Hierarchieshttps://docs.communityhealthtoolkit.org/apps/concepts/hierarchy/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/concepts/hierarchy/The Core Framework requires a hierarchy to organize the information in the app. It is often based on the hierarchy of a health system within a particular geographic region. +Large deployment sites often need three or more levels of place hierarchy, while some small sites need fewer than three levels. For this reason, the Core Framework’s information hierarchies are configurable to meet a users needs. +A user logging into their app will see a custom set of people, tasks, reports, and analytics based on the hierarchy level that they belong to.Setting up Multi-facility usershttps://docs.communityhealthtoolkit.org/apps/tutorials/multi-facility-users/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/multi-facility-users/This tutorial will take you through how to create and assign users to multiple places in the CHT UI. Assigning users to multiple places is only available from CHT 4.9.0. +This tutorial covers; +Creating contacts and their associated users Creating places and assign contacts to those places Assigning users to multiple places The CHT application settings allows you to both persist information that is critical to the application outside the code, and to create profiles that store the preferences for project deployments.Building SMS Scheduleshttps://docs.communityhealthtoolkit.org/apps/tutorials/sms-schedules/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/sms-schedules/SMS schedules allow you to send reminder messages at predetermined times. These reminders serve as useful prompts for end-users to take specific actions. +This tutorial takes you through how to set up SMS schedules for CHT applications. It uses a pregnancy registration workflow and follow-up reminders for a Community Health Worker as an example. The same methodology can be applied to other workflows and reminders as needed. +Brief Overview of Key Concepts SMS schedules are a series of SMS messages that are to be sent to specific contacts at future dates and times.Development Workflowhttps://docs.communityhealthtoolkit.org/contribute/code/workflow/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/workflow/Code Writing Where possible, follow our coding style guide. +Aim for self-documenting code. Where code cannot be made self-documenting add commenting. Usually comments are useful when they explain why some code exists, and should not be explaining what some code is doing. +Pushing Code &amp; Opening Pull Requests Never push commits directly to the main branch (main or master). Always use a pull request. +If your code is in a regular pull request, it is assumed to be done and only needing a review and testing as checks before merging.Building App Formshttps://docs.communityhealthtoolkit.org/apps/tutorials/app-forms/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/app-forms/App forms allow users to submit reports from Android devices +This tutorial will take you through how to build App forms for CHT applications, including: +Authoring forms in Excel, Google sheets or other spreadsheet applications. Converting XLSForms to XForms Uploading XForms to CHT You will be building assessment workflow that allows Community Health Workers to conduct a health assessment for children under the age of 5. +Brief Overview of Key Concepts App forms serve as actions within the app.Transparency & Accountabilityhttps://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/transparency-accountability/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/transparency-accountability/It is important that people around Medic, our partners, and community can always see what is being worked on and that commitments are made and kept. There are a few key ways teammates are expected to operate to enable such transparency and accountability. +GitHub project boards Each FWG has a board where tickets are displayed and actively kept up-to-date. These boards are publicly readable and anyone should be able to see the items on the board, what state they are in, and have an expectation of when they will be done.Usershttps://docs.communityhealthtoolkit.org/apps/concepts/users/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/concepts/users/Apps built with the Core Framework use roles and permissions to determine who has access to what data. User roles are general categories you can use to assign a collection of broad permissions to users. There are two classes of roles: online and offline. Generally speaking, CHWs are usually offline users, while managers and nurses are usually online users. SMS users do not use the app, and thus do not have a user role.CHT Product Repository Checklisthttps://docs.communityhealthtoolkit.org/contribute/code/repository-checklist/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/repository-checklist/Repository Creation Checklist When creating a new CHT Product repository under Medic&rsquo;s GitHub organization, the contributor(s) should use the cht-repo-template repository containing the following configurations: +Source Control The main branch is locked via branch protection rules. Merges are done through PRs. Automatically delete head branches. Issue templates exist. PR template exists. PRs reference related issues. Commit formats follow the guidelines. Secrets are not part of the commit history or made public.Setting Form Propertieshttps://docs.communityhealthtoolkit.org/apps/tutorials/form-properties/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/form-properties/This tutorial will take you through how to write the &lt;form_name&gt;.properties.json file. +The &lt;form_name&gt;.properties.json file allows you to add logic that ensures that the right action appears for the right contacts (people and places). For instance, an assessment form for children under-5 will only appear for person contacts on the CHT whose age is less than 5. +You will be adding meta-data and context to an assessment workflow that allows Community Health Workers to conduct a health assessment for children under the age of 5.Interoperabilityhttps://docs.communityhealthtoolkit.org/apps/concepts/interoperability/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/concepts/interoperability/Introduction Interoperability refers to the ability of different health information systems and applications to communicate with each other and exchange data seamlessly. With interoperability, patient information can be seen, exchanged, and used across different platforms. The information/data exchanged has to be understood across the different software for these systems to become interoperable. This is different from integration which requires custom development to connect two specific systems together. +Interoperability is the best practice for health systems because it allows information from one system to be shared with one or more other systems with no additional development.Updating Dependencieshttps://docs.communityhealthtoolkit.org/contribute/code/core/update-dependencies/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/core/update-dependencies/Every minor release we update dependencies to get the latest fixes and improvements. We do this early in the release cycle so that we have some more time to find regressions and issues. This is done on all folders with a package.json, including: +cht-core / (root) /admin /api /sentinel /shared-libs/* /webapp cht-conf Steps Checkout and pull the latest default branch - get the latest code Make a branch: git checkout -b &quot;&lt;issue&gt;-update-dependencies&quot; Take a look at the current list of dependencies related issues, where you can find the latest conversations and information.Prerequisites for App Developmenthttps://docs.communityhealthtoolkit.org/apps/concepts/prerequisites/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/concepts/prerequisites/There are no set prerequisites for users of CHT apps, yet the following are helpful for developing CHT applications. +Test Instance To build your own application using the Core Framework you will need an instance set up for testing. You can set up a local instance by following these instructions. +Build tool The build tool for applications using the Core Framework is cht-conf. To set it up, follow the installation instructions.Coding Style Guidehttps://docs.communityhealthtoolkit.org/contribute/code/style-guide/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/style-guide/Language Prefer JavaScript or TypeScript wherever possible, including in webapps, on the server, and for scripting. This is because: +every developer on the team knows it already, so we can all maintain it together, it makes it easy to write cross-platform code and we have developers on all major operating systems, it has a vast number of libraries that are easy to include, and it&rsquo;s easy to unit test Exceptions to this can be made on a case-by-case basis, but the decision must be made collectively before coding has begun to avoid having to rewrite.Building A Simple Taskhttps://docs.communityhealthtoolkit.org/apps/tutorials/tasks-1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/tasks-1/Tasks prompt users to complete activities on a programmatic schedule. This guide will explain how to write a task which prompts CHW users to complete an assessment app form for new patients within 7 days of registration. +Creating a straight-forward task Running and testing that task Prerequisites Complete the App Forms Tutorial - Tasks prompt users to complete activities by opening an app form. The app forms tutorial produces an assessment app form which we will use here.AWS Hosting in CHT 3.xhttps://docs.communityhealthtoolkit.org/hosting/3.x/ec2-setup-guide/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/3.x/ec2-setup-guide/Most production CHT instances are deployed on AWS EC2. Leveraging Elastic Compute Cloud (EC2) and Elastic Block Store (EBS), CHT instances can easily be scaled up with larger EC2 instances and have easy increased disk space, backup and restores with EBS. +This guide will walk you through the process of creating an EC2 instance, mounting an EBS volume and provisioning Docker containers. +Create and Configure EC2 Instance Create EC2 (use security best practices)CHT App Configurerhttps://docs.communityhealthtoolkit.org/contribute/code/cht-conf/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/cht-conf/Requirements nodejs 18 or later python 3 Docker(optional) Installation Operating System Specific Linux (Ubuntu) macOS Windows (WSL2) npm install -g cht-conf sudo python -m pip install git+https://github.com/medic/pyxform.git@medic-conf-1.17#egg=pyxform-medic npm install -g cht-conf pip install git+https://github.com/medic/pyxform.git@medic-conf-1.17#egg=pyxform-medic # As Administrator: npm install -g cht-conf python -m pip install git+https://github.com/medic/pyxform.git@medic-conf-1.17#egg=pyxform-medic --upgrade Using Docker CHT Conf can also be run from within a Docker container. This is useful if you are already familiar with Docker and do not wish to configure the various dependencies required for developing CHT apps on your local machine.Configurationhttps://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/configuration/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/configuration/CHT gateway supports Android 4.1 and above. To have it up and fully working, follow the 3 steps below. +Install the latest APK from the releases page in the cht-gateway repo. This APK is not in the Play Store, you will need to side-load it as is done with CHT Android. +Open the app. if you are installing the app for the first time or afresh, you will get a Warning:medic-gateway is not set as the default messaging app on this device .Migration from CHT 3.x to CHT 4.xhttps://docs.communityhealthtoolkit.org/hosting/4.x/data-migration/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/data-migration/The hosting architecture differs entirely between CHT-Core 3.x and CHT-Core 4.x. Migrating data from an existing instance running CHT 3.x requires a few manual steps. This guide will present the required steps while using a migration helping tool, called couchdb-migration. This tool interfaces with CouchDb, to update shard maps and database metadata. By the end of this guide, your CHT-Core 3.x CouchDb will be down and CHT-Core 4.x ready to be used.Glossary and Definitionshttps://docs.communityhealthtoolkit.org/glossary/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/glossary/A Accredited Social Health Activist (ASHA) Antenatal Care (ANC) Antiretrovirals (ARV) Artificial intelligence (AI) Assisted Global Positioning System (A-GPS) B Backend The parts of the software that make the front end happen. These are all hidden from the user. +C Center for Disease Control and Prevention (US) (CDC) Childhood Immunizations (IMM) Community Based Organization (CBO) Community Based Volunteers (CBV) Community Health Assistants (CHA) Community Health Extension Worker (CHEW) Community Health Information System (CHIS) Community Health Promoters (CHP) Community Health Toolkit (CHT) Community Health Volunteer (CHV) Community Health Worker (CHW) &ldquo;The umbrella term &lsquo;Community Health Worker&rsquo; embraces a variety of community health aides selected, trained and working in the communities from which they come&rdquo; (WHO, 2007 PDF).Roadmaphttps://docs.communityhealthtoolkit.org/core/overview/roadmap/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/overview/roadmap/The CHT is actively developed by Medic, a non-profit organization that serves as the technical steward, alongside many partners in the CHT Community. +Improvements for the CHT are raised and discussed with the community in the Forum, and tracked in the CHT Roadmap. The roadmap is organized around Product initiatives; specific improvement areas that align with medium and long-term vision for the CHT. These initiatives are organized by the ones that are being worked on Now, those that are likely Next, and those that might be Later.Self Hosting in CHT 4.x - Single CouchDB Nodehttps://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/single-node/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/single-node/This for a single node CHT 4.x instance and is the recommended solution for small deployments. If you want a more powerful setup, check out the 4.x multi-node install docs. +Prerequisites Be sure you have followed the requirements document including installing Docker and Docker Compose. This guide assumes you&rsquo;re using the ubuntu user and that it has sudo-less access to Docker. +Directory Structure Create the following directory structure: +|-- /home/ubuntu/cht/ |-- compose/ |-- certs/ |-- couchdb/ |-- upgrade-service/ By calling this mkdir commands:Documentation Style Guidehttps://docs.communityhealthtoolkit.org/contribute/docs/style-guide/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/docs/style-guide/This style guide provides a set of editorial guidelines for anyone writing documentation for Community Health Toolkit projects. These are guidelines, not rules. Use your best judgment. +Note This documentation site does not involve release management and acceptance testing. Help us maintain the quality of our documentation by submitting a pull request (PR) with any suggested changes. One of the repository&rsquo;s maintainers will review the PR, request additional changes as needed, and merge the PR when it is ready.Building Target Widgetshttps://docs.communityhealthtoolkit.org/apps/tutorials/targets/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/targets/This tutorial will take you through how to build target widgets. +Target widgets provide a summary or analysis of the data in submitted reports. +You will be adding target widgets that will allow Community Health Workers (CHWs) to track various metrics based on assessment reports submitted. +Brief Overview of Key Concepts Targets is the user dashboard or analytics tab. +Target widgets provide a summary or analysis of the data in submitted reports.Using NPMhttps://docs.communityhealthtoolkit.org/contribute/code/using-npm/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/using-npm/npm Orgs We use npm Orgs to organize our npm packages. It provides a centralized way to manage a team&rsquo;s published npm packages and permissions. Here are some guidelines when using this service. +See npm&rsquo;s Orgs docs for more information. +Our organization is medic or using npm&rsquo;s notation, @medic. +We also created @medicmobile but it&rsquo;s not currently in use, it was created to reserve the namespace. +Adding a Package When you publish an npm module on npmjs.Building Contact Summaryhttps://docs.communityhealthtoolkit.org/apps/tutorials/contact-summary/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/contact-summary/This tutorial will take you through building a contact summary for CHT applications. +Contact summaries display basic information about the contact. +You will be adding a contact summary that displays information about a person&rsquo;s patient id, age, sex, phone number, and information about the place they belong to ie. parent. +Brief Overview of Key Concepts Each field that can be shown on a contact’s profile is defined as an object in the fields array of contact-summary.Building Death Report Workflowshttps://docs.communityhealthtoolkit.org/apps/tutorials/death-reporting/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/death-reporting/Death Reporting Guide for setting up a comprehensive death report workflow In this tutorial you will learn how to set up a death report workflow. This includes laying out a death report form as well as handling all the configurations needed for wiring it up in the CHT. By the end of the tutorial you should be able to: +Mark select contacts as deceased Make relevant app updates for dead contacts Brief Overview of Key Concepts When a contact is marked as deceased within the CHT, the contact will be hidden by default on the contacts tab.Static Analysishttps://docs.communityhealthtoolkit.org/contribute/code/static-analysis/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/static-analysis/eslint All code must pass an eslint check which runs early in the CI cycle and uses the standard medic eslint configuration. +Sonar Sonar static analysis supports development by providing feedback on code quality and security issues. Sonar analysis must pass on all new code. +SonarCloud can be enabled on any public repo in the medic organization. +Workflow During development While writing code, the SonarLint plugin can be used to get real-time code analysis in your IDE.Automated Testshttps://docs.communityhealthtoolkit.org/contribute/code/core/automated-tests/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/core/automated-tests/The goal of automated testing Developers should be able to make changes in the codebase quickly and confidently. A big part of this means knowing that new changes have not impacted other functionality in the system and everything continues to work as expected. +Of course any new functionality itself may or may not work as expected and it is up to the developer to write the appropriate tests to ensure it works correctly in both expected and unexpected scenarios.Building Condition Cardshttps://docs.communityhealthtoolkit.org/apps/tutorials/condition-cards/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/condition-cards/This tutorial will take you through building a condition card for CHT applications. +Condition cards, like contact summaries display information about the contact. The data displayed in condition cards can be pulled from submitted reports. +In this tutorial,you will be adding a condition card that displays information about a person&rsquo;s most recent assessment, including: the date of the most recent assessment, and whether or not they had a cough.Design Documents Guidehttps://docs.communityhealthtoolkit.org/contribute/code/design-docs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/design-docs/What are design docs? Software development is not just about writing code, but rather about solving problems and building the right solutions. Before diving into an initiative or feature and starting coding, it’s essential that the developers (and other team members) have a high-level understanding of what a solution might look like. +Design docs are informal documents that the leading developer of a certain piece of software creates before they start the actual coding of a solution.Style guide for automated testshttps://docs.communityhealthtoolkit.org/contribute/code/core/style-guide-automated-e2e-tests/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/core/style-guide-automated-e2e-tests/There are three files that are the base of every new automated test case, the most important one is the spec file, which contains the actual test that will be executed. +Automated tests cover different CHT Configs, consider the following setups when writing a new test: +default Config file: ../tests/e2e/default/wdio.conf.js Name convention for the spec file: ../tests/e2e/default/*/&lt;name&gt;.wdio-spec.js Command to execute the tests that belong to this config: npm run wdio-local Important: Make sure the spec file follows the name convention, otherwise the file won&rsquo;t be executed.Building CHT Android Flavorshttps://docs.communityhealthtoolkit.org/apps/guides/android/branding/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/android/branding/This tutorial will take you through building a CHT Android Application off the existing wrapper. +The CHT Android application is a thin wrapper to load the CHT Core Framework web application in a WebView. +You will be adding a new android flavor based off the CHT Android. +Brief Overview of Key Concepts The CHT Android is a native Android container for the Community Health Toolkit (CHT). The repository contains &ldquo;flavored&rdquo; configurations, where each &ldquo;flavor&rdquo; or &ldquo;brand&rdquo; is an app.Localizationhttps://docs.communityhealthtoolkit.org/apps/tutorials/localizing-cht/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/localizing-cht/Given that CHT apps are used around the world, the Core Framework was designed with localization in mind. The Core Framework itself is available in English, French, Hindi, Nepali, Spanish, Swahili, and Indonesian. +This tutorial will take you through localizing the CHT to a custom language (Swahili). This will include setting up the user interface labels as well as outgoing text messages. +By the end of the tutorial you should be able to:Configuring CHT Application Graphicshttps://docs.communityhealthtoolkit.org/apps/tutorials/application-graphics/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/application-graphics/This tutorial will take you through customising some graphical elements of CHT core. +You will cover site branding, partner logos, header tab icons, and app icons (used in tasks, targets, and contacts). +Required Resources You should have a functioning CHT instance with cht-conf installed locally and completed a project folder setup. +Implementation Steps 1. Site branding You have the ability to modify the app title, logo, and favicon. For Progressive Web App installations you can also configure the desktop icon.Application Testshttps://docs.communityhealthtoolkit.org/apps/tutorials/application-tests/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/application-tests/This tutorial takes you through testing the various configurable components of CHT applications using cht-conf-test-harness. +Prerequisites Complete the following tutorials: +Building App Forms Building A Simple Task Building Target Widgets Building Contact Summary Importance of testing your application Testing your CHT application is important as it ensures you are consistently maintaining your application and defining implementation requirements. Testing also helps the user make better architectural decisions, optimizes the forms, tasks and other components of the application.How to bulk load usershttps://docs.communityhealthtoolkit.org/apps/guides/data/users-bulk-load/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/users-bulk-load/The bulk user upload feature is available in 3.16.0 and later versions of the CHT. As of CHT 3.17.0, when creating both a contact and a place, the contact will be set as the default contact of the place. User creation can be scripted using the CHT API directly or using the cht-conf tool, which is detailed in the CSV-to-Docs guide. +This feature can be used to load as many users as possible but works optimally with chunks of 1,000 users or less.CHT Impact Metricshttps://docs.communityhealthtoolkit.org/apps/guides/data/impact-metrics/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/impact-metrics/Impact monitoring is an essential part of both the Community Health Toolkit and Medic&rsquo;s processes and ethos. We are committed to harnessing data to: +Support our partners in data-driven operational and strategic decision making Inform our product roadmap and organizational strategy, and Participate in overarching policy discussions around community health. As a member of the CHT community and potential CHT implementer, we encourage you to learn more about the recommended impact metrics for monitoring and evaluation across priority use cases by reviewing the metrics listed below.Detecting and fixing production data on training instanceshttps://docs.communityhealthtoolkit.org/apps/guides/data/training-instance/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/training-instance/After onboarding CHWs, sometimes data ends up on the wrong CHT instance. There are some passive and active actions you can take to help deal with this situation. +Monitoring Monitoring is a good way to see if CHWs are sending forms to the wrong CHT server. By catching such a problem early, it may be easy to fix manually which avoids more laborious fixes on the command line for admins.Developing on Windowshttps://docs.communityhealthtoolkit.org/contribute/code/core/using-windows/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/core/using-windows/We don&rsquo;t actively support development on Windows, instead preferring MacOS or Linux. +However, Microsoft has recently been stabilizing their Windows Subsystem for Linux, which appears to work reasonably well for development. +Installation instructions are mostly the same as they written in the README with a couple of caveats as of time of writing (2019-07-25), noted below. +Note Both the Windows Subsystem for Linux and Medic&rsquo;s support for developing in it is very much in beta.Revalidate invalid reportshttps://docs.communityhealthtoolkit.org/apps/guides/data/invalid-reports/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/invalid-reports/You may encounter a dreaded case when reports coming in to a Medic Webapp instance have a red indicator instead of the green indicator. +This may be caused by: +Missing forms in the app_settings config. Missing or incorrect fields in the input form. e.g Missing patient ID, or Patient ID with letters Extra fields in the input form. This happens when you don&rsquo;t configure for some fields in the app_settings.json of the webapp Configuring some forms in the wrong section of the app_settings i.Running multiple Chrome versionshttps://docs.communityhealthtoolkit.org/contribute/code/core/run-multiple-chrome-versions/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/core/run-multiple-chrome-versions/Note These steps are suitable for Mac. It was tested in a Mac Intel. It can be adapted to any Chrome version. Follow these steps on a Mac to run Chrome version 90 while having another Chrome app on a different version. +Download Chrome 90.0.4430.72 from slimjet Do not install the Google Chrome.app in your Application folder. Install it in your Desktop folder for example. Change the name of the app to Google Chrome 90.CSV to Docshttps://docs.communityhealthtoolkit.org/apps/guides/data/csv-to-docs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/data/csv-to-docs/Seeding data with cht-conf Users, contacts, and report data can be specified in comma-separated value (CSV) files, then converted to JavaScript Object Notation (JSON) files and uploaded into your instance using cht-conf. This documentation will cover the CSV notation used, fetching CSV files from Google Sheets, converting the CSV files into JSON docs, and then uploading the data from the JSON files to your instance. +Converting CSVs Running cht-conf with the csv-to-docs action converts CSV files from the csv folder into JSON docs to be uploaded to your instance.Africa’s Talking SMS Aggregatorhttps://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/africas-talking/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/africas-talking/As of v3.6.0, SMS messages can be sent and received using the Africa&rsquo;s Talking service. +Africa&rsquo;s Talking configuration First generate a long unique key to use as the cht-api-key. +Log on to the Africa&rsquo;s Talking Dashboard and configure your callback URLs as follows. +Delivery Reports: https://&lt;hostname&gt;/api/v1/sms/africastalking/delivery-reports?key=&lt;cht-api-key&gt; Incoming Messages: https://&lt;hostname&gt;/api/v1/sms/africastalking/incoming-messages?key=&lt;cht-api-key&gt; Then generate an &ldquo;API Key&rdquo; (we&rsquo;ll refer to this as the at-api-key) and save this in your CHT Core configuration covered below.Self Hosting in CHT 4.x - Multiple CouchDB Nodes on Docker Swarmhttps://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/multiple-nodes/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/multiple-nodes/The clustered multi-node hosting described below is recommended for deployments that need increased performance gains. These gains will increase the complexity of troubleshooting and decrease the ease ongoing maintenance. +If you are unsure which deployment to use check out Self-hosting recommendations. +About clustered deployments In a clustered CHT setup, there are multiple CouchDB nodes responding to users. The ability to horizontally scale a CHT instance was added in version CHT 4.Self Hosting in CHT 4.x - Multiple CouchDB Nodes on k3s on VMWarehttps://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/self-hosting-k3s-multinode/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/self-hosting-k3s-multinode/This page covers an example k3s cluster setup on a VMware datacenter with vSphere 7+ for a national deployment across 50 counties capable of supporting 20,000+ CHWs concurrently. After setup, administrators should only add VMs to the cluster or deploy CHT Core projects to be orchestrated. +About container orchestration A container orchestrator helps easily allocate hardware resources spread across a datacenter. For national scale projects, or a deployments with a large number of CHT Core instances, Medic recommends a lightweight Kubernetes orchestrator called k3s.Phoneshttps://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/phones/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/phones/In order to use SMS workflows with the CHT you will need an SMS gateway. For reliability an SMS Aggregator, such as Africa&rsquo;s Talking or RapidPro, is recommended. When an SMS Aggregator is not available, an Android device running CHT Gateway can be used to send and receive SMS in your CHT application. You may use an existing Android device and are not required to purchase a new one. However, for more reliable sending and receiving of SMS, the Android device should be in your organization’s office or facility with a consistent internet connection.RapidPro Messaging Gatewayhttps://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/rapidpro/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/rapidpro/As of v3.11.0, messages can be sent and received using RapidPro as a messaging gateway. +RapidPro configuration Store globals Generate a long unique key to use as the cht_api_key. +Log in to your RapidPro dashboard, go to the globals page (/global/) and create two globals with the following data: +name: cht_url, value: https://&lt;your-cht-instance-host&gt;/api/v2/sms/rapidpro/incoming-messages. For security the instance host must not include basic authentication. (NB: This endpoint was added in CHT 4.Installing RapidPro - CHT Gateway Android APKhttps://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/rapidpro_cht_gateway/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/rapidpro_cht_gateway/RapidPro - CHT uses your Android phone to send and receive messages on your behalf. +Due to restrictions that Google has placed on Android applications that send SMS messages, RapidPro - CHT applications can no longer be distributed through the Google Play Store. You will need to download the application file and install it using the steps below: +Download App - On your Android device, open your browser and download the app by entering the URL: https://rapidpro.Self Hosting in CHT 3.xhttps://docs.communityhealthtoolkit.org/hosting/3.x/self-hosting/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/3.x/self-hosting/Whether run on bare-metal or in a cloud provider, the Community Health Toolkit (CHT) core framework has been packaged into a docker container to make it portable and easy to install. It is available from dockerhub. To learn more how to work with docker you could follow the tutorial here and the cheat sheet here. +Note Before continuing, ensure all requirements are met. Installing with a compose file The CHT containers are installed using docker compose so that you can run multiple containers as a single service.SMS message stateshttps://docs.communityhealthtoolkit.org/apps/guides/messaging/sms-states/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/sms-states/Interaction with SMS providers CHT Applications can use CHT Gateway and third party aggregators to send and receive SMS messages. +When an SMS report comes in from a user, medic-sentinel adds the appropriate list of scheduled messages (to be sent at a future date) to the report doc. +Periodically, sentinel checks for messages that need to be sent, and sets their state to pending if their scheduled sending time has been reached or passed.CHT User Management toolhttps://docs.communityhealthtoolkit.org/apps/tutorials/user-management-tool/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/user-management-tool/The Community Health Toolkit (CHT) is highly configurable and can be customized to support multiple hierarchies and users in the health care system. The CHT user management tool is a user friendly web application that works with the CHT to decentralize the user management process to the subnational levels, increasing efficiency and accuracy. This guide highlights steps for setting up and configuring the user management tool. The guide has been tailored for specific CHT-supported national community health information systems.Short Contact Identifiershttps://docs.communityhealthtoolkit.org/apps/guides/messaging/shortcodes/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/shortcodes/Short unique identifiers for contacts are often used to identify contacts in messaging workflows. Unique short codes are generated on doc.patient_id against any document of a person type, and on every doc.place_id against any document of a place type. By default, these IDs start at 5 numeric digits long, and will increase in length as deemed necessary by the generation algorithm. +If the length is increased, this increase is stored in a CouchDB documented called shortcode-id-length:App Developer Hosting in CHT 3.xhttps://docs.communityhealthtoolkit.org/hosting/3.x/app-developer/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/3.x/app-developer/This guide assumes you are a CHT app developer wanting to either run concurrent instances of the CHT, or easily be able to switch between different instances without loosing any data while doing so. To do development on the CHT core itself, see the development guide. +To deploy the CHT in production, see either AWS hosting or Self hosting +Getting started Be sure to meet the CHT hosting requirements first.App Developer Hosting in CHT 4.xhttps://docs.communityhealthtoolkit.org/hosting/4.x/app-developer/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/app-developer/This guide assumes you are a CHT app developer wanting to either run concurrent instances of the CHT, or easily be able to switch between different instances without losing any data while doing so. To do development on the CHT Core Framework itself, see the development guide. +To deploy the CHT 3.x in production, see either AWS hosting or Self hosting. 4.x production hosting guides are coming soon! +Getting started Be sure to meet the CHT hosting requirements first.Contributor Hall of Famehttps://docs.communityhealthtoolkit.org/contribute/code/hall-of-fame/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/contribute/code/hall-of-fame/Code Thank you to everyone who has contributed to the CHT codebase over the years! To see the full list, visit each repo on GitHub. +CHT Android CHT Conf CHT Core CHT Docs CHT Interoperability CHT Pipeline CHT Sync CHT Watchdog Security Kudos to everyone who has disclosed security vulnerabilities. +Alex Anderson with 4 disclosures +#9122, #9121, #9120, #9108SSL Cert Install in CHT 3.xhttps://docs.communityhealthtoolkit.org/hosting/3.x/ssl-cert-install/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/3.x/ssl-cert-install/Requirements Installed CHT-Core 3.x via either Self Hosted, EC2 or Local Setup, but must use docker-compose. Your own SSL certifications like Let&rsquo;s Encrypt. Copy certs into medic-os container On your server copy the .crt and .key files to the medic-os container. The existing self signed .crt and .key files will be overwritten: +sudo docker cp /path/to/ssl.crt medic-os:/srv/settings/medic-core/nginx/private/default.crt sudo docker cp /path/to/ssl.key medic-os:/srv/settings/medic-core/nginx/private/default.key Restart services Now that the .crt and .key files are in place, restart nginx in the medic-os container with:Message Loopshttps://docs.communityhealthtoolkit.org/apps/guides/messaging/message-loops/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/message-loops/Endless messaging loops can between the webapp and a mobile number via the gateway due to autoreplies from the webapp. +See the Github Issue. +Solution: Add the offending number(e.g 800 or SAFARICOM) to the Outgoing Deny List in the webapp&rsquo;s app_settings configuration file. +&#34;multipart_sms_limit&#34;: 10, &#34;outgoing_deny_list&#34;: &#34;800, SAFARICOM&#34;, &#34;contact_summary&#34;: &#34;&#34;Troubleshootinghttps://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/troubleshooting/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/troubleshooting/In a techlead heaven, we would have immediate physical access to gateway phones, but alas, most of the time we have to hand them over to the partner. Fortunately, comes this guide on debugging gateway problems. +Follow the steps as below (if you don&rsquo;t have physical access to the phone, start with step 2 i.e debug from the server side first) +Make sure that the device: +Is connected to the internet.Adding Privacy Policies to Appshttps://docs.communityhealthtoolkit.org/apps/guides/privacy/privacy-policy/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/privacy/privacy-policy/As of 3.10.0, privacy policies can be customized for every language, by adding desired content into HTML files. +Privacy policies are now publicly accessible rather than only being available after logging in. This means it can be shared with third parties, for example, app store compliance. If your instance URL is https://my-health-facility.org, then the privacy policy is available at https://my-health-facility.org/medic/privacy-policy. Added in 3.17.0. +Add these HTML files to the privacy-policies folder in your configuration.Introduction to monitoring and alertinghttps://docs.communityhealthtoolkit.org/hosting/monitoring/introduction/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/monitoring/introduction/This guide applies to all production instances of the CHT for both 3.x (beyond 3.9) and 4.x. +Be sure to see how to deploy a solution to monitor and alert on production CHT instances. +Each deployment will experience different stresses on its resources. Be sure to tune any alerting levels in the case of a false positive so that you may avoid them in the future. Any thresholds for alerts, and even what is alerted on, is just a guideline, not a guarantee of uptime.Offline Hosting of CHT 3.x Serverhttps://docs.communityhealthtoolkit.org/hosting/3.x/offline/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/3.x/offline/This guide is not meant for a production CHT instance. Support may be added in the future an offline CHT server in a production environment. Please see the &ldquo;Considerations&rdquo; section below. +Proceed only if you have staff familiar with DNS, TLS Certs, DHCP, LAN topology and Linux in general. This is a complex deployment where mistakes are easy to make unless proper training is in place. +Note This guide only applies to CHT 3.CHT Watchdog Setuphttps://docs.communityhealthtoolkit.org/hosting/monitoring/setup/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/monitoring/setup/These instructions apply to both CHT 3.x (beyond 3.12) and CHT 4.x. +Medic maintains CHT Watchdog which is an opinionated configuration of Prometheus (including json_exporter) and Grafana which can easily be deployed using Docker. It is supported on CHT 3.12 and later, including CHT 4.x. By using this solution a CHT deployment can easily get longitudinal monitoring and push alerts using Email, Slack or other mechanisms. All tools are open source and have no licensing fees.Adding TLS certificates in CHT 4.xhttps://docs.communityhealthtoolkit.org/hosting/4.x/adding-tls-certificates/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/adding-tls-certificates/By default, CHT 4.x will create a self-signed certificate for every deployment. These instructions are for changing to either a pre-existing certificate or automatically creating and renewing a Certbot based certificate using ACME, like Let&rsquo;s Encrypt. +This guide assumes you&rsquo;ve already met the hosting requirements, specifically around Docker being installed. +Pre-existing certificate To load your certificates into your CHT instance, we&rsquo;ll be creating an interstitial container called cht-temp-tls which will enable you to copy your local certificate files into the native docker volume.Production CHT Watchdoghttps://docs.communityhealthtoolkit.org/hosting/monitoring/production/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/monitoring/production/These instructions apply to both CHT 3.x (beyond 3.12) and CHT 4.x. +What it means to run in production When you run CHT Watchdog in production, and it is publicly accessible on the Internet, and has mission-critical data on it, you should take extra precautions around security and backup. This mainly consists of: +using TLS for all HTTP connections using VPN or SSH for insecure protocols like ssl=false in Postgres ensuring if the server were to fail, you can recover the data This guide assumes you have already set up TLS on your CHT instance and have gone through the Setup steps to deploy an instance of CHT Watchdog on server with a static IP and DNS entry, monitor.Viewing server logs in CHT 4.xhttps://docs.communityhealthtoolkit.org/hosting/4.x/logs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/logs/CHT 4.x has the following services running via Docker and each can have its logs queried: +nginx sentinel api haproxy couchdb healthcheck upgrade-service Setting log level By default, the CHT server logs are set to the info level. To change the log level to debug, you can set the NODE_ENV environment variable to development. A log level of debug can affect system performance and cause log files sizes to grow rapidly.Backups in CHT 4.xhttps://docs.communityhealthtoolkit.org/hosting/4.x/backups/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/backups/This guide is about backups in CHT 4.x - there&rsquo;s the self hosted guide for 3.x which includes backups for 3.x. +Introduction As CHT 4.x uses a container per service, the only data that needs to be backed up is: +CouchDB database Docker Compose and .env files TLS certificates This is because Docker containers are inherently stateless so all the important binaries are already stored in CHT&rsquo;s Docker images. Docker Compose files, including the .Custom Postgres metrics in CHT Watchdoghttps://docs.communityhealthtoolkit.org/hosting/monitoring/postgres-ingest/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/monitoring/postgres-ingest/These instructions apply to both CHT 3.x (beyond 3.12) and CHT 4.x. +Introduction After setting up your Watchdog instance and making it production ready, you can include additional custom metrics from your deployment. These metrics should be ingested by Prometheus and then can be used to create new Grafana dashboards and alerts. Example use cases include monitoring and alerting on health metrics like CHW visits per county or household registration rates, etc.Integrating CHT Watchdoghttps://docs.communityhealthtoolkit.org/hosting/monitoring/integration/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/monitoring/integration/These instructions apply to both CHT 3.x (beyond 3.12) and CHT 4.x. +Going beyond basic setup After you have done the setup of CHT Watchdog and configured it to run with TLS and have backups enabled, you may want to extend it to scrape other Prometheus data sources so that Grafana can send alerts on non-CHT Core metrics. +This guide uses example instances of CHT Core (cht.example.com) and CHT Watchdog (watchdog.Search Resultshttps://docs.communityhealthtoolkit.org/search/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/search/0.x release noteshttps://docs.communityhealthtoolkit.org/core/releases/0.4.15-and-earlier/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/0.4.15-and-earlier/0.4.15 March 2, 2017 +Fixed potential race condition with medic-gateway. Issue: medic-projects/issues/1243 Bumped libphonenumber to make phone number validation more up to date. Issue: medic-projects/issues/1005 0.4.14 December 16, 2016 +Bug fix for medic-gateway sending scheduled messages. Issue: #2535 0.4.13 October 21, 2016 +Option to set birthdate using days old instead of weeks. Issue: #2756 The week/month is off by 2 in the Reporting Rates analytics dashboard. Issue: #2781 Remove socket limit in medic-api.2.10.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.10.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.10.0/Features Use reference to translation keys in app_settings. Issue: #3127 Add date of birth to person created by SMS. Issue: #3100 Configure the max number of SMS in multipart SMS. Issue: #3095 Load messages script fails to use https. Issue: #3081 Cannot access all fields for contact in select2. Issue: #3069 Configurable contact summary cards. Issue: #3037 Display additional information in contact profile. Issue: #2914 Support additional context for hiding/showing actions.2.10.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.10.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.10.1/Bug fixes Sending a message from the Messages tab creates a message with uuid equal to database URL. Issue: #32422.10.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.10.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.10.2/Bug fixes Sentinel somehow infinitely loops and continually writes to its metadata file. Issue: #3275 API crashes after /medic/_bulk_docs gets called. Issue: #32682.10.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.10.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.10.3/Bug fixes Unicode support for storing enketo xml. Issue: #3308 Support negative values in xform fields better. Issue: medic/medic-projects#1624 Trigger enketo calc updates when option names are changed. Issue: #3281 New Household button missing. Issue: #3132 Change report form language without requiring refresh. Issue: #3174 Corrupted translation strings. Issue: #3305 UI/UX improvements Show report subject name on patient page. Issue: #3309 Translate task schedule group titles. Issue: #3283 Add additional supported moment locales.2.11.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.11.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.11.0/Migration notes #3230 changes patient ID generation so it automatically increases the length as needed, up to 13 digits. If you are validating incoming patient_ids in Sentinel, be sure to remove or correct any length restrictions, e.g. ^[0-9]{5}$ would become ^[0-9]{5,13}$. #3166 adds a new transition that adds patient_ids to every created person: generate_patient_id_on_people. Enable this transition if you want to send SMS about patients that may be created through the webapp.2.11.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.11.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.11.1/Bug fixes Cannot report via SMS about people who are registered in the web app. Issue: #3401 Results page CSS messed up in v2.11. Issue: #3369 The user needs an associated contact to create a contact. Issue: #3394 Error when adding Place with new person. Issue: #3420 Error after canceling and re-opening any contact creation form. Issue: #3448 namespace-form-fields migration : report bulk errors. Issue: #3371 (second part)2.11.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.11.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.11.2/Performance improvements Slow initial replication for users with lots of docs. Issue: #35082.11.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.11.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.11.3/Bug fixes The namespace-form-fields migration conflicts itself. Issue: #3534 In the create-patient-contacts migration provide a more complete list of potential patient_name locations. Issue: #33722.12.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.12.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.12.0/Features Add sync status indicator for offline users. Issue: #3357 Add gateway message delivery statuses. Issue: #3073 Add a replication_date property to records. Issue: #2180 Change patient id generation to store the length of id it&rsquo;s generating. Issues: #3505 Allow form upload through Form Configuration UI. Issue: #3433 Bug fixes On small screen, cannot re-open date filter in history tab. Issue: #3467 Debug section of the About screen has some weird extra text.2.12.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.12.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.12.1/Bug fixes Improved error messages for SMS endpoint. Issue: #3587 Allow for empty SMS message Content. Issue: #3656 Implement 500 item max for bulk delete. Issue: #3605 Security Fixed kanso packages that inadvertently cached credentials. Issue: #36482.12.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.12.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.12.2/Bug fixes Accept patient reports for patients created in app. Issue: #3740 Stop accept_patient_reports transition clearing messages for unrelated registrations. Issue: #37422.12.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.12.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.12.3/Bug fixes Accept messages with empty from or content. See: https://github.com/medic/medic-api/pull/1852.12.4 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.12.4/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.12.4/Bug fixes Fix issue with /api/v1/records. Issue: #37702.12.5 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.12.5/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.12.5/Bug fixes Fix bug where id generation wouldn&rsquo;t automatically increase id length when it ran out of ids. Issue: #37902.13.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.13.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.13.0/Migration notes #2635 changes the context available to the configured contact summary script. The contact parameter no longer has information about parents. This information is now in an array called lineage. More information is available in the configuration documentation. #3546 changes the implementation of the contact_summary so instead of declaring the output on the last line of the script, now you have to return the output. Usually this is as easy as adding a return on the last line, so output; becomes return output;.2.13.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.13.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.13.1/Bug fixes Fix bug in extract-person-contacts migration introduced in 2.13.0 #4031 Remove the now invalid erlang migrations #40332.13.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.13.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.13.2/Bug fixes Force outputs to recalc on form load #41112.13.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.13.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.13.3/Improvements Bump libphonenumber to the latest2.13.4 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.13.4/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.13.4/Improvements Bump libphonenumber for Nepal Smart Telecom.2.13.5 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.13.5/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.13.5/Bug fixes Update Notification transition crashes sentinel if the patient id is misconfigured. #41212.13.6 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.13.6/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.13.6/Performance improvements Drastically improve performance of form loading when the patient context is used, and that context is very large (e.g. you&rsquo;re including lineages). #44302.13.7 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.13.7/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.13.7/Performance improvements Various PouchDB performance improvements were backported from 2.14. This includes increasing the PouchDB version and removing our use of pouchdb-worker.2.14.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.14.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.14.0/Additional release notes are available here. +Migration notes #3449: We included a feature which makes it unnecessary to use a repeat-relevant node in Enketo forms to workaround a bug which created an empty child. This node should now be removed. #3629: We added more configurable text to the target widgets. Also, configuring an array of target titles is now deprecated in favor specifying a single translation key. Reconfigure your targets to specify values for translation_key, subtitle_translation_key, and percentage_count_translation_key properties.2.14.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.14.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.14.1/Performance improvements #4430: Drastically improve performance of form loading when the patient context is used, and that context is very large (e.g. you&rsquo;re including lineages).2.14.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.14.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.14.2/Bug fixes #3099: Uncaught exception triggers 500 response for subsequent requests.2.14.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.14.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.14.3/Bug fixes #4457: The z-score enketo widget is not usable. #4460: Uncaught Exception: write after end error.2.15.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.15.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.15.0/What&rsquo;s New View &lsquo;clinic&rsquo; Places in Places Filter You might have noticed that for SMS projects, CHW areas went missing from the places filter in the Reports tab. Good news! They are back. +When we started having CHWs log into the Medic app and register families, the places filter on the reports page became crowded with thousands of families, creating a performance issue. To get around that issue, we removed 'clinic' level places from this filter drop-down so that users would only see health centers and CHW areas, no families.2.16.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.16.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.16.0/What&rsquo;s New View date last visited for places on the people tab Because knowing where you&rsquo;ve been helps you know where you&rsquo;re going next! - Medic proverb +In order to help CHWs achieve full coverage of every family or household they care for, we&rsquo;ve added an optional feature to update the list of families or areas to display the date that family or area was last visited. You can use any patient- or family-level form or forms to update the date last visited.2.16.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.16.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.16.1/What&rsquo;s New Show an icon next to families or areas that are overdue for a visit As the ICONic Britney Spears once sang, &ldquo;Show me a siiiiiign… [visit] me, baby, one more time!&rdquo; +In order to help CHWs achieve full coverage of every family or household they care for, we added a feature in 2.16 to update the list of families or areas to display the date that family or area was last visited.2.17.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.17.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.17.0/What&rsquo;s New Show pictures in the report view (History tab) Your selfies are safe with us (and now visible in the Reports tab). +Photos uploaded by CHWs are now visible in the Reports view, or History tab. This is currently being used in the mRDT workflow by supervisors to confirm that CHWs read the mRDT test results correctly. [#4742] +Improve styling of mRDT Enketo widget &ldquo;OK, but make it pretty.&rdquo; Fine, here you go: ✨ UI fairy dust ✨2.18.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.18.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.18.0/What&rsquo;s New Make People page default sort configurable (UHC mode) Previously, the default sort for the People page list was alphabetical. We&rsquo;ve now made the default sort configurable. For UHC mode, this would likely mean sorting by last visited date. In the future, it could include sorting by number of visits this month or another value. +To enable default contact sorting to be based on last visited date, you need to configure it in app_settings.2.18.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.18.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.18.1/What&rsquo;s New Count two visits on the same day as one visit If a family is visited twice on the same day it now only counts as one visit in UHC mode. [#4897] +Inputs group not saved when its relevance is set to false Form inputs are now always saved on the reports even when they are marked as not relevant to help with analytics and editing forms. [#4875]2.6.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.6.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.6.0/This release contains breaking changes from 0.x versions. Updating from 0.x versions may result in the application no longer operating as expected. +The app can now be used offline and synced back to the server later. Added an android app for accessing the webapp from mobile. Added Tasks feature for rich event scheduling. Forms can now be provided in XForm format for rich form UIs. Added a configurable Target analytics module.2.6.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.6.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.6.1/ User&rsquo;s fullname is not showing up in /configuration/users. Issue: #2200 Deleted documents cause sentinel log spam. Issue: #1999 Disable nools for unrestricted users. Issue: medic-projects#149 Update libphonenumber and use strict validation. Issue: #2159 #2196 Contacts export response garbled. Issue: #21872.6.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.6.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.6.2/Update PouchDB to improve replication reliability and performance. Issue: #2134 #2167 When editing a CHP Area, previously set values for CHP, Branch, and Supervisor do not show up. Issue: #2223 Dropdowns in CHP Area create and edit forms have no blank option. Issue: #2227 allow-new appearance in Enketo doesn&rsquo;t make the &ldquo;New&rdquo; option appear. Issue: #2251 Improve performance of Enketo db-object-widget. Issue: #2161 Ensure roles are always available on user-settings.2.6.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.6.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.6.3/ &ldquo;console not defined&rdquo; error when loading page. Issue: #2277 Pouch doesn&rsquo;t update seq unless something has changed. Issue: #2288 Snackbar showing all the time. Issue: #2306 Support external_id property on user-settings docs. Issue: #23102.7.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.7.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.7.0/Features Bulk delete reports. Issue: #1000 Bug fixes Report list item summaries aren&rsquo;t translated. Issue: #2100 Fix form type filter. Issue: #1409 Performance improvements Replication performance. Issue: #2286 Improve search performance. Issue: #2302 Don&rsquo;t fetch form titles for each Contact report. Issue: #2300 Only fetch relevant data for the Users service. Issue: #2262 Remove clinics from the Facility filter dropdown. Issue: #2218 Optimize admin bandwidth concerns. Issue: #2211 We request facilities from the server over and over again.2.7.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.7.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.7.1/Bug fixes Creating user via fails due to invalid reported_date. Issue: #2449 Performance improvements App takes minutes to load a person dropdown. Issue: #2445 Cannot load Configuration Users page. Issue: #24442.7.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.7.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.7.2/Bug fixes Connection refused when trying to load app. Issue: #24762.7.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.7.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.7.3/Bug fixes Remove maxSockets limit to allow more concurrent connections. Issue: #24922.8.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.8.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.8.0/Features Pass user&rsquo;s info to rule to customize Tasks per user type or location. Issue: #2408 Add context to target types and goals. Issue: #2409 Update default translations Add ageInDays and ageInMonths functions to the XML forms context utilities. Issue: #2650 Users can now only access an optionally configured number of hierarchy levels below their facility. Issue: #2648 Bug fixes Android back button doesn&rsquo;t work as expected. Issue: #2600 In date filter for Reports tab, the selected dates are being offset by 1 day.2.8.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.8.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.8.1/Bug fixes If initial sync fails without syncing anything subsequent syncs get no results. Issue: #2770 Initial sync fails if server doesn&rsquo;t respond within 30 seconds. Issue: #2771 Targets tab is blank on first access. Issue: #2739 Performance improvements Adding a space to a contact search term performs poorly. Issue: #2769 Local DB grows without limit. Issue: #24342.8.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.8.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.8.2/Bug fixes Ensure PouchDB doesn&rsquo;t mis-label TECNO phones as devices running Safari. Issue: #27972.8.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.8.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.8.3/Performance improvements Remove traffic statistics collection. Issue: #28862.8.4 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.8.4/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.8.4/Bug fixes Debounce form submissions to stop duplicate submissions. Issue: #29092.8.5 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.8.5/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.8.5/ No changes, only a bump in version number to trigger a new release.2.9.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.9.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.9.0/Features Redesign of People tab to introduce patient centric workflows. Create Task and Target based on reports using short patient_id format. Issue: #2986 Calculate Z-Score within app workflow form. Issue: #2915 Transitions do not run for XForms. Issue: #2864 CHWs should not be able to edit their own area. Issue: #2844 Allow for people-centric SMS workflows. Issue: #2700 Unique &ldquo;add person&rdquo; forms to a place. Issue: #2693 Store GPS failure. Issue: #2670 Progressive Web App.2.9.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/2.9.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/2.9.1/Bug fixes Added a migration to fix scheduled messages so they can be sent by medic-gateway. Issue: #30153.0.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.0.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.0.0/Upgrade notes The supported versions for client and server software have been changed significantly. Make sure your software meets the requirements before upgrading to 3.0.0. The /api/v1/messages endpoint has been removed as it was no longer actively used, and contained bugs. [#3971] The ANC analytics page and the following APIs have been removed as they are no longer used. [#1002] /api/active-pregnancies /api/upcoming-appointments /api/missed-appointments /api/upcoming-due-dates /api/high-risk /api/total-births /api/missing-delivery-reports /api/delivery-location /api/visits-completed /api/visits-during /api/monthly-registrations /api/monthly-deliveries The /api/v1/export/messages, /api/v1/export/forms, and /api/v1/export/contacts endpoints have been removed in favor of /api/v2/export/messages, /api/v2/export/reports, and /api/v2/export/contacts respectively.3.1.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.1.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.1.0/Upgrade notes There are no breaking changes when upgrading from 3.0.x. +What&rsquo;s New SMS spam protection SMS message generation will be skipped if an identical message has been generated recently meaning our software won&rsquo;t get into an infinite loop with autoresponding robots. [#4715]. +Flexible phone number validation Phone number validation can be configured to be strict (default), tolerant, or disabled altogether. This is useful if phone numbers are being incorrectly rejected as invalid which can happen if carriers update their number ranges.3.10.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.10.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.10.0/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes Updates to meta databases replication The replication of users meta databases to the conglomerate, medic-users-meta database, is no longer configurable. This task now runs every day, at 2am UTC, and replicates feedback and telemetry documents to medic-users-meta database. +Android wrapper update required for new features Supporting remote first-time login is only fully functional while using medic-android version 0.3.10.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.10.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.10.1/Known issues Check the repository for the latest known issues. +Supported software There are no required changes to the supported software matrix from 3.0.0. +Node CouchDB Browsers SMS bridge Android medic-android medic-couch2pg 8.11+ 2.1+ Chrome 53+, Firefox latest medic-gateway 4.4+ 0.4.5+ 3.0+ Bug fixes cht-core#6700: For targets without idType, the winner emission is not deterministic3.10.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.10.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.10.2/Known issues Check the repository for the latest known issues. +Supported software There are no required changes to the supported software matrix from 3.0.0. +Node CouchDB Browsers SMS bridge Android medic-android medic-couch2pg 8.11+ 2.1+ Chrome 53+, Firefox latest medic-gateway 4.4+ 0.4.5+ 3.0+ Bug fixes cht-core#6848: Language translations not working when not supported by make-plural Features cht-core#6849: Add bootstrap-datepicker translations for Tagalog (tl) Illonggo (hil) and Bisaya (ceb) languages cht-core#6861: Add moment locales for Tagalog (tl) Illonggo (hil) and Bisaya (ceb) languages We recognize that it is atypical to have new features in a &ldquo;bugfix&rdquo; version.3.10.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.10.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.10.3/Known issues Check the repository for the latest known issues. +Supported software There are no required changes to the supported software matrix from 3.0.0. +Node CouchDB Browsers SMS bridge Android medic-android medic-couch2pg 8.11+ 2.1+ Chrome 53+, Firefox latest medic-gateway 4.4+ 0.4.5+ 3.0+ Upgrade notes This release fixes issues around using the default hierarchy and having contact documents that have a default type (person, clinic, health_center or district_hospital) and also have a contact_type property.3.10.4 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.10.4/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.10.4/Known issues Check the repository for the latest known issues. +Breaking changes None. +UI/UX changes None. +Bug fixes #7169: API can return 401 status codes for valid sessions under load, forcing users to be logged out3.10.5 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.10.5/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.10.5/This release adds a fix that overrides the default 2 minute timeout of Node HTTP requests. Client requests will no longer timeout at API level. Timeouts are still possible, but can only come from the load balancer or CouchDB. +Known issues Check the repository for the latest known issues. +Breaking changes None. +UI/UX changes None. +Bug fixes #7183: Client requests receive a 502 statuscode after 2 minutes of idle time3.11.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.11.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.11.0/Known issues CHT Android medic-android#127: Image upload forms crash the app. This has been broken for some time and is not easy to fix while supporting Android 4.4 so the resolution has been deferred until we can make this breaking change. Reach out if you require this feature in the near future. CHT Core Check the repository for the latest known issues. +Upgrade notes This upgrade can be rolled out remotely.3.11.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.11.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.11.1/Known issues Check the repository for the latest known issues. +Breaking changes None. +UI/UX changes None. +Bug fixes #7169: API can return 401 status codes for valid sessions under load, forcing users to be logged out3.11.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.11.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.11.2/This release adds a fix that overrides the default 2 minute timeout of Node HTTP requests. Client requests will no longer timeout at API level. Timeouts are still possible, but can only come from the load balancer or CouchDB. +Known issues Check the repository for the latest known issues. +Breaking changes None. +UI/UX changes None. +Bug fixes #7183: Client requests receive a 502 statuscode after 2 minutes of idle time3.11.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.11.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.11.3/This release adds a fix for the phone widget&rsquo;s validation when editing forms. +Known issues Check the repository for the latest known issues. +Breaking changes None. +UI/UX changes None. +Bug fixes #7261: Phone field invalid when editing a contact with a valid phone number.3.12.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.12.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.12.0/Known issues Check the repository for the latest known issues. +Upgrade notes This upgrade can be rolled out remotely. Users will download the new version in the background and be prompted to reload the app when it&rsquo;s ready. A small amount of data will be needed to download the new version. Upgrading to this version does not require other applications to be upgraded. This release does not drop support for any hardware or software that works with the previous version.3.12.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.12.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.12.1/This release adds a fix for the phone widget&rsquo;s validation when editing forms. +Known issues Check the repository for the latest known issues. +Breaking changes None. +UI/UX changes None. +Bug fixes #7261: Phone field invalid when editing a contact with a valid phone number.3.13.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.13.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.13.0/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes Configuration for Task Due Date Display By default, an overdue task is simply displayed as &ldquo;Due today&rdquo;. This enhancement adds configuration to allow for displaying the number of days passed since the task&rsquo;s due date. This configuration is modified by setting the task_days_overdue value as described in the documentation. +If the configuration is not set, there will be no UX changes to the way that overdue tasks are displayed.3.14.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.14.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.14.0/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes New dialog showing sync status When an offline user manually triggers a sync via the hamburger menu, a notification dialog is opened indicating the status of the sync process. +Sync in progress: Sync complete: Sync failed (with retry option): #5207: Show dialog when user clicks &ldquo;Sync Now&rdquo; in hamburger menu +Updated icon for the Create Report button The icon for the Create Report button has been updated so that its purpose is more clear.3.14.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.14.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.14.1/This release adds a fix for repeat groups in Enketo forms to pick the correct translation for fields. +Known issues Check the repository for the latest known issues. +Breaking changes None. +UI/UX changes None. +Bug fixes #7515: Translations not working when using repeat groups with dynamic repeat count3.14.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.14.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.14.2/This release adds a fix for repeat groups in Enketo forms using choice_filter. +Known issues Check the repository for the latest known issues. +Breaking changes None. +UI/UX changes None. +Bug fixes #7550: Fix blank labels in forms with repeat group using choice_filter3.15.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.15.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.15.0/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Highlights Support reporting of Bikram Sambat dates using JSON forms SMS forms can now include a date form for submitting an exact date in Bikram Sambat format. Learn how to use this feature in the documentation. +#4613: Support reporting of dates as exact date using text forms +A new API for adding users in bulk Previously there was an API for creating a single user but this feature adds an API for creating multiple users in one go.3.16.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.16.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.16.0/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes #6028: Gracefully handle overflowing form title text Highlights Support adding many users at once using data from a CSV file New CHT users (and associated places) can now be added in bulk by importing the data from a CSV file. Learn how to use this feature in the documentation. +#7706: Add support for bulk user upload from CSV3.16.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.16.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.16.1/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #7912: Calling /api/v1/settings/deprecated-transitions prevents any future transitions to run over new records in API #7933: Error 500 when processing SMS message missing year from Bikram Sambat aggregate3.17.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.17.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.17.0/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes Action button labels now show for all devices Action button labels now show on lower resolution phones, making it easier to understand what each action button does. +#7721: Icon labels on action buttons are not showing at the default dp for commonly used mobile devices. Update to search and filters on the Contacts and Reports tabs Search and filters on the Contacts and Reports tabs have been updated to more closely align with Android UX and material design patterns.3.17.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.17.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.17.1/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #7912: Calling /api/v1/settings/deprecated-transitions prevents any future transitions to run over new records in API #7933: Error 500 when processing SMS message missing year from Bikram Sambat aggregate3.17.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.17.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.17.2/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #7670: Same patient id assigned to multiple patients in a same instance #8576: Low performance in Reports tab for users with thousands of places3.2.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.2.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.2.0/Upgrade notes There are no breaking changes when upgrading from 3.1.x. +Muting You can now mute people and places which stops any scheduled messages from being sent and updates the UI for that contact. [#4767] +For more information read the feature overview and the configuration documentation. +UHC performance If you have UHC configured loading contacts is now much faster. [#4768] +Benchmarks The following benchmarks were taken on a Tecno Y4 as a CHW with a representative amount of data.3.2.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.2.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.2.1/Bug fixes More reliable replication In earlier versions if a device&rsquo;s replication connection got interrupted some documents may never be replicated to that device leaving it in an unknown state. To avoid this it is recommended that everyone upgrade to 3.2.1 or above as soon as possible. [#5235] +Refresh dialog not shown When upgrading from 2.x to 3.2.0 the dialog to update isn&rsquo;t presented to the user. The user is unaware the app updated and would need to do a manual refresh to get the latest app changes.3.3.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.3.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.3.0/Upgrade notes There are no breaking changes when upgrading from 3.2.x. +Purging Documents on phones can now be deleted once no longer useful. This allows for space on the device to be recovered and improves performance. Read the configuration documentation for how to add script to indicate that a document is no longer useful and is ready for purging. [#5048] +Outgoing messages screen Administrators can now see all outgoing messages that are either scheduled, due, or unable to be sent.3.4.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.4.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.4.0/Known issues medic#5617: Broken functionality after upgrade to 3.4 with custom locales Upgrade notes Breaking changes There are no breaking changes when upgrading from 3.3.x. +Supported software There are no required changes to the supported software matrix from 3.0.0. +Node CouchDB Browsers SMS bridge Android medic-android medic-couch2pg 8.11+ 2.1+ Chrome 53+, Firefox latest medic-gateway 4.4+ 0.4.5+ 3.0+ Scale to support more users In previous versions users who had an internet connection would maintain a continuous request to the server waiting for any relevant database changes.3.4.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.4.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.4.1/Bug fixes Unicode form codes not clearing schedules When using non-Latin characters in a form code our software failed to find the right schedules to clear so unwanted messages were being sent. This affects versions from 3.0.0 to 3.4.0. medic#56983.5.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.5.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.5.0/Known issues A small performance regression when loading the list of Contacts. medic#5755. Upgrade notes Breaking changes There are no breaking changes when upgrading from 3.4.x. +Supported software There are no required changes to the supported software matrix from 3.0.0. +Node CouchDB Browsers SMS bridge Android medic-android medic-couch2pg 8.11+ 2.1+ Chrome 53+, Firefox latest medic-gateway 4.4+ 0.4.5+ 3.0+ Performance Optimizing the history tab We found that loading the History tab was taking a long time in projects with many places.3.6.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.6.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.6.0/Known issues None. +Upgrade notes Breaking changes There are no breaking changes when upgrading from 3.5.x. +Supported software There are no required changes to the supported software matrix from 3.0.0. +Node CouchDB Browsers SMS bridge Android medic-android medic-couch2pg 8.11+ 2.1+ Chrome 53+, Firefox latest medic-gateway 4.4+ 0.4.5+ 3.0+ Africa&rsquo;s Talking integration Version 3.6.0 includes an integration with the Africa&rsquo;s Talking SMS aggregator. This allows us to offer a cloud based alternative to managing your own medic-gateway devices.3.6.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.6.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.6.1/Bug fixes medic#5844: Source validation failing for Africa&rsquo;s Talking aggregator integration3.6.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.6.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.6.2/Performance fixes medic#5942: Replace underscore with lodash in replication3.7.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.7.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.7.0/Known issues None. +Upgrade notes Breaking changes A new OS level dependency (xsltproc) is required for generating XFORM html on the server. Not fully supported by medic-conf@2.x. For full functionality, users must upgrade to medic-conf@3.x. With the introduction of configurable hierarchies, additional configuration must be added. More information about how to set up configurable hierarchies. Supported software There are no required changes to the supported software matrix from 3.0.0. +Node CouchDB Browsers SMS bridge Android medic-android medic-couch2pg 8.3.7.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.7.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.7.1/Bug fixes cht-core#6025: Outbound can halt scheduler execution permanently cht-core#6029: Inconsistent field label in CHT Reference App - Blue skin color cht-core#6052: Missing labels for report form fields cht-core#6065: Confirmation notification displayed while navigating away from tasks list cht-core#6071: Configurable contacts not correctly mapped as SMS recipients medic-conf#260: Fields are not showing when custom type is defined for appliesToType3.8.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.8.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.8.0/Known issues No known issues. +Upgrade notes Breaking changes The changes to tasks and targets require a &ldquo;3.8 compatible&rdquo; partner configurations to be deployed for the tasks and target tabs to continue to function. You&rsquo;ll see errors on these tabs and the console error Rules Engine: Updates to the nools schema are required if the configuration is not compatible with 3.8. All &ldquo;3.8 compatible&rdquo; configurations are backward compatible and can be deployed safely to any Core Framework version.3.8.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.8.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.8.1/Upgrade notes CouchDB view code has been modified which will require rebuilding which may take some time depending on how many docs you have. We recommend you use the Stage feature to rebuild the views before upgrading to reduce server downtime. Webapp code has been modified and therefore users will download the application again. Supported software There are no required changes to the supported software matrix from 3.0.0. +Node CouchDB Browsers SMS bridge Android medic-android medic-couch2pg 8.3.8.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.8.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.8.2/Known issues When logging out (or after being logged out), users could end up in a redirect loop (cht-core#6337). Supported software There are no required changes to the supported software matrix from 3.0.0. +Node CouchDB Browsers SMS bridge Android medic-android medic-couch2pg 8.11+ 2.1+ Chrome 53+, Firefox latest medic-gateway 4.4+ 0.4.5+ 3.0+ Bug fixes cht-core#6583: Users are forced to login one year after they last logged in3.9.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.9.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.9.0/Known issues None. +Upgrade notes Breaking changes Docker image update required Prior to initiating an upgrade to 3.9, you will need to update the CHT Docker Image and a few packages inside the medic-os container. Please closely follow our 3.9 CHT Docker Image Upgrade Process +This image updates the horticulturalist package to stage ddocs properly. +Outbound Push only sends each report once The implementation of Outbound Push has changed as part of cht-core#6306.3.9.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.9.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.9.1/Supported software There are no required changes to the supported software matrix from 3.0.0. +Node CouchDB Browsers SMS bridge Android medic-android medic-couch2pg 8.11+ 2.1+ Chrome 53+, Firefox latest medic-gateway 4.4+ 0.4.5+ 3.0+ Bug fixes cht-core#6562: Switching between tabs while tasks are being calculated can result in having multiple task documents with the same emission id cht-core#6583: Users are forced to login one year after they last logged in3.9.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/3.9.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/3.9.2/Supported software There are no required changes to the supported software matrix from 3.0.0. +Node CouchDB Browsers SMS bridge Android medic-android medic-couch2pg 8.11+ 2.1+ Chrome 53+, Firefox latest medic-gateway 4.4+ 0.4.5+ 3.0+ Bug fixes cht-core#6700: For targets without idType, the winner emission is not deterministic cht-core#6756: Backwards compatibility for location gathering4.0.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.0.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.0.0/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes To prepare for this release, please read through our Preparing to Upgrade documentation. +Upgrade process Be aware that as this is a major upgrade some manual steps are required - following the usual upgrade process will not work. Data migration documentation is coming soon, but in the meantime please reach out on the forum for direct support in upgrading.4.0.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.0.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.0.1/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #7912: Calling /api/v1/settings/deprecated-transitions prevents any future transitions to run over new records in API #7918: Error loading forms containing countdown-widget #7933: Error 500 when processing SMS message missing year from Bikram Sambat aggregate Contributors Thanks to all who committed changes for this release! +Gareth Bowen Diana Barsan4.1.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.1.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.1.0/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes Bulk delete of reports redesign The bulk delete of Reports tab has been redesigned to allow the user to select multiple reports and delete them. You can read more about it in the Reports tab docs. +#7778: Bulk delete of reports redesign Highlights Support replacing a CHW user without any connectivity Offline users can now be replaced on a device so that a new user can use that device without needing to immediately sync with the server.4.1.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.1.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.1.1/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Features #7712: Add moment and datepicker translations for Luganda Technical improvements #8110: E2E test failing when next year is leap year #8112: Integration E2E test failing when next year is leap year Contributors Thanks to all who committed changes for this release! +Gareth Bowen Andra Blaj Diana Barsan4.1.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.1.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.1.2/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #8173: API Changes watcher skips changes - or becomes blocked #8205: Nginx can&rsquo;t connect to API after container restarts because of dynamic IP allocation Contributors Thanks to all who committed changes for this release! +Diana Barsan Gareth Bowen4.2.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.2.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.2.0/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes Floating Action Button The additive actions (creating reports, places, people, etc&hellip;) have moved from the bottom action bar to a Floating Action Button that opens a menu with all actions. This change aligns the CHT more closely with Android UX and material design patterns, and applies to the Messages, Reports, and Contacts tab.4.2.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.2.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.2.1/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Improvements #8214: Align nginx and ALB timeout values Contributors Thanks to all who committed changes for this release! +Diana Barsan Jennifer Q Gareth Bowen4.2.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.2.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.2.2/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #8161: Telemetry doc metadata.versions.app is unknown for all telemetry docs #8359: Infinite scrolling not working on Contacts tab Contributors Thanks to all who committed changes for this release! +Diana Barsan Jennifer Q Gareth Bowen4.2.3 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.2.3/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.2.3/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #8539: Sentinel stuck in infinite loop when 100 sequential deletions fail to generate tombstones Technical improvements #8558: Skip Protractor e2e tests suites on old supported branches Contributors Thanks to all who committed changes for this release! +Diana Barsan Jennifer Q Gareth Bowen4.2.4 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.2.4/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.2.4/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #7670: Same patient id assigned to multiple patients in a same instance #8576: Low performance in Reports tab for users with thousands of places4.3.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.3.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.3.0/Known issues Check the repository for the latest known issues. +Upgrade notes Continuous downwards replication (the algorithm through which offline users download docs from the server) has been completely rewritten. This change required a high number of view updates, which implies that staging this upgrade and indexing views before upgrading will be a lengthy process - depending on the size of the database. Additionally, the server might need additional storage while this process is ongoing.4.3.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.3.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.3.1/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #8478: Invalid phone for patient still running triggers Contributors Thanks to all who committed changes for this release! +Prajwol Shrestha Gareth Bowen Jennifer Q Diana Barsan4.3.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.3.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.3.2/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #7670: Same patient id assigned to multiple patients in a same instance #8576: Low performance in Reports tab for users with thousands of places #8589: Users unable to edit the report they created4.4.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.4.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.4.0/Known issues Check the repository for the latest known issues. +Upgrade notes The deprecated hardcoded national_admin role no longer behaves as a CouchDb admin. The role now behaves as an ordinary online user role, with no additional implicit permissions. Configurations using national_admin role can either switch to using a CouchDb admin for admin-only tasks and/or granting more permissions to national_admin. +Breaking changes None. +UI/UX changes CHT dialogs. CHT dialogs were updated to align with Material guidelines.4.4.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.4.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.4.1/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #7670: Same patient id assigned to multiple patients in a same instance #8576: Low performance in Reports tab for users with thousands of places #8589: Users unable to edit the report they created Contributors Thanks to all who committed changes for this release! +Jennifer Q Gareth Bowen Diana Barsan4.4.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.4.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.4.2/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #8745: error.loading.form.no_authorized when opening a form from tasks tab because context evaluates to false Contributors Thanks to all who committed changes for this release! +Diana Barsan4.5.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.5.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.5.0/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes #8513: Align &ldquo;Target&rdquo; components with Material design guidelines #8541: Options with long names look wrong in enketo selects Highlights Schedules can be created by passing array on start_from It is now possible to configure the start_from property to accept array of fields while configuring SMS schedules. This allows users to pass multiple fields and the first existing field will be used to create schedules.4.5.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.5.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.5.1/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #8745: error.loading.form.no_authorized when opening a form from tasks tab because context evaluates to false Contributors Thanks to all who committed changes for this release! +Diana Barsan Jennifer Q4.5.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.5.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.5.2/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #8837: Telemetry date not logged on telemetry documents Contributors Thanks to all who committed changes for this release! +Jennifer Q4.6.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.6.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.6.0/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes #6177: Improve look and utility of the &ldquo;About&rdquo; page #7770: Browser compatibility modal notice for Chrome version 75-90 #8075: Update default branding to CHT logo #8660: Link to Contact&rsquo;s Profile from Messages tab Highlights Allow contact searches in forms to be filtered by descendants of the current contact A contact selector can be used in forms to allow users to select a contact by searching.4.7.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.7.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.7.0/Known issues Due to a critical bug that prevented further upgrades from 4.7.0, we have have removed 4.7.0 from our releases list, so that it will not be possible to upgrade to 4.7.0. The bug has been fixed in 4.7.1. If you have already installed or upgraded to 4.7.0, please connect with us on the forum to get unblocked. We apologise for the inconvenience. Upgrade notes Breaking changes None. +UI/UX changes #5807: Fix widget wrapping when displaying multiple columns.4.7.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.7.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.7.1/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #9117: Impossible to upgrade from 4.7.0 Technical improvements None. +Contributors Thanks to all who committed changes for this release! +Diana Barsan4.7.2 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.7.2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.7.2/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #9166: &ldquo;users-meta failed with compilation_error&rdquo; when upgrading from 4.2.4 #9187: Haproxy unit tests are failing due to haproxy patch release Technical improvements None.4.8.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.8.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.8.0/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Highlights Performance The Contacts page loads much faster now. Using apdex scores and testing on low spec devices, we were able to identify slow areas and make improvements to both the list view and the detail view. +Apdex Improvements #9006: Improves performance of the list view by reducing the number of rows fetched each time #9019: Improves performance of the detail view by reducing loops for tasks and reports Security Two security issues with severity level of &ldquo;high&rdquo; and one of &ldquo;low&rdquo; were fixed in this release.4.8.1 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.8.1/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.8.1/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes None. +Bug fixes #9166: &ldquo;users-meta failed with compilation_error&rdquo; when upgrading from 4.2.4 #9187: Haproxy unit tests are failing due to haproxy patch release Technical improvements None.4.9.0 release noteshttps://docs.communityhealthtoolkit.org/core/releases/4.9.0/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/core/releases/4.9.0/Known issues Check the repository for the latest known issues. +Upgrade notes Breaking changes None. +UI/UX changes This release adds support for assigning multiple places to users. Users who are configured to have multiple places will see some subtle UI changes as described in the Highlights section. For a video walkthrough of the changes, check out the June 2024 CHT Round-up call and this forum post. +Highlights Allow multiple places to be assigned to users To better support Supervisors who manage CHWs across multiple areas, it is now possible to assign more than one “Place” (ie Community Health Unit, Health Center, etc…) to a user.Creating Additional Docs from App Formshttps://docs.communityhealthtoolkit.org/apps/guides/forms/additional-docs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/additional-docs/In version 2.13.0 and higher, you can configure app forms to generate additional docs upon submission. You can create one or more docs using variations on the configuration described below. One case where this can be used is to register a newborn from a delivery report, as shown below. First, here is an overview of what you can do and how the configuration should look in XML: +Extra Docs Extra docs can be added by defining structures in the model with the attributeMaking Calls and Sending SMS from App Formshttps://docs.communityhealthtoolkit.org/apps/guides/forms/app-form-sms/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/app-form-sms/Triggering Calls and SMS When an XForm is loaded on a phone you can start a phone call or trigger the sending of an SMS within the form itself. This can be useful if within a task or assessment, you want to tell the user to contact a patient, or perhaps a health worker at a facility. +To set up the call or SMS you&rsquo;ll need to create a link with tel: or sms: within a note field.Obtaining Browser and Phone Logshttps://docs.communityhealthtoolkit.org/apps/guides/debugging/obtaining-logs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/debugging/obtaining-logs/There are many places where useful logs reside. This details all those places, and the easiest way to get a hold of them. +On a laptop or desktop To check if there are relevant logs open up the developer console in your browser. The shortcut is probably COMMAND+OPTION+I on MacOS, or CTRL+SHIFT+I on Linux and Windows. Click the console tab and copy out any errors or logging that you think is relevant.CHT APIhttps://docs.communityhealthtoolkit.org/apps/reference/_partial_cht_api/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/_partial_cht_api/Introduced in v3.12.0 +Provides CHT-Core Framework&rsquo;s functions to contact summary, targets and tasks. The API is available in the cht reserved variable under the v1 version. +Function Arguments Description hasPermissions(permissions, userRoles, chtPermissionsSettings) permissions: String or array of permission name(s). +userRoles: (Optional) Array of user roles. Default to the current logged in user. +chtPermissionsSettings: (Optional) Object of configured permissions in CHT-Core&rsquo;s settings. Default to the current instance&rsquo;s configured permissions. Returns true if the user has the permission(s), otherwise returns false.Building A Complex Task (Optional)https://docs.communityhealthtoolkit.org/apps/tutorials/tasks-2/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/tasks-2/Tasks prompt users to complete activities on a programmatic schedule. This tutorial will guide you through the development of an advanced task. This is an optional tutorial and is not required to get started with CHT Application development. +Create a task with a complex follow-up schedule Use a 3rd party JavaScript library luxon to make Date/Time calculations easier Pass information from the task into the action app form Custom logic for resolving a task Prerequisites Building a simple task Maternal and Newborn Health Reference App Scenario This scenario is loosely based on the Pregnancy Visit Task from the Maternal and Newborn Health Reference App.Contact Muting in SQL querieshttps://docs.communityhealthtoolkit.org/apps/guides/database/muting_in_dashboards/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/database/muting_in_dashboards/When a contact gets muted, two of many things happen: +The target contact and all of its descendants have a muted property set equal to the date they were muted an entry is added to the contact&rsquo;s muting_history in sentinel&rsquo;s info docs When building dashboards on Superset, Klipfolio, or other data visualization platforms, you might need to exclude these muted contacts from the visualized data. An easy way to do this is to check the contact&rsquo;s muted property which when present has the date value of when the contact was muted and when absent means that the contact is not muted.Contact Tracinghttps://docs.communityhealthtoolkit.org/apps/examples/contact-tracing/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/contact-tracing/The CHT’s Contact Tracing functionality enables effective disease surveillance within communities to help control infectious disease outbreaks. It is a community public health tool that is designed to: +Centrally register patient cases and track contacts to prevent secondary spread of diseases in communities Create a coordinated approach to contact tracing within existing health systems Communicate the importance of self-isolation and symptom screening to exposed individuals and their families Problem Being Addressed An essential part of containing disease outbreaks, such as COVID-19, requires public health organizations to rapidly notify people who have come into contact with confirmed or suspected patient cases.Fixing couch2pg Memory Errorshttps://docs.communityhealthtoolkit.org/apps/guides/database/couch2pg-oom-errors/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/database/couch2pg-oom-errors/Some times when couch2pg is replicating documents to postgres, it encounters very large info docs that are larger than the memory allocation of the document sync array and causes out-of-memory errors. To fix this, we need to delete this document so that couch2pg can proceed. Below are steps to follow to achieve this. +Reduce the size of the replicated docs to a value of say 4 in the couch2pg.conf file so that you can get within the range of the failing document.CouchDB Authenticationhttps://docs.communityhealthtoolkit.org/apps/guides/database/couchdb-authentication/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/database/couchdb-authentication/To invalidate a session in couchdb, there are two options: Change the session signing certificate on the server Change the password and/or salt for the user whose session should be invalidated There are drawbacks to note with each. Option 1 will invalidate all sessions; option 2 will invalidate all sessions for that user, and also their password. +Because of the nature of couch&rsquo;s session management, there is no way to see a list of active/open sessions.Primary Health Care Adaptations for COVID-19https://docs.communityhealthtoolkit.org/apps/examples/phc-covid/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/phc-covid/Primary Health Care (PHC) workflows using the CHT are easily adaptable to help communities and facilities strengthen continuity of routine primary care services during COVID-19. Adapting PHC workflows is designed to: +Address disruptions to PHC delivery within communities while keeping CHWs and patients safe Offer remote patient and CHW support through call and text-first protocols Limit physical contact between patients, CHW’s, and facility providers by modifying existing health assessments Contain transmission via embedded COVID-19 symptom screening and referral protocols Problem Being Addressed Most primary health care programs are not designed to address the specific health needs of pandemics, such as COVID-19.COVID-19 Education and Training for CHWshttps://docs.communityhealthtoolkit.org/apps/examples/covid-education/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/covid-education/The COVID-19 pandemic has created unique challenges to providing in-person Community Health Worker training. To support CHWs, three learning modules were created to rapidly and remotely train them on COVID-19 safety protocols and patient care. Through CHT app and SMS deployments, Supervisors can now train CHWs on care workflows without being physically present. This example covers the following learning modules: +Health safety protocols for preventing the spread of COVID-19 Preventing the dissemination of misinformation about COVID-19 Recognizing COVID-19 and caring for patients with suspected infection Problem Being Addressed It is important for Community Health Workers to stay safe and serve their communities by understanding effective health protocols for preventing the spread of COVID-19.COVID-19 Testing with Rapid Diagnostic Testshttps://docs.communityhealthtoolkit.org/apps/examples/covid-rdt-reference-app/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/covid-rdt-reference-app/Medic has worked with FIND to build a CHT reference application for COVID-19 point-of-care testing with Rapid Diagnostic Tests (RDT). Using the reference app as an example, CHT app developers can easily include the provisioning and capture of RDT in workflows. These workflows can include third-party applications, like Dimagi&rsquo;s RD-Toolkit, that guide health workers through the use of the RDT. +You can find the code for this application in the CHT Core repository on GitHub.Database Conflictshttps://docs.communityhealthtoolkit.org/apps/guides/database/database-conflicts/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/database/database-conflicts/Conflicts are a natural and unavoidable part of working in a distributed system. +Conflicts occur when one client (eg PouchDB) attempts to replicate to another (eg CouchDB), and the document that the first has does not have the same tree of changes that the second one has. +An example To make it clear what&rsquo;s happening, let&rsquo;s walk through an example. If you already understand conflicts feel free to skip this section.DHIS2 Aggregatehttps://docs.communityhealthtoolkit.org/apps/guides/integrations/dhis2-aggregate/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/integrations/dhis2-aggregate/One of the first things you’ll need to do is identify the specific DHIS2 data set that you plan to implement. You’ll need a list of all the data elements on that data set, a detailed understanding of how each is calculated, the frequency in which the data set is submitted (weekly, monthly, etc…), and for which organisation units the data set applies. You’ll also want to identify and engage the appropriate DHIS2 stakeholders to get access to DHIS2 metadata, test environments, and discuss workflows.Direct-to-client, two-way texting workflows on CHThttps://docs.communityhealthtoolkit.org/apps/examples/direct-to-client/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/direct-to-client/This documentation provides a guide for designing and deploying direct-to-client (D2C), two-way texting (2wT) workflows to support client follow-up care using the community health toolkit (CHT). 2wT is a mobile text messaging system built on open-source, CHT tools to engage clients in their health care; to facilitate prompt client - healthcare provider interactions; to provide low-cost telehealth; and, to improve health care outcomes through the early identification of, and referral for, potential complications.Docker Directory Setuphttps://docs.communityhealthtoolkit.org/hosting/4.x/_partial_docker_directories/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/_partial_docker_directories/Create the following directory structure: +|-- /home/ubuntu/cht/ |-- compose/ |-- certs/ |-- couchdb/ |-- upgrade-service/ By calling this mkdir commands: +mkdir -p /home/ubuntu/cht/{compose,certs,upgrade-service,couchdb} compose - docker-compose files for cht-core and CouchDB certs - TLS certificates directory upgrade-service - where docker-compose file for the upgrade-service couchdb - the path for the docker-compose file of the upgrade-service (not used in multi-node)Event Based Surveillancehttps://docs.communityhealthtoolkit.org/apps/examples/ebs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/ebs/The CHT’s Event Based Surveillance (EBS) functionality enables rapid capture of information about community events that are a potential risk to public health. Deployed at the community level, this functionality is designed to: +Achieve earliest possible detection of COVID-19 cases in communities Provide visibility into signals, reports, and investigations centrally for follow-up Coordinate and support local action based on existing roles in disease surveillance Problem Being Addressed Patient reporting through traditional primary health care channels is not well suited for detecting rapidly spreading outbreaks at a community scale.Feature Flagshttps://docs.communityhealthtoolkit.org/apps/guides/updates/feature-flags/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/updates/feature-flags/Some CHT Core features can be enabled for specific users only. This can be particularly helpful for features that require training. The updated or &ldquo;new&rdquo; version will generally be the system default, but users can be configured to see the &ldquo;old&rdquo; version. If you do nothing when you upgrade, users will automatically start seeing the new version. +Note Feature flags are used primarily as a way to phase in updates. The old version should be considered deprecated and will be completely removed in a future release.Fetching forms from Google Drivehttps://docs.communityhealthtoolkit.org/apps/guides/forms/google-drive/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/google-drive/To work collaboratively on form design it can be helpful to keep XLSForms in Google Drive. The fetch-forms-from-google-drive action downloads these XLSForms so that they can be converted to XForms and uploaded to your CHT app. +This action requires the following files in the top-level folder of your CHT app config: forms-on-google-drive.json and .gdrive.secrets.json. +forms-on-google-drive.json This JSON file contains key value pairs, where the key is the relative path and name for the downloaded file, and the value is the file&rsquo;s Google Drive File ID.Input data available in formshttps://docs.communityhealthtoolkit.org/apps/guides/forms/form-inputs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/form-inputs/CHT forms have access to varying amounts of input data depending on the type of form and its source. +contact forms Available data: +initial contact data inputs data for user Contact data via contact selector Initial contact data in contact forms Create forms Forms for adding contacts have access to a small group of fields contained in a top-level group that is named for the contact_type id of the contact being added (so person, clinic, etc).Learning and Carehttps://docs.communityhealthtoolkit.org/apps/examples/learning-care/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/learning-care/The Learning &amp; Care Apps in the Community Health Toolkit are designed to onboard community health workers remotely to new digital training tools, and help them learn new information and care delivery responsibilities through customized educational modules. +The modules can be deployed both (i) via the Android integration with the Academy App (powered by OppiaMobile) described in this documentation, and (ii) within the CHT core alone, to provide a seamless online &amp; offline experience for CHWs, supervisors, and government partners.Local Docker Setuphttps://docs.communityhealthtoolkit.org/apps/tutorials/_partial_docker_setup/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/tutorials/_partial_docker_setup/Linux (Ubuntu) macOS Windows (WSL2) curl -fsSL get.docker.com -o get-docker.sh &amp;&amp; sh get-docker.sh # OPTIONAL: Allow user to run Docker without sudo dockerd-rootless-setuptool.sh install echo &#34;export PATH=/usr/bin:$PATH&#34; &gt;&gt; ~/.$(basename $SHELL)rc echo &#34;export DOCKER_HOST=unix:///run/user/1000/docker.sock&#34; &gt;&gt; ~/.$(basename $SHELL)rc . ~/.$(basename $SHELL)rc Download and install Docker Desktop or Colima. +Download and install Docker Desktop. +Restart your entire machine to finish initializing Docker. +After restarting, verify Docker is running as expected. Run the simple hello-world Docker container.Loss to Follow Uphttps://docs.communityhealthtoolkit.org/apps/examples/interoperability/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/interoperability/Loss to Follow Up Workflow (LTFU) This workflow describes a use case where a health facility or a requesting system generates a list of patients who have missed follow-up appointments that were made through the CHT. A CHW would then follow up with the listed patients through SMS, a physical visit or a phone call. +Problem Being Addressed Data exchange between the CHT and other systems has primarily been at peer-to-peer level.Privacy & Data Protection Policyhttps://docs.communityhealthtoolkit.org/apps/guides/privacy/policy/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/privacy/policy/Note This policy is reviewed and updated periodically by our Responsible Data Working Group and is intended as a resource for the CHT community. If you have any questions, please reach out to our Data Protection Officer at support@medic.org. +Table of Contents Table of Contents Our responsible data promise Compliance with applicable data regulations and policies Data and protected health information Use of software to collect and process data Protected health information policy Security practices Technology security overview Security training for our Partners Inquiries with Medic’s Data Protection Officer Our responsible data promise As a non-profit organization, Medic Mobile (“Medic”)’s mission is to advance good health and human flourishing by building open source technology with and for hard-to-reach communities.Moving Contacts within the Hierarchyhttps://docs.communityhealthtoolkit.org/apps/guides/updates/moving-contacts/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/updates/moving-contacts/Contacts are organized into a hierarchy. It is not straight-forward to move contacts from one position in the hierarchy to another because many copies of this hierarchy exist. Use the move-contacts action in cht-conf to assign a new parent to contacts. This command will move the specified contact, all the contacts under that contact, and all reports created by any of those contacts. This action will download all documents that need to be updated, update the lineages within those documents, and then save the updated documents on your local disk.Including Multimedia in Formshttps://docs.communityhealthtoolkit.org/apps/guides/forms/multimedia/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/multimedia/Multimedia Formats There are many supported formats for video, audio, and images. We recommend using h.264(mpeg) for video, jpeg for images, and mp3 for audio. When creating videos or images keep in mind the dimensions and storage capabilities on phones that may be used. Lower end phones have smaller storage and screen sizes. When rendering images, video, and audio the CHT uses the browser&rsquo;s built in rendering tools. This means you can render any media format that is supported by the minimum version of Chrome.OpenMRShttps://docs.communityhealthtoolkit.org/apps/guides/integrations/openmrs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/integrations/openmrs/OpenMRS is an open source electronic medical record system used in dozens of countries. Integrating CHT apps with OpenMRS can open up opportunities to improve health outcomes for patients by promoting better coordination of care. For example, referrals by CHWs in the community can be sent electronically to health facilities using OpenMRS so that nurses and clinicians can prepare for their visit and have full access to the assessment done by a CHW.OppiaMobilehttps://docs.communityhealthtoolkit.org/apps/guides/integrations/oppia/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/integrations/oppia/The training modules configuration consists of five main components: +App Forms - Content that the user will interact with; Tasks - How forms are presented to the user: how and when the user accesses the forms for input; Targets - Shows the progress of the user; Contact Summary - Gives a highlight of the modules completed by the user; Context - Defines what forms are available to fill from the user’s profile, or available as tasks.Passing data from a task into the app formhttps://docs.communityhealthtoolkit.org/apps/guides/tasks/pass-data-to-form/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/tasks/pass-data-to-form/This guide explains how to pass data from a task into the action application form. +Prerequisites Complex Tasks Tutorial Application Forms Tutorial Scenario Let&rsquo;s look deeper at the scenario from the Complex Tasks Tutorial where we have an ANC follow-up task which recurs eight times, and we want to ask the user different questions on the first and last follow-up. +Developing the task From the Complex Tasks Tutorial, here is the task.Pharmacovigilance Reference apphttps://docs.communityhealthtoolkit.org/apps/examples/pharmacovigilance-reference-app/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/pharmacovigilance-reference-app/Problem Being Addressed Self-medication, unregulated medical products, and counterfeit drugs have led to a significant increase in the prevalence of adverse drug reactions (ADRs), adverse effects following Immunization (AEFI), and the proliferation of poor-quality health products and technologies. ADRs are a common cause of hospital admissions and contribute to patient mortality, placing a substantial economic burden on resource-limited healthcare systems, especially in African countries. +Need for Pharmacovigilance: Pharmacovigilance focuses on the detection, assessment, understanding and prevention of adverse effects and other potential drug-related problems.YendaNafe CHT app by PIH in Malawihttps://docs.communityhealthtoolkit.org/apps/examples/pih/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/pih/Since 2017, Partners in Health (PIH) Malawi and Medic have collaboratively co-designed and developed YendaNafe, a digital health app for community based service provision. In the spirit of openness, Medic and PIH have coordinated the release of the full application source code of Yendanafe app as first of kind ‘‘Integrated CHT Reference app’’. This Reference app provides an example that CHT Implementers can learn how they can design and configure integrated workflows.Preparing to upgrade to CHT 4.0https://docs.communityhealthtoolkit.org/apps/guides/updates/preparing-for-4/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/updates/preparing-for-4/Note This guide applies to both self-hosted and Medic hosted deployments. Introduction Medic uses Semantic Versioning (aka &ldquo;SemVer&rdquo;) which means that the CHT upgrade from the major 3.x version to the 4.x version denotes there are breaking changes. The key to a successful upgrade will be to understand and plan for these breaking changes. Aside from the Docker hosting infrastructure (out of scope for this prep document), the two breaking changes are around CHT Android and Enketo.Replicating Production Data Locallyhttps://docs.communityhealthtoolkit.org/apps/guides/debugging/replicating-production-data-locally/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/debugging/replicating-production-data-locally/Sometimes there will be a production problem that you need to dig into locally to solve. This guide explains how to: +Copy the data from an instance to a local CouchDB database Run a local webapp instance with that data First, a note about data safety Production data is medical data. It&rsquo;s HIV statuses and pregnancies. It&rsquo;s important, and it&rsquo;s not yours. If you&rsquo;re downloading it, do so on an encrypted drive and delete it once you&rsquo;re done with it.Purginghttps://docs.communityhealthtoolkit.org/apps/guides/performance/purging/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/performance/purging/Only available in 3.7.0 and above +Purging is a tool that allows you to increase performance and available disk space for offline users (eg CHWs) by removing unneeded documents from their device. +As users continually generate new reports their performance may naturally degrade as a result. You can use purging to remove older documents that are no longer relevant from their devices. Purging only removes documents from user&rsquo;s devices: these reports are still available for online analytics and impact metrics.Querying Apdex Telemetry Datahttps://docs.communityhealthtoolkit.org/apps/guides/database/querying_apdex_telemetry/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/database/querying_apdex_telemetry/Added in 4.7.0, CHT now records the Apdex (Application Performance Index) that is an open standard for measuring performance of software applications. +Since Apdex is part of the telemetry system, it is possible to view Apdex data directly from CouchDB. However, it is more useful when aggregated across many users, interactions, and/or days. With this in mind, it is typically easier to query the data using SQL from an analytics database.Querying Task Documentshttps://docs.communityhealthtoolkit.org/apps/guides/tasks/query-task-data/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/tasks/query-task-data/This guide explains the data which results from tasks and how to query it. +Write a PostgreSQL query to examine task data Build deeper understanding of task data Present some data considerations of which task authors should remain mindful Prerequisites Data Flows for Analytics Querying task data The task system running on each user&rsquo;s device is powered by task documents and those task documents sync to the server and to PostgreSQL just like a contact or a report.Tracking Wealth Quintileshttps://docs.communityhealthtoolkit.org/apps/guides/forms/wealth-quintiles/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/wealth-quintiles/Introduced in v2.16.0 +Household surveys with questions about the home, possessions, and access to safe drinking water have been used to create equity score and improve the targeting of health services. This guide will cover how quintile information from a household survey can be used in customizing care for individual household members. For example, the equity quintile can be used to increase the number of pregnancy follow-ups for women in specific households, or to display specific notes or questions within patient forms.RapidProhttps://docs.communityhealthtoolkit.org/apps/guides/integrations/rapidpro/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/integrations/rapidpro/RapidPro is the open-source platform that powers TextIt, developed by UNICEF and Nyaruka. RapidPro allows you to visually build messaging workflows for mobile-based services. Review RapidPro’s documentation to familiarize yourself with various components that include the API. Before you embark on designing an integrated RapidPro/CHT workflow, you should start by understanding the needs of your users, identifying a problem to solve, and establishing goals. While an integrated RapidPro/CHT workflow can open up many powerful and personalized messaging capabilities, introducing an additional technology solution does come with complexities and cost.Connecting to RDBMS from MacOShttps://docs.communityhealthtoolkit.org/apps/guides/database/rdbms-from-mac/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/database/rdbms-from-mac/Follow these steps on a Mac to generate your public/private keys and access the PostgreSQL server. +Access Terminal Terminal (Terminal.app) is the terminal emulator included in the macOS operating system. You can use this application to generate your SSH key. +Open a new Finder window Navigate to the Applications folder Navigate to the Utilities folder Open the Terminal app Generate Key From Terminal, follow these instructions (see screenshot below): +Type: ssh-keygen -t rsa Hit return to use the default file / location Enter a passphrase Enter your passphrase again Type: cat ~/.Connecting to RDBMS from Windowshttps://docs.communityhealthtoolkit.org/apps/guides/database/rdbms-from-windows/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/database/rdbms-from-windows/Connecting to RDBMS, the PostgreSQL server, is pretty stratightforward in nix systems. In Windows there are a couple of things you need to do to get it up and running. +SSH Key Generation and Importing Download Puttygen from here +Run Puttygen Go to Windows Start menu → All Programs → PuTTY→ PuTTYgen. +Create a new key pair for your computer. +Convert the key generated from ssh2 format to openssh. Puttygen supports this.Update Collect Forms Remotelyhttps://docs.communityhealthtoolkit.org/apps/guides/updates/collect-forms-update/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/updates/collect-forms-update/To do over the air Medic Collect form updates via HTTP rather than sending APKs which have a long manual install process, follow the steps below: +Have your xls forms ready in the folder. They should use underscore as name separators. e.g form_name.xlsx They should have form_id and name properties in the settings Upload the forms to the instance using cht-conf Using the upload-collect-forms action as shown below. cht --instance=user:pass@instancename.app.medicmobile.org upload-collect-forms Go to the Collect App.Remote Onboarding and Traininghttps://docs.communityhealthtoolkit.org/apps/examples/training/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/training/The CHT’s Remote Onboarding and Training functionality enables Supervisors and Administrators to train CHWs on care workflows and related app use without being physically present. It is designed for: +Safety: maintaining distance due to infectious disease Speed: faster deployment when timing is a critical Scalability: onboarding large numbers of users at the same time Measurability: evaluation to provide added support where needed Adaptability: integration with existing program and workflow structures Problem Being Addressed Providing consistent training for CHWs is critically important in the context of evolving health programs and use of digital support tools.CouchDB replicationhttps://docs.communityhealthtoolkit.org/apps/guides/performance/replication/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/performance/replication/Replication is what we call it when users download a copy of the data on to their device. +Restricting replication If the user has an online role they can access all the data, otherwise they will get restricted access to the data. +Restriction by place The most common restriction is by place. This is where we check the user&rsquo;s facility_id property, and allow access to all contacts that are descendants of that place, and all reports and messages that are about one of those descendants.Customizing Titles in the Reports Listhttps://docs.communityhealthtoolkit.org/apps/guides/forms/report-titles/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/report-titles/Added in 3.9.0 +By default the CHT shows the name of the subject of the report in the reports list. This can be overridden by configuring the subject_key property with a translation key in the form document. +The translation uses a summary of the report as the evaluation context so you can include report fields in your value, for example: Case registration {{case_id}}. Useful properties available in the summary include: from (the phone number of the sender), phone (the phone number of the report contact), form (the form code), subject.Securely onboarding users at scalehttps://docs.communityhealthtoolkit.org/apps/guides/security/securely-onboarding-users-at-scale/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/security/securely-onboarding-users-at-scale/This document shows how to achieve a high level of credential management security for a CHT deployment. Implementers need to know when ease of use is more important than a more secure system. By reading this document you should be able to know when to make the &ldquo;more secure&rdquo; vs &ldquo;easier to use&rdquo; trade off. +No system is perfectly secure - be prepared to remediate a security breach! +When a CHT deployment will support hundreds of users or more, secure credential management becomes critical.Securely Sharing Your Development Environmenthttps://docs.communityhealthtoolkit.org/apps/guides/debugging/secure-sharing-of-developer-instance/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/debugging/secure-sharing-of-developer-instance/Warning Be extra careful with this process! The end result will be that your development instance will be accessible to the internet. If you have simple logins and passwords like &ldquo;admin/test.223&rdquo; because you thought it was just your local dev instance and it doesn&rsquo;t matter, now it matters! Whenever you&rsquo;re not using the SSH tunnel for testing, shut it down so there&rsquo;s no more remote access. +Never expose a development instance to the internet where you&rsquo;ve replicated production data locally.Securing Android Deviceshttps://docs.communityhealthtoolkit.org/apps/guides/security/securing-android/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/security/securing-android/To secure an android device you should enable at least a pin code lock on the device, enable FDE (full disc encryption) and setup remote wiping capabilities by enabling mobile device management. +PIN Setup and FDE Instructions are slightly different per device. Enabling FDE has the added benefit that you must also lock the device with a pin code or password. +Android 5.0 or later Open the Security menu under settings.Sharing 4.x Logshttps://docs.communityhealthtoolkit.org/apps/guides/debugging/sharing-4x-logs/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/debugging/sharing-4x-logs/CHT 4.x moves from a monolithic container MedicOS to discrete containers, each service hosting one service of the CHT. When troubleshooting an issue with your CHT instance, it can be hard to list each container, see it&rsquo;s status, gather up logs for each container and then share all this information with Medic or other support staff. To ease this pain, a script was written which automates the process. +Prerequisites This assumes you&rsquo;re running CHT 4.Stock Monitoringhttps://docs.communityhealthtoolkit.org/apps/examples/stock-monitoring/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/stock-monitoring/Problem Being Addressed Paper based commodity management systems are prone to errors due to reliance on manually updated registers, this greatly affects data quality. Further, it is time consuming for CHWs to reference stock balances and at the same time update stocks on the commodity management sheet while providing treatment to household members. In terms of supervision, CHW’s supervisors do not know when CHWs have stock outs in time for replenishing and ordering purposes.Reference Application for CHW Supervision and Performance Managementhttps://docs.communityhealthtoolkit.org/apps/examples/supervisor-reference-app/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/examples/supervisor-reference-app/Medic has worked with D-tree International to build a CHT supervisor reference app. The app supports community health worker (CHW) supervisors to continually monitor and improve the program quality for Zanzibar National community health program Jamii ni Afya. The supervisor reference application is designed to enable supervisors to access CHW performance information and any other information required to supervise, mentor, and support CHWs to provide quality community health services. This reference app provides an example that CHT app developers can easily customize to meet the needs for their specific program areas to support CHW program management.Understanding the parameters in the Task Schemahttps://docs.communityhealthtoolkit.org/apps/guides/tasks/task-schema-parameters/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/tasks/task-schema-parameters/This guide explains the parameters available in the Task Schema and important constraints governing the design of tasks. +Useful knowledge if you are stuck writing your first appliesIf predicate Understanding the data which is available in the task system and important constraints Understand the special significance of the appliesTo attribute Let&rsquo;s synthesize some knowledge about CHT applications to help clarify what is happening within the task system: +All contacts in CHT applications are organised into hierarchies.User telemetryhttps://docs.communityhealthtoolkit.org/apps/guides/performance/telemetry/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/performance/telemetry/Introduced in v3.4.0 +The app collects performance data on certain user actions which is then aggregated each day and replicated to the server. This can be used to evaluate the performance of the code and configuration and to evaluate where improvements can be made. +The aggregate doc for the previous day is created when the first telemetry item is recorded each day. This is stored in the medic-user-&lt;username&gt;-meta database on the device and replicated to the server when an internet connection is available.Upgrading the cht-upgrade-servicehttps://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/_partial_upgrade_service/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/_partial_upgrade_service/Upgrading the cht-upgrade-service The CHT Upgrade Service provides an interface between the CHT Core API and Docker to allow easy startup and one-click upgrades from the CHT Admin UI. Occasionally, the CHT Upgrade Service, itself, will need to be upgraded. If an upgrade is available, it is highly recommended that you install the upgrade for the CHT Upgrade Service before performing further upgrades on your CHT instance. This is done via the following steps:Utils Functionshttps://docs.communityhealthtoolkit.org/apps/reference/_partial_utils/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/reference/_partial_utils/Utility functions in the Core Framework can make common tasks much easier. These are available only for Tasks and Targets. To use the function call Utils.&lt;function-name&gt;(&lt;params&gt;), for example Utils.addDate(report.reported_date, 10). +Name Description isTimely(date, event) Returns true if the given date is after the start date and before the end date of the event. addDate(date, days) Returns a new Date set to midnight the given number of days after the given date.Versioning formshttps://docs.communityhealthtoolkit.org/apps/guides/forms/versioning/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/apps/guides/forms/versioning/Added in cht-core 3.15.0 and cht-conf 3.10.0 +When uploading app or contact xforms, cht-conf 3.10.0+ will automatically generate a version and include it in the form doc&rsquo;s xmlVersion property. The version has two properties. +Property Description time The time that the form was uploaded to the server in millis since the epoch. sha256 A hash of the xform content. For example: +&#34;xmlVersion&#34;: { &#34;time&#34;: 1658717177750, &#34;sha256&#34;: &#34;6f0bbfe5a9a9ebeb25784165879afec5e311b197cbd76ade5698c83c22dd9a8f&#34; } When a user fills in a form with an xmlVersion property, the version is copied in to the report doc as the form_version property. \ No newline at end of file diff --git a/js/click-to-copy.min.73478a7d4807698aed7e355eb23f9890ca18fea3158604c8471746d046702bad.js b/js/click-to-copy.min.73478a7d4807698aed7e355eb23f9890ca18fea3158604c8471746d046702bad.js new file mode 100644 index 0000000000..0b95e45e69 --- /dev/null +++ b/js/click-to-copy.min.73478a7d4807698aed7e355eb23f9890ca18fea3158604c8471746d046702bad.js @@ -0,0 +1,2 @@ +let codeListings=document.querySelectorAll(".highlight > pre");for(let t=0;t{e.setAttribute(t,o[t])}),e.classList.add("fas","fa-copy","btn","btn-sm","td-click-to-copy");const i=new bootstrap.Tooltip(e);e.onclick=()=>{copyCode(s),e.setAttribute("data-bs-original-title","Copied!"),i.show()},e.onmouseout=()=>{e.setAttribute("data-bs-original-title","Copy to clipboard"),i.hide()};const n=document.createElement("div");n.classList.add("click-to-copy"),n.append(e),codeListings[t].insertBefore(n,s)}const copyCode=e=>{navigator.clipboard.writeText(e.textContent.trim()+` +`)} \ No newline at end of file diff --git a/js/click-to-copy.min.f724d3de49218995223b7316aa2e53e2b34bf42026bf399ebb21bb02212402d1.js b/js/click-to-copy.min.f724d3de49218995223b7316aa2e53e2b34bf42026bf399ebb21bb02212402d1.js deleted file mode 100644 index 26dffe3a24..0000000000 --- a/js/click-to-copy.min.f724d3de49218995223b7316aa2e53e2b34bf42026bf399ebb21bb02212402d1.js +++ /dev/null @@ -1,2 +0,0 @@ -let codeListings=document.querySelectorAll(".highlight > pre");for(let t=0;t{e.setAttribute(t,o[t])}),e.classList.add("fas","fa-copy","btn","btn-dark","btn-sm","td-click-to-copy");const i=new bootstrap.Tooltip(e);e.onclick=()=>{copyCode(s),e.setAttribute("data-bs-original-title","Copied!"),i.show()},e.onmouseout=()=>{e.setAttribute("data-bs-original-title","Copy to clipboard"),i.hide()};const n=document.createElement("div");n.classList.add("click-to-copy"),n.append(e),codeListings[t].insertBefore(n,s)}const copyCode=e=>{navigator.clipboard.writeText(e.textContent.trim()+` -`)} \ No newline at end of file diff --git a/js/main.min.1d460700c89cb719991adb15f3c49635425c372f375f09fb5bd85da5639db5da.js b/js/main.min.1d460700c89cb719991adb15f3c49635425c372f375f09fb5bd85da5639db5da.js deleted file mode 100644 index cbfdad0814..0000000000 --- a/js/main.min.1d460700c89cb719991adb15f3c49635425c372f375f09fb5bd85da5639db5da.js +++ /dev/null @@ -1,5 +0,0 @@ -/*! - * Bootstrap v5.2.3 (https://getbootstrap.com/) - * Copyright 2011-2022 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */(function(e,t){typeof exports=="object"&&typeof module!="undefined"?module.exports=t():typeof define=="function"&&define.amd?define(t):(e=typeof globalThis!="undefined"?globalThis:e||self,e.bootstrap=t())})(this,function(){"use strict";const ro=1e6,Jr=1e3,ut="transitionend",Xr=e=>e==null?`${e}`:Object.prototype.toString.call(e).match(/\s([a-z]+)/i)[1].toLowerCase(),Gr=e=>{do e+=Math.floor(Math.random()*ro);while(document.getElementById(e))return e},ss=e=>{let t=e.getAttribute("data-bs-target");if(!t||t==="#"){let n=e.getAttribute("href");if(!n||!n.includes("#")&&!n.startsWith("."))return null;n.includes("#")&&!n.startsWith("#")&&(n=`#${n.split("#")[1]}`),t=n&&n!=="#"?n.trim():null}return t},ns=e=>{const t=ss(e);return t?document.querySelector(t)?t:null:null},v=e=>{const t=ss(e);return t?document.querySelector(t):null},Yr=e=>{if(!e)return 0;let{transitionDuration:t,transitionDelay:n}=window.getComputedStyle(e);const s=Number.parseFloat(t),o=Number.parseFloat(n);return!s&&!o?0:(t=t.split(",")[0],n=n.split(",")[0],(Number.parseFloat(t)+Number.parseFloat(n))*Jr)},es=e=>{e.dispatchEvent(new Event(ut))},g=e=>!!e&&typeof e=="object"&&(typeof e.jquery!="undefined"&&(e=e[0]),typeof e.nodeType!="undefined"),E=e=>g(e)?e.jquery?e[0]:e:typeof e=="string"&&e.length>0?document.querySelector(e):null,H=e=>{if(!g(e)||e.getClientRects().length===0)return!1;const n=getComputedStyle(e).getPropertyValue("visibility")==="visible",t=e.closest("details:not([open])");if(!t)return n;if(t!==e){const n=e.closest("summary");if(n&&n.parentNode!==t)return!1;if(n===null)return!1}return n},w=e=>!e||e.nodeType!==Node.ELEMENT_NODE||!!e.classList.contains("disabled")||(typeof e.disabled!="undefined"?e.disabled:e.hasAttribute("disabled")&&e.getAttribute("disabled")!=="false"),Jn=e=>{if(!document.documentElement.attachShadow)return null;if(typeof e.getRootNode=="function"){const t=e.getRootNode();return t instanceof ShadowRoot?t:null}return e instanceof ShadowRoot?e:e.parentNode?Jn(e.parentNode):null},Oe=()=>{},ne=e=>{e.offsetHeight},Xn=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,He=[],Pr=e=>{document.readyState==="loading"?(He.length||document.addEventListener("DOMContentLoaded",()=>{for(const e of He)e()}),He.push(e)):e()},a=()=>document.documentElement.dir==="rtl",c=e=>{Pr(()=>{const t=Xn();if(t){const n=e.NAME,s=t.fn[n];t.fn[n]=e.jQueryInterface,t.fn[n].Constructor=e,t.fn[n].noConflict=()=>(t.fn[n]=s,e.jQueryInterface)}})},y=e=>{typeof e=="function"&&e()},Yn=(e,t,n=!0)=>{if(!n){y(e);return}const i=5,a=Yr(t)+i;let s=!1;const o=({target:n})=>{if(n!==t)return;s=!0,t.removeEventListener(ut,o),y(e)};t.addEventListener(ut,o),setTimeout(()=>{s||es(t)},a)},Le=(e,t,n,s)=>{const i=e.length;let o=e.indexOf(t);return o===-1?!n&&s?e[i-1]:e[0]:(o+=n?1:-1,s&&(o=(o+i)%i),e[Math.max(0,Math.min(o,i-1))])},Nr=/[^.]*(?=\..*)\.|.*/,zr=/\..*/,Tr=/::\d+$/,Ge={};let Un=1;const Vn={mouseenter:"mouseover",mouseleave:"mouseout"},Ar=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function In(e,t){return t&&`${t}::${Un++}`||e.uidEvent||Un++}function Fn(e){const t=In(e);return e.uidEvent=t,Ge[t]=Ge[t]||{},Ge[t]}function xr(t,n){return function s(o){return st(o,{delegateTarget:t}),s.oneOff&&e.off(t,o.type,n),n.apply(t,[o])}}function Or(t,n,s){return function o(i){const a=t.querySelectorAll(n);for(let{target:r}=i;r&&r!==this;r=r.parentNode)for(const c of a){if(c!==r)continue;return st(i,{delegateTarget:r}),o.oneOff&&e.off(t,i.type,n,s),s.apply(r,[i])}}}function Sn(e,t,n=null){return Object.values(e).find(e=>e.callable===t&&e.delegationSelector===n)}function En(e,t,n){const o=typeof t=="string",i=o?n:t||n;let s=On(e);return Ar.has(s)||(s=e),[o,i,s]}function Cn(e,t,n,s,o){if(typeof t!="string"||!e)return;let[r,i,c]=En(t,n,s);if(t in Vn){const e=e=>function(t){if(!t.relatedTarget||t.relatedTarget!==t.delegateTarget&&!t.delegateTarget.contains(t.relatedTarget))return e.call(this,t)};i=e(i)}const d=Fn(e),u=d[c]||(d[c]={}),l=Sn(u,i,r?n:null);if(l){l.oneOff=l.oneOff&&o;return}const h=In(i,t.replace(Nr,"")),a=r?Or(e,n,i):xr(e,i);a.delegationSelector=r?n:null,a.callable=i,a.oneOff=o,a.uidEvent=h,u[h]=a,e.addEventListener(c,a,r)}function Me(e,t,n,s,o){const i=Sn(t[n],s,o);if(!i)return;e.removeEventListener(n,i,Boolean(o)),delete t[n][i.uidEvent]}function wr(e,t,n,s){const o=t[n]||{};for(const i of Object.keys(o))if(i.includes(s)){const s=o[i];Me(e,t,n,s.callable,s.delegationSelector)}}function On(e){return e=e.replace(zr,""),Vn[e]||e}const e={on(e,t,n,s){Cn(e,t,n,s,!1)},one(e,t,n,s){Cn(e,t,n,s,!0)},off(e,t,n,s){if(typeof t!="string"||!e)return;const[c,r,i]=En(t,n,s),l=i!==t,o=Fn(e),a=o[i]||{},d=t.startsWith(".");if(typeof r!="undefined"){if(!Object.keys(a).length)return;Me(e,o,i,r,c?n:null);return}if(d)for(const n of Object.keys(o))wr(e,o,n,t.slice(1));for(const n of Object.keys(a)){const s=n.replace(Tr,"");if(!l||t.includes(s)){const t=a[n];Me(e,o,i,t.callable,t.delegationSelector)}}},trigger(e,t,n){if(typeof t!="string"||!e)return null;const i=Xn(),l=On(t),d=t!==l;let s=null,a=!0,r=!0,c=!1;d&&i&&(s=i.Event(t,n),i(e).trigger(s),a=!s.isPropagationStopped(),r=!s.isImmediatePropagationStopped(),c=s.isDefaultPrevented());let o=new Event(t,{bubbles:a,cancelable:!0});return o=st(o,n),c&&o.preventDefault(),r&&e.dispatchEvent(o),o.defaultPrevented&&s&&s.preventDefault(),o}};function st(e,t){for(const[n,s]of Object.entries(t||{}))try{e[n]=s}catch{Object.defineProperty(e,n,{configurable:!0,get(){return s}})}return e}const x=new Map,it={set(e,t,n){x.has(e)||x.set(e,new Map);const s=x.get(e);if(!s.has(t)&&s.size!==0){console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(s.keys())[0]}.`);return}s.set(t,n)},get(e,t){return x.has(e)?x.get(e).get(t)||null:null},remove(e,t){if(!x.has(e))return;const n=x.get(e);n.delete(t),n.size===0&&x.delete(e)}};function _n(e){if(e==="true")return!0;if(e==="false")return!1;if(e===Number(e).toString())return Number(e);if(e===""||e==="null")return null;if(typeof e!="string")return e;try{return JSON.parse(decodeURIComponent(e))}catch{return e}}function ht(e){return e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`)}const b={setDataAttribute(e,t,n){e.setAttribute(`data-bs-${ht(t)}`,n)},removeDataAttribute(e,t){e.removeAttribute(`data-bs-${ht(t)}`)},getDataAttributes(e){if(!e)return{};const t={},n=Object.keys(e.dataset).filter(e=>e.startsWith("bs")&&!e.startsWith("bsConfig"));for(const o of n){let s=o.replace(/^bs/,"");s=s.charAt(0).toLowerCase()+s.slice(1,s.length),t[s]=_n(e.dataset[o])}return t},getDataAttribute(e,t){return _n(e.getAttribute(`data-bs-${ht(t)}`))}};class te{static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}_getConfig(e){return e=this._mergeConfigObj(e),e=this._configAfterMerge(e),this._typeCheckConfig(e),e}_configAfterMerge(e){return e}_mergeConfigObj(e,t){const n=g(t)?b.getDataAttribute(t,"config"):{};return{...this.constructor.Default,...typeof n=="object"?n:{},...g(t)?b.getDataAttributes(t):{},...typeof e=="object"?e:{}}}_typeCheckConfig(e,t=this.constructor.DefaultType){for(const n of Object.keys(t)){const s=t[n],o=e[n],i=g(o)?"element":Xr(o);if(!new RegExp(s).test(i))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${n}" provided type "${i}" but expected type "${s}".`)}}}const _r="5.2.3";class u extends te{constructor(e,t){if(super(),e=E(e),!e)return;this._element=e,this._config=this._getConfig(t),it.set(this._element,this.constructor.DATA_KEY,this)}dispose(){it.remove(this._element,this.constructor.DATA_KEY),e.off(this._element,this.constructor.EVENT_KEY);for(const e of Object.getOwnPropertyNames(this))this[e]=null}_queueCallback(e,t,n=!0){Yn(e,t,n)}_getConfig(e){return e=this._mergeConfigObj(e,this._element),e=this._configAfterMerge(e),this._typeCheckConfig(e),e}static getInstance(e){return it.get(E(e),this.DATA_KEY)}static getOrCreateInstance(e,t={}){return this.getInstance(e)||new this(e,typeof t=="object"?t:null)}static get VERSION(){return _r}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}static eventName(e){return`${e}${this.EVENT_KEY}`}}const Se=(t,n="hide")=>{const o=`click.dismiss${t.EVENT_KEY}`,s=t.NAME;e.on(document,o,`[data-bs-dismiss="${s}"]`,function(e){if(["A","AREA"].includes(this.tagName)&&e.preventDefault(),w(this))return;const o=v(this)||this.closest(`.${s}`),i=t.getOrCreateInstance(o);i[n]()})},jr="alert",dr="bs.alert",vn=`.${dr}`,rr=`close${vn}`,Ja=`closed${vn}`,Qa="fade",Ga="show";class de extends u{static get NAME(){return jr}close(){const t=e.trigger(this._element,rr);if(t.defaultPrevented)return;this._element.classList.remove(Ga);const n=this._element.classList.contains(Qa);this._queueCallback(()=>this._destroyElement(),this._element,n)}_destroyElement(){this._element.remove(),e.trigger(this._element,Ja),this.dispose()}static jQueryInterface(e){return this.each(function(){const t=de.getOrCreateInstance(this);if(typeof e!="string")return;if(t[e]===void 0||e.startsWith("_")||e==="constructor")throw new TypeError(`No method named "${e}"`);t[e](this)})}}Se(de,"close"),c(de);const Ka="button",$a="bs.button",Ia=`.${$a}`,Da=".data-api",ba="active",fn='[data-bs-toggle="button"]',va=`click${Ia}${Da}`;class fe extends u{static get NAME(){return Ka}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle(ba))}static jQueryInterface(e){return this.each(function(){const t=fe.getOrCreateInstance(this);e==="toggle"&&t[e]()})}}e.on(document,va,fn,e=>{e.preventDefault();const t=e.target.closest(fn),n=fe.getOrCreateInstance(t);n.toggle()}),c(fe);const t={find(e,t=document.documentElement){return[].concat(...Element.prototype.querySelectorAll.call(t,e))},findOne(e,t=document.documentElement){return Element.prototype.querySelector.call(t,e)},children(e,t){return[].concat(...e.children).filter(e=>e.matches(t))},parents(e,t){const s=[];let n=e.parentNode.closest(t);for(;n;)s.push(n),n=n.parentNode.closest(t);return s},prev(e,t){let n=e.previousElementSibling;for(;n;){if(n.matches(t))return[n];n=n.previousElementSibling}return[]},next(e,t){let n=e.nextElementSibling;for(;n;){if(n.matches(t))return[n];n=n.nextElementSibling}return[]},focusableChildren(e){const t=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map(e=>`${e}:not([tabindex^="-"])`).join(",");return this.find(t,e).filter(e=>!w(e)&&H(e))}},ga="swipe",$=".bs.swipe",pa=`touchstart${$}`,ma=`touchmove${$}`,ua=`touchend${$}`,na=`pointerdown${$}`,Xi=`pointerup${$}`,$i="touch",Vi="pen",Pi="pointer-event",Li=40,Ni={endCallback:null,leftCallback:null,rightCallback:null},Di={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"};class Re extends te{constructor(e,t){if(super(),this._element=e,!e||!Re.isSupported())return;this._config=this._getConfig(t),this._deltaX=0,this._supportPointerEvents=Boolean(window.PointerEvent),this._initEvents()}static get Default(){return Ni}static get DefaultType(){return Di}static get NAME(){return ga}dispose(){e.off(this._element,$)}_start(e){if(!this._supportPointerEvents){this._deltaX=e.touches[0].clientX;return}this._eventIsPointerPenTouch(e)&&(this._deltaX=e.clientX)}_end(e){this._eventIsPointerPenTouch(e)&&(this._deltaX=e.clientX-this._deltaX),this._handleSwipe(),y(this._config.endCallback)}_move(e){this._deltaX=e.touches&&e.touches.length>1?0:e.touches[0].clientX-this._deltaX}_handleSwipe(){const e=Math.abs(this._deltaX);if(e<=Li)return;const t=e/this._deltaX;if(this._deltaX=0,!t)return;y(t>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?(e.on(this._element,na,e=>this._start(e)),e.on(this._element,Xi,e=>this._end(e)),this._element.classList.add(Pi)):(e.on(this._element,pa,e=>this._start(e)),e.on(this._element,ma,e=>this._move(e)),e.on(this._element,ua,e=>this._end(e)))}_eventIsPointerPenTouch(e){return this._supportPointerEvents&&(e.pointerType===Vi||e.pointerType===$i)}static isSupported(){return"ontouchstart"in document.documentElement||navigator.maxTouchPoints>0}}const is="carousel",Mi="bs.carousel",k=`.${Mi}`,Wt=".data-api",ki="ArrowLeft",yi="ArrowRight",ji=500,Z="next",K="prev",P="left",Ae="right",vi=`slide${k}`,Ve=`slid${k}`,di=`keydown${k}`,li=`mouseenter${k}`,oi=`mouseleave${k}`,ti=`dragstart${k}`,Zo=`load${k}${Wt}`,qo=`click${k}${Wt}`,Lt="carousel",ye="active",Wo="slide",Bo="carousel-item-end",Io="carousel-item-start",Po="carousel-item-next",Ro="carousel-item-prev",Mt=".active",Ct=".carousel-item",Lo=Mt+Ct,No=".carousel-item img",Do=".carousel-indicators",zo="[data-bs-slide], [data-bs-slide-to]",To='[data-bs-ride="carousel"]',bo={[ki]:Ae,[yi]:P},go={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},lo={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"};class ie extends u{constructor(e,n){super(e,n),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=t.findOne(Do,this._element),this._addEventListeners(),this._config.ride===Lt&&this.cycle()}static get Default(){return go}static get DefaultType(){return lo}static get NAME(){return is}next(){this._slide(Z)}nextWhenVisible(){!document.hidden&&H(this._element)&&this.next()}prev(){this._slide(K)}pause(){this._isSliding&&es(this._element),this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval(()=>this.nextWhenVisible(),this._config.interval)}_maybeEnableCycle(){if(!this._config.ride)return;if(this._isSliding){e.one(this._element,Ve,()=>this.cycle());return}this.cycle()}to(t){const n=this._getItems();if(t>n.length-1||t<0)return;if(this._isSliding){e.one(this._element,Ve,()=>this.to(t));return}const s=this._getItemIndex(this._getActive());if(s===t)return;const o=t>s?Z:K;this._slide(o,n[t])}dispose(){this._swipeHelper&&this._swipeHelper.dispose(),super.dispose()}_configAfterMerge(e){return e.defaultInterval=e.interval,e}_addEventListeners(){this._config.keyboard&&e.on(this._element,di,e=>this._keydown(e)),this._config.pause==="hover"&&(e.on(this._element,li,()=>this.pause()),e.on(this._element,oi,()=>this._maybeEnableCycle())),this._config.touch&&Re.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(const n of t.find(No,this._element))e.on(n,ti,e=>e.preventDefault());const n=()=>{if(this._config.pause!=="hover")return;this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout(()=>this._maybeEnableCycle(),ji+this._config.interval)},s={leftCallback:()=>this._slide(this._directionToOrder(P)),rightCallback:()=>this._slide(this._directionToOrder(Ae)),endCallback:n};this._swipeHelper=new Re(this._element,s)}_keydown(e){if(/input|textarea/i.test(e.target.tagName))return;const t=bo[e.key];t&&(e.preventDefault(),this._slide(this._directionToOrder(t)))}_getItemIndex(e){return this._getItems().indexOf(e)}_setActiveIndicatorElement(e){if(!this._indicatorsElement)return;const s=t.findOne(Mt,this._indicatorsElement);s.classList.remove(ye),s.removeAttribute("aria-current");const n=t.findOne(`[data-bs-slide-to="${e}"]`,this._indicatorsElement);n&&(n.classList.add(ye),n.setAttribute("aria-current","true"))}_updateInterval(){const e=this._activeElement||this._getActive();if(!e)return;const t=Number.parseInt(e.getAttribute("data-bs-interval"),10);this._config.interval=t||this._config.defaultInterval}_slide(t,n=null){if(this._isSliding)return;const o=this._getActive(),a=t===Z,s=n||Le(this._getItems(),o,a,this._config.wrap);if(s===o)return;const c=this._getItemIndex(s),l=n=>e.trigger(this._element,n,{relatedTarget:s,direction:this._orderToDirection(t),from:this._getItemIndex(o),to:c}),d=l(vi);if(d.defaultPrevented)return;if(!o||!s)return;const u=Boolean(this._interval);this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(c),this._activeElement=s;const i=a?Io:Bo,r=a?Po:Ro;s.classList.add(r),ne(s),o.classList.add(i),s.classList.add(i);const h=()=>{s.classList.remove(i,r),s.classList.add(ye),o.classList.remove(ye,r,i),this._isSliding=!1,l(Ve)};this._queueCallback(h,o,this._isAnimated()),u&&this.cycle()}_isAnimated(){return this._element.classList.contains(Wo)}_getActive(){return t.findOne(Lo,this._element)}_getItems(){return t.find(Ct,this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(e){return a()?e===P?K:Z:e===P?Z:K}_orderToDirection(e){return a()?e===K?P:Ae:e===K?Ae:P}static jQueryInterface(e){return this.each(function(){const t=ie.getOrCreateInstance(this,e);if(typeof e=="number"){t.to(e);return}if(typeof e=="string"){if(t[e]===void 0||e.startsWith("_")||e==="constructor")throw new TypeError(`No method named "${e}"`);t[e]()}})}}e.on(document,qo,zo,function(e){const n=v(this);if(!n||!n.classList.contains(Lt))return;e.preventDefault();const t=ie.getOrCreateInstance(n),s=this.getAttribute("data-bs-slide-to");if(s){t.to(s),t._maybeEnableCycle();return}if(b.getDataAttribute(this,"slide")==="next"){t.next(),t._maybeEnableCycle();return}t.prev(),t._maybeEnableCycle()}),e.on(window,Zo,()=>{const e=t.find(To);for(const t of e)ie.getOrCreateInstance(t)}),c(ie);const os="collapse",ao="bs.collapse",ee=`.${ao}`,eo=".data-api",Zs=`show${ee}`,Xs=`shown${ee}`,Gs=`hide${ee}`,qs=`hidden${ee}`,Ks=`click${ee}${eo}`,rt="show",V="collapse",je="collapsing",Us="collapsed",Ws=`:scope .${V} .${V}`,$s="collapse-horizontal",Vs="width",Bs="height",Is=".collapse.show, .collapse.collapsing",et='[data-bs-toggle="collapse"]',Hs={parent:null,toggle:!0},Ps={parent:"(null|element)",toggle:"boolean"};class le extends u{constructor(e,n){super(e,n),this._isTransitioning=!1,this._triggerArray=[];const s=t.find(et);for(const e of s){const n=ns(e),o=t.find(n).filter(e=>e===this._element);n!==null&&o.length&&this._triggerArray.push(e)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return Hs}static get DefaultType(){return Ps}static get NAME(){return os}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let n=[];if(this._config.parent&&(n=this._getFirstLevelChildren(Is).filter(e=>e!==this._element).map(e=>le.getOrCreateInstance(e,{toggle:!1}))),n.length&&n[0]._isTransitioning)return;const s=e.trigger(this._element,Zs);if(s.defaultPrevented)return;for(const e of n)e.hide();const t=this._getDimension();this._element.classList.remove(V),this._element.classList.add(je),this._element.style[t]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const o=()=>{this._isTransitioning=!1,this._element.classList.remove(je),this._element.classList.add(V,rt),this._element.style[t]="",e.trigger(this._element,Xs)},i=t[0].toUpperCase()+t.slice(1),a=`scroll${i}`;this._queueCallback(o,this._element,!0),this._element.style[t]=`${this._element[a]}px`}hide(){if(this._isTransitioning||!this._isShown())return;const n=e.trigger(this._element,Gs);if(n.defaultPrevented)return;const t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,ne(this._element),this._element.classList.add(je),this._element.classList.remove(V,rt);for(const e of this._triggerArray){const t=v(e);t&&!this._isShown(t)&&this._addAriaAndCollapsedClass([e],!1)}this._isTransitioning=!0;const s=()=>{this._isTransitioning=!1,this._element.classList.remove(je),this._element.classList.add(V),e.trigger(this._element,qs)};this._element.style[t]="",this._queueCallback(s,this._element,!0)}_isShown(e=this._element){return e.classList.contains(rt)}_configAfterMerge(e){return e.toggle=Boolean(e.toggle),e.parent=E(e.parent),e}_getDimension(){return this._element.classList.contains($s)?Vs:Bs}_initializeChildren(){if(!this._config.parent)return;const e=this._getFirstLevelChildren(et);for(const t of e){const n=v(t);n&&this._addAriaAndCollapsedClass([t],this._isShown(n))}}_getFirstLevelChildren(e){const n=t.find(Ws,this._config.parent);return t.find(e,this._config.parent).filter(e=>!n.includes(e))}_addAriaAndCollapsedClass(e,t){if(!e.length)return;for(const n of e)n.classList.toggle(Us,!t),n.setAttribute("aria-expanded",t)}static jQueryInterface(e){const t={};return typeof e=="string"&&/show|hide/.test(e)&&(t.toggle=!1),this.each(function(){const n=le.getOrCreateInstance(this,t);if(typeof e=="string"){if(typeof n[e]=="undefined")throw new TypeError(`No method named "${e}"`);n[e]()}})}}e.on(document,Ks,et,function(e){(e.target.tagName==="A"||e.delegateTarget&&e.delegateTarget.tagName==="A")&&e.preventDefault();const n=ns(this),s=t.find(n);for(const e of s)le.getOrCreateInstance(e,{toggle:!1}).toggle()}),c(le);var O,z,s="top",se,Ln,Bn,ae,Gn,Qn,Je,St,At,kt,Et,he,o="bottom",i="right",n="left",xe="auto",G=[s,o,i,n],F="start",W="end",$t="clippingParents",ze="viewport",L="popper",Kt="reference",Ie=G.reduce(function(e,t){return e.concat([t+"-"+F,t+"-"+W])},[]),Be=[].concat(G,[xe]).reduce(function(e,t){return e.concat([t,t+"-"+F,t+"-"+W])},[]),Gt="beforeRead",Xt="read",Qt="afterRead",Zt="beforeMain",Jt="main",en="afterMain",tn="beforeWrite",nn="write",sn="afterWrite",on=[Gt,Xt,Qt,Zt,Jt,en,tn,nn,sn];function f(e){return e?(e.nodeName||"").toLowerCase():null}function r(e){if(e==null)return window;if(e.toString()!=="[object Window]"){var t=e.ownerDocument;return t?t.defaultView||window:window}return e}function D(e){var t=r(e).Element;return e instanceof t||e instanceof Element}function l(e){var t=r(e).HTMLElement;return e instanceof t||e instanceof HTMLElement}function Ye(e){if(typeof ShadowRoot=="undefined")return!1;var t=r(e).ShadowRoot;return e instanceof t||e instanceof ShadowRoot}function Ss(e){var t=e.state;Object.keys(t.elements).forEach(function(e){var o=t.styles[e]||{},s=t.attributes[e]||{},n=t.elements[e];if(!l(n)||!f(n))return;Object.assign(n.style,o),Object.keys(s).forEach(function(e){var t=s[e];t===!1?n.removeAttribute(e):n.setAttribute(e,t===!0?"":t)})})}function Es(e){var t=e.state,n={popper:{position:t.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(t.elements.popper.style,n.popper),t.styles=n,t.elements.arrow&&Object.assign(t.elements.arrow.style,n.arrow),function(){Object.keys(t.elements).forEach(function(e){var s=t.elements[e],o=t.attributes[e]||{},i=Object.keys(t.styles.hasOwnProperty(e)?t.styles[e]:n[e]),a=i.reduce(function(e,t){return e[t]="",e},{});if(!l(s)||!f(s))return;Object.assign(s.style,a),Object.keys(o).forEach(function(e){s.removeAttribute(e)})})}}const Ze={name:"applyStyles",enabled:!0,phase:"write",fn:Ss,effect:Es,requires:["computeStyles"]};function m(e){return e.split("-")[0]}O=Math.max,se=Math.min,z=Math.round;function nt(){var e=navigator.userAgentData;return e!=null&&e.brands?e.brands.map(function(e){return e.brand+"/"+e.version}).join(" "):navigator.userAgent}function jn(){return!/^((?!chrome|android).)*safari/i.test(nt())}function X(e,t,n){t===void 0&&(t=!1),n===void 0&&(n=!1),s=e.getBoundingClientRect(),o=1,i=1,t&&l(e)&&(o=e.offsetWidth>0?z(s.width)/e.offsetWidth||1:1,i=e.offsetHeight>0?z(s.height)/e.offsetHeight||1:1);var s,o,i,f=D(e)?r(e):window,a=f.visualViewport,u=!jn()&&n,c=(s.left+(u&&a?a.offsetLeft:0))/o,d=(s.top+(u&&a?a.offsetTop:0))/i,h=s.width/o,m=s.height/i;return{width:h,height:m,top:d,right:c+h,bottom:d+m,left:c,x:c,y:d}}function dt(e){var t=X(e),n=e.offsetWidth,s=e.offsetHeight;return Math.abs(t.width-n)<=1&&(n=t.width),Math.abs(t.height-s)<=1&&(s=t.height),{x:e.offsetLeft,y:e.offsetTop,width:n,height:s}}function wn(e,t){var n,s=t.getRootNode&&t.getRootNode();if(e.contains(t))return!0;if(s&&Ye(s)){n=t;do{if(n&&e.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function p(e){return r(e).getComputedStyle(e)}function xs(e){return["table","td","th"].indexOf(f(e))>=0}function _(e){return((D(e)?e.ownerDocument:e.document)||window.document).documentElement}function ve(e){return f(e)==="html"?e:e.assignedSlot||e.parentNode||(Ye(e)?e.host:null)||_(e)}function kn(e){return!l(e)||p(e).position==="fixed"?null:e.offsetParent}function _s(e){var t,n,o,s=/firefox/i.test(nt()),i=/Trident/i.test(nt());if(i&&l(e)&&(o=p(e),o.position==="fixed"))return null;for(t=ve(e),Ye(t)&&(t=t.host);l(t)&&["html","body"].indexOf(f(t))<0;){if(n=p(t),n.transform!=="none"||n.perspective!=="none"||n.contain==="paint"||["transform","perspective"].indexOf(n.willChange)!==-1||s&&n.willChange==="filter"||s&&n.filter&&n.filter!=="none")return t;t=t.parentNode}return null}function re(e){for(var n=r(e),t=kn(e);t&&xs(t)&&p(t).position==="static";)t=kn(t);return t&&(f(t)==="html"||f(t)==="body"&&p(t).position==="static")?n:t||_s(e)||n}function Ue(e){return["top","bottom"].indexOf(e)>=0?"x":"y"}function oe(e,t,n){return O(e,se(t,n))}function js(e,t,n){var s=oe(e,t,n);return s>n?n:s}function zn(){return{top:0,right:0,bottom:0,left:0}}function Dn(e){return Object.assign({},zn(),e)}function Nn(e,t){return t.reduce(function(t,n){return t[n]=e,t},{})}Ln=function(t,n){return t=typeof t=="function"?t(Object.assign({},n.rects,{placement:n.placement})):t,Dn(typeof t!="number"?t:Nn(t,G))};function bs(e){var r,c,d,u,p,g,v,b,j,y,_,O,x,C,E,t=e.state,S=e.name,A=e.options,h=t.elements.arrow,f=t.modifiersData.popperOffsets,w=m(t.placement),a=Ue(w),k=[n,i].indexOf(w)>=0,l=k?"height":"width";if(!h||!f)return;g=Ln(A.padding,t),v=dt(h),b=a==="y"?s:n,j=a==="y"?o:i,y=t.rects.reference[l]+t.rects.reference[a]-f[a]-t.rects.popper[l],_=f[a]-t.rects.reference[a],c=re(h),p=c?a==="y"?c.clientHeight||0:c.clientWidth||0:0,O=y/2-_/2,x=g[b],C=p-v[l]-g[j],u=p/2-v[l]/2+O,d=oe(x,u,C),E=a,t.modifiersData[S]=(r={},r[E]=d,r.centerOffset=d-u,r)}function vs(e){var n=e.state,o=e.options,s=o.element,t=s===void 0?"[data-popper-arrow]":s;if(t==null)return;if(typeof t=="string"&&(t=n.elements.popper.querySelector(t),!t))return;if(!wn(n.elements.popper,t))return;n.elements.arrow=t}const Hn={name:"arrow",enabled:!0,phase:"main",fn:bs,effect:vs,requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function Q(e){return e.split("-")[1]}Bn={top:"auto",right:"auto",bottom:"auto",left:"auto"};function ms(e){var n=e.x,s=e.y,o=window,t=o.devicePixelRatio||1;return{x:z(n*t)/t||0,y:z(s*t)/t||0}}function $n(e){var c,u,h,b,j,y,x,T,z,f=e.popper,D=e.popperRect,d=e.placement,k=e.variation,m=e.offsets,S=e.position,v=e.gpuAcceleration,A=e.adaptive,w=e.roundOffsets,L=e.isFixed,N=m.x,t=N===void 0?0:N,M=m.y,a=M===void 0?0:M,E=typeof w=="function"?w({x:t,y:a}):{x:t,y:a},t=E.x,a=E.y,F=m.hasOwnProperty("x"),C=m.hasOwnProperty("y"),O=n,g=s,l=window;return A&&(c=re(f),y="clientHeight",j="clientWidth",c===r(f)&&(c=_(f),p(c).position!=="static"&&S==="absolute"&&(y="scrollHeight",j="scrollWidth")),c=c,(d===s||(d===n||d===i)&&k===W)&&(g=o,T=L&&c===l&&l.visualViewport?l.visualViewport.height:c[y],a-=T-D.height,a*=v?1:-1),(d===n||(d===s||d===o)&&k===W)&&(O=i,z=L&&c===l&&l.visualViewport?l.visualViewport.width:c[j],t-=z-D.width,t*=v?1:-1)),x=Object.assign({position:S},A&&Bn),b=w===!0?ms({x:t,y:a}):{x:t,y:a},t=b.x,a=b.y,v?Object.assign({},x,(h={},h[g]=C?"0":"",h[O]=F?"0":"",h.transform=(l.devicePixelRatio||1)<=1?"translate("+t+"px, "+a+"px)":"translate3d("+t+"px, "+a+"px, 0)",h)):Object.assign({},x,(u={},u[g]=C?a+"px":"",u[O]=F?t+"px":"",u.transform="",u))}function hs(e){var t=e.state,n=e.options,s=n.gpuAcceleration,c=s===void 0||s,o=n.adaptive,l=o===void 0||o,i=n.roundOffsets,a=i===void 0||i,r={placement:m(t.placement),variation:Q(t.placement),popper:t.elements.popper,popperRect:t.rects.popper,gpuAcceleration:c,isFixed:t.options.strategy==="fixed"};t.modifiersData.popperOffsets!=null&&(t.styles.popper=Object.assign({},t.styles.popper,$n(Object.assign({},r,{offsets:t.modifiersData.popperOffsets,position:t.options.strategy,adaptive:l,roundOffsets:a})))),t.modifiersData.arrow!=null&&(t.styles.arrow=Object.assign({},t.styles.arrow,$n(Object.assign({},r,{offsets:t.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:a})))),t.attributes.popper=Object.assign({},t.attributes.popper,{"data-popper-placement":t.placement})}const Te={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:hs,data:{}};ae={passive:!0};function ls(e){var n=e.state,t=e.instance,s=e.options,o=s.scroll,i=o===void 0||o,a=s.resize,c=a===void 0||a,l=r(n.elements.popper),d=[].concat(n.scrollParents.reference,n.scrollParents.popper);return i&&d.forEach(function(e){e.addEventListener("scroll",t.update,ae)}),c&&l.addEventListener("resize",t.update,ae),function(){i&&d.forEach(function(e){e.removeEventListener("scroll",t.update,ae)}),c&&l.removeEventListener("resize",t.update,ae)}}const Pe={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:ls,data:{}};Gn={left:"right",right:"left",bottom:"top",top:"bottom"};function ke(e){return e.replace(/left|right|bottom|top/g,function(e){return Gn[e]})}Qn={start:"end",end:"start"};function Zn(e){return e.replace(/start|end/g,function(e){return Qn[e]})}function Ke(e){var t=r(e),n=t.pageXOffset,s=t.pageYOffset;return{scrollLeft:n,scrollTop:s}}function qe(e){return X(_(e)).left+Ke(e).scrollLeft}function rs(e,t){var s,d=r(e),o=_(e),n=d.visualViewport,i=o.clientWidth,a=o.clientHeight,c=0,l=0;return n&&(i=n.width,a=n.height,s=jn(),(s||!s&&t==="fixed")&&(c=n.offsetLeft,l=n.offsetTop)),{width:i,height:a,x:c+qe(e),y:l}}function Fi(e){var s,n=_(e),o=Ke(e),t=(s=e.ownerDocument)==null?void 0:s.body,i=O(n.scrollWidth,n.clientWidth,t?t.scrollWidth:0,t?t.clientWidth:0),r=O(n.scrollHeight,n.clientHeight,t?t.scrollHeight:0,t?t.clientHeight:0),a=-o.scrollLeft+qe(e),c=-o.scrollTop;return p(t||n).direction==="rtl"&&(a+=O(n.clientWidth,t?t.clientWidth:0)-i),{width:i,height:r,x:a,y:c}}function at(e){var t=p(e),n=t.overflow,s=t.overflowX,o=t.overflowY;return/auto|scroll|overlay|hidden/.test(n+o+s)}function zt(e){return["html","body","#document"].indexOf(f(e))>=0?e.ownerDocument.body:l(e)&&at(e)?e:zt(ve(e))}function ce(e,t){t===void 0&&(t=[]);var s,n=zt(e),o=n===((s=e.ownerDocument)==null?void 0:s.body),i=r(n),a=o?[i].concat(i.visualViewport||[],at(n)?n:[]):n,c=t.concat(a);return o?c:c.concat(ce(ve(a)))}function Fe(e){return Object.assign({},e,{left:e.x,top:e.y,right:e.x+e.width,bottom:e.y+e.height})}function cs(e,t){var n=X(e,!1,t==="fixed");return n.top=n.top+e.clientTop,n.left=n.left+e.clientLeft,n.bottom=n.top+e.clientHeight,n.right=n.left+e.clientWidth,n.width=e.clientWidth,n.height=e.clientHeight,n.x=n.left,n.y=n.top,n}function qn(e,t,n){return t===ze?Fe(rs(e,n)):D(t)?cs(t,n):Fe(Fi(_(e)))}function ds(e){var n=ce(ve(e)),s=["absolute","fixed"].indexOf(p(e).position)>=0,t=s&&l(e)?re(e):e;return D(t)?n.filter(function(e){return D(e)&&wn(e,t)&&f(e)!=="body"}):[]}function us(e,t,n,s){var a=t==="clippingParents"?ds(e):[].concat(t),i=[].concat(a,[n]),r=i[0],o=i.reduce(function(t,n){var o=qn(e,n,s);return t.top=O(o.top,t.top),t.right=se(o.right,t.right),t.bottom=se(o.bottom,t.bottom),t.left=O(o.left,t.left),t},qn(e,r,s));return o.width=o.right-o.left,o.height=o.bottom-o.top,o.x=o.left,o.y=o.top,o}function Wn(e){var a,r,l,t=e.reference,c=e.element,d=e.placement,u=d?m(d):null,p=d?Q(d):null,h=t.x+t.width/2-c.width/2,f=t.y+t.height/2-c.height/2;switch(u){case s:a={x:h,y:t.y-c.height};break;case o:a={x:h,y:t.y+t.height};break;case i:a={x:t.x+t.width,y:f};break;case n:a={x:t.x-c.width,y:f};break;default:a={x:t.x,y:t.y}}if(r=u?Ue(u):null,r!=null)switch(l=r==="y"?"height":"width",p){case F:a[r]=a[r]-(t[l]/2-c[l]/2);break;case W:a[r]=a[r]+(t[l]/2-c[l]/2);break}return a}function I(e,t){t===void 0&&(t={});var w,n=t,v=n.placement,j=v===void 0?e.placement:v,f=n.strategy,T=f===void 0?e.strategy:f,p=n.boundary,E=p===void 0?$t:p,x=n.rootBoundary,F=x===void 0?ze:x,C=n.elementContext,c=C===void 0?L:C,m=n.altBoundary,M=m!==void 0&&m,b=n.padding,d=b===void 0?0:b,a=Dn(typeof d!="number"?d:Nn(d,G)),S=c===L?Kt:L,O=e.rects.popper,h=e.elements[M?S:c],r=us(D(h)?h:h.contextElement||_(e.elements.popper),E,F,T),y=X(e.elements.reference),k=Wn({reference:y,element:O,strategy:"absolute",placement:j}),A=Fe(Object.assign({},O,k)),l=c===L?A:y,u={top:r.top-l.top+a.top,bottom:l.bottom-r.bottom+a.bottom,left:r.left-l.left+a.left,right:l.right-r.right+a.right},g=e.modifiersData.offset;return c===L&&g&&(w=g[j],Object.keys(u).forEach(function(e){var t=[i,o].indexOf(e)>=0?1:-1,n=[s,o].indexOf(e)>=0?"y":"x";u[e]+=w[n]*t})),u}function fs(e,t){t===void 0&&(t={});var s,n=t,c=n.placement,l=n.boundary,d=n.rootBoundary,u=n.padding,h=n.flipVariations,i=n.allowedAutoPlacements,f=i===void 0?Be:i,a=Q(c),r=a?h?Ie:Ie.filter(function(e){return Q(e)===a}):G,o=r.filter(function(e){return f.indexOf(e)>=0});return o.length===0&&(o=r),s=o.reduce(function(t,n){return t[n]=I(e,{placement:n,boundary:l,rootBoundary:d,padding:u})[m(n)],t},{}),Object.keys(s).sort(function(e,t){return s[e]-s[t]})}function ps(e){if(m(e)===xe)return[];var t=ke(e);return[Zn(e),t,Zn(t)]}function gs(e){var r,c,l,u,h,g,v,y,_,x,E,k,z,t=e.state,a=e.options,C=e.name;if(t.modifiersData[C]._skip)return;for(var M=a.mainAxis,H=M===void 0||M,D=a.altAxis,R=D===void 0||D,L=a.fallbackPlacements,N=a.padding,w=a.boundary,O=a.rootBoundary,B=a.altBoundary,T=a.flipVariations,j=T===void 0||T,V=a.allowedAutoPlacements,d=t.options.placement,U=m(d),P=U===d,K=L||(P||!j?[ke(d)]:ps(d)),p=[d].concat(K).reduce(function(e,n){return e.concat(m(n)===xe?fs(t,{placement:n,boundary:w,rootBoundary:O,padding:N,flipVariations:j,allowedAutoPlacements:V}):n)},[]),W=t.rects.reference,$=t.rects.popper,A=new Map,S=!0,f=p[0],b=0;b=0,_=y?"width":"height",h=I(t,{placement:r,boundary:w,rootBoundary:O,altBoundary:B,padding:N}),l=y?g?i:n:g?o:s,W[_]>$[_]&&(l=ke(l)),z=ke(l),c=[],H&&c.push(h[v]<=0),R&&c.push(h[l]<=0,h[z]<=0),c.every(function(e){return e})){f=r,S=!1;break}A.set(r,c)}if(S)for(k=j?3:1,E=function(t){var n=p.find(function(e){var n=A.get(e);if(n)return n.slice(0,t).every(function(e){return e})});if(n)return f=n,"break"},u=k;u>0;u--)if(x=E(u),x==="break")break;t.placement!==f&&(t.modifiersData[C]._skip=!0,t.placement=f,t.reset=!0)}const Pn={name:"flip",enabled:!0,phase:"main",fn:gs,requiresIfExists:["offset"],data:{_skip:!1}};function Rn(e,t,n){return n===void 0&&(n={x:0,y:0}),{top:e.top-t.height-n.y,right:e.right-t.width+n.x,bottom:e.bottom-t.height+n.y,left:e.left-t.width-n.x}}function Tn(e){return[s,i,o,n].some(function(t){return e[t]>=0})}function ys(e){var t=e.state,a=e.name,r=t.rects.reference,c=t.rects.popper,l=t.modifiersData.preventOverflow,d=I(t,{elementContext:"reference"}),u=I(t,{altBoundary:!0}),n=Rn(d,r),s=Rn(u,c,l),o=Tn(n),i=Tn(s);t.modifiersData[a]={referenceClippingOffsets:n,popperEscapeOffsets:s,isReferenceHidden:o,hasPopperEscaped:i},t.attributes.popper=Object.assign({},t.attributes.popper,{"data-popper-reference-hidden":o,"data-popper-escaped":i})}const An={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:ys};function ws(e,t,o){var c=m(e),d=[n,s].indexOf(c)>=0?-1:1,l=typeof o=="function"?o(Object.assign({},t,{placement:e})):o,a=l[0],r=l[1],a=a||0,r=(r||0)*d;return[n,i].indexOf(c)>=0?{x:r,y:a}:{x:a,y:r}}function Os(e){var t=e.state,i=e.options,a=e.name,n=i.offset,r=n===void 0?[0,0]:n,s=Be.reduce(function(e,n){return e[n]=ws(n,t.rects,r),e},{}),o=s[t.placement],c=o.x,l=o.y;t.modifiersData.popperOffsets!=null&&(t.modifiersData.popperOffsets.x+=c,t.modifiersData.popperOffsets.y+=l),t.modifiersData[a]=s}const xn={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:Os};function Cs(e){var t=e.state,n=e.name;t.modifiersData[n]=Wn({reference:t.rects.reference,element:t.rects.popper,strategy:"absolute",placement:t.placement})}const Qe={name:"popperOffsets",enabled:!0,phase:"read",fn:Cs,data:{}};function ks(e){return e==="x"?"y":"x"}function As(e){var r,c,h,p,v,w,C,k,A,M,T,z,D,L,R,P,H,B,V,$,W,U,K,q,Y,G,X,Z,t=e.state,l=e.options,be=e.name,fe,ue,ee,te,ie,ce,le,me,pe=l.mainAxis,ge=pe===void 0||pe,ne=l.altAxis,we=ne!==void 0&&ne,_e=l.boundary,ye=l.rootBoundary,ve=l.altBoundary,je=l.padding,de=l.tether,d=de===void 0||de,ae=l.tetherOffset,S=ae===void 0?0:ae,x=I(t,{boundary:_e,rootBoundary:ye,padding:je,altBoundary:ve}),J=m(t.placement),E=Q(t.placement),he=!E,a=Ue(J),j=ks(a),b=t.modifiersData.popperOffsets,u=t.rects.reference,g=t.rects.popper,_=typeof S=="function"?S(Object.assign({},t.rects,{placement:t.placement})):S,f=typeof _=="number"?{mainAxis:_,altAxis:_}:Object.assign({mainAxis:0,altAxis:0},_),y=t.modifiersData.offset?t.modifiersData.offset[t.placement]:null,N={x:0,y:0};if(!b)return;ge&&(R=a==="y"?s:n,P=a==="y"?o:i,r=a==="y"?"height":"width",h=b[a],V=h+x[R],$=h-x[P],W=d?-g[r]/2:0,Z=E===F?u[r]:g[r],X=E===F?-g[r]:-u[r],q=t.elements.arrow,ue=d&&q?dt(q):{width:0,height:0},k=t.modifiersData["arrow#persistent"]?t.modifiersData["arrow#persistent"].padding:zn(),K=k[R],U=k[P],v=oe(0,u[r],ue[r]),ee=he?u[r]/2-W-v-K-f.mainAxis:Z-v-K-f.mainAxis,te=he?-u[r]/2+W+v+U+f.mainAxis:X+v+U+f.mainAxis,C=t.elements.arrow&&re(t.elements.arrow),ie=C?a==="y"?C.clientTop||0:C.clientLeft||0:0,B=(fe=y?.[a])!=null?fe:0,ce=h+ee-B-ie,le=h+te-B,H=oe(d?se(V,ce):V,h,d?O($,le):$),b[a]=H,N[a]=H-h),we&&(Y=a==="x"?s:n,me=a==="x"?o:i,c=b[j],p=j==="y"?"height":"width",L=c+x[Y],D=c-x[me],w=[s,n].indexOf(J)!==-1,z=(G=y?.[j])!=null?G:0,T=w?L:c-u[p]-g[p]-z+f.altAxis,M=w?c+u[p]+g[p]-z-f.altAxis:D,A=d&&w?js(T,c,M):oe(d?T:L,c,d?M:D),b[j]=A,N[j]=A-c),t.modifiersData[be]=N}const un={name:"preventOverflow",enabled:!0,phase:"main",fn:As,requiresIfExists:["offset"]};function Ms(e){return{scrollLeft:e.scrollLeft,scrollTop:e.scrollTop}}function Fs(e){return e===r(e)||!l(e)?Ke(e):Ms(e)}function Ts(e){var t=e.getBoundingClientRect(),n=z(t.width)/e.offsetWidth||1,s=z(t.height)/e.offsetHeight||1;return n!==1||s!==1}function zs(e,t,n){n===void 0&&(n=!1);var r=l(t),c=l(t)&&Ts(t),i=_(t),o=X(e,c,n),a={scrollLeft:0,scrollTop:0},s={x:0,y:0};return(r||!r&&!n)&&((f(t)!=="body"||at(i))&&(a=Fs(t)),l(t)?(s=X(t,!0),s.x+=t.clientLeft,s.y+=t.clientTop):i&&(s.x=qe(i))),{x:o.left+a.scrollLeft-s.x,y:o.top+a.scrollTop-s.y,width:o.width,height:o.height}}function Ds(e){var n=new Map,t=new Set,s=[];e.forEach(function(e){n.set(e.name,e)});function o(e){t.add(e.name);var i=[].concat(e.requires||[],e.requiresIfExists||[]);i.forEach(function(e){if(!t.has(e)){var s=n.get(e);s&&o(s)}}),s.push(e)}return e.forEach(function(e){t.has(e.name)||o(e)}),s}function Ns(e){var t=Ds(e);return on.reduce(function(e,n){return e.concat(t.filter(function(e){return e.phase===n}))},[])}function Ls(e){var t;return function(){return t||(t=new Promise(function(n){Promise.resolve().then(function(){t=void 0,n(e())})})),t}}function Rs(e){var t=e.reduce(function(e,t){var n=e[t.name];return e[t.name]=n?Object.assign({},n,t,{options:Object.assign({},n.options,t.options),data:Object.assign({},n.data,t.data)}):t,e},{});return Object.keys(t).map(function(e){return t[e]})}Je={placement:"bottom",modifiers:[],strategy:"absolute"};function Tt(){for(var t=arguments.length,n=new Array(t),e=0;eNumber.parseInt(e,10)):typeof e=="function"?t=>e(t,this._element):e}_getPopperConfig(){const e={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return(this._inNavbar||this._config.display==="static")&&(b.setDataAttribute(this._menu,"popper","static"),e.modifiers=[{name:"applyStyles",enabled:!1}]),{...e,...typeof this._config.popperConfig=="function"?this._config.popperConfig(e):this._config.popperConfig}}_selectMenuItem({key:e,target:n}){const s=t.find(_o,this._menu).filter(e=>H(e));if(!s.length)return;Le(s,n,e===gt,!s.includes(n)).focus()}static jQueryInterface(e){return this.each(function(){const t=h.getOrCreateInstance(this,e);if(typeof e!="string")return;if(typeof t[e]=="undefined")throw new TypeError(`No method named "${e}"`);t[e]()})}static clearMenus(e){if(e.button===to||e.type==="keyup"&&e.key!==vt)return;const n=t.find(vo);for(const a of n){const t=h.getInstance(a);if(!t||t._config.autoClose===!1)continue;const s=e.composedPath(),o=s.includes(t._menu);if(s.includes(t._element)||t._config.autoClose==="inside"&&!o||t._config.autoClose==="outside"&&o)continue;if(t._menu.contains(e.target)&&(e.type==="keyup"&&e.key===vt||/input|select|option|textarea|form/i.test(e.target.tagName)))continue;const i={relatedTarget:t._element};e.type==="click"&&(i.clickEvent=e),t._completeHide(i)}}static dataApiKeydownHandler(e){const a=/input|textarea/i.test(e.target.tagName),s=e.key===Qs,o=[Js,gt].includes(e.key);if(!o&&!s)return;if(a&&!s)return;e.preventDefault();const i=this.matches(S)?this:t.prev(this,S)[0]||t.next(this,S)[0]||t.findOne(S,e.delegateTarget.parentNode),n=h.getOrCreateInstance(i);if(o){e.stopPropagation(),n.show(),n._selectMenuItem(e);return}n._isShown()&&(e.stopPropagation(),n.hide(),i.focus())}}e.on(document,jt,S,h.dataApiKeydownHandler),e.on(document,jt,be,h.dataApiKeydownHandler),e.on(document,bt,h.clearMenus),e.on(document,co,h.clearMenus),e.on(document,bt,S,function(e){e.preventDefault(),h.getOrCreateInstance(this).toggle()}),c(h);const wt=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",Ot=".sticky-top",me="padding-right",xt="margin-right";class tt{constructor(){this._element=document.body}getWidth(){const e=document.documentElement.clientWidth;return Math.abs(window.innerWidth-e)}hide(){const e=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,me,t=>t+e),this._setElementAttributes(wt,me,t=>t+e),this._setElementAttributes(Ot,xt,t=>t-e)}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,me),this._resetElementAttributes(wt,me),this._resetElementAttributes(Ot,xt)}isOverflowing(){return this.getWidth()>0}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(e,t,n){const s=this.getWidth(),o=e=>{if(e!==this._element&&window.innerWidth>e.clientWidth+s)return;this._saveInitialAttribute(e,t);const o=window.getComputedStyle(e).getPropertyValue(t);e.style.setProperty(t,`${n(Number.parseFloat(o))}px`)};this._applyManipulationCallback(e,o)}_saveInitialAttribute(e,t){const n=e.style.getPropertyValue(t);n&&b.setDataAttribute(e,t,n)}_resetElementAttributes(e,t){const n=e=>{const n=b.getDataAttribute(e,t);if(n===null){e.style.removeProperty(t);return}b.removeDataAttribute(e,t),e.style.setProperty(t,n)};this._applyManipulationCallback(e,n)}_applyManipulationCallback(e,n){if(g(e)){n(e);return}for(const s of t.find(e,this._element))n(s)}}const Ft="backdrop",Ho="fade",pt="show",Dt=`mousedown.bs.${Ft}`,Vo={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},$o={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"};class Nt extends te{constructor(e){super(),this._config=this._getConfig(e),this._isAppended=!1,this._element=null}static get Default(){return Vo}static get DefaultType(){return $o}static get NAME(){return Ft}show(e){if(!this._config.isVisible){y(e);return}this._append();const t=this._getElement();this._config.isAnimated&&ne(t),t.classList.add(pt),this._emulateAnimation(()=>{y(e)})}hide(e){if(!this._config.isVisible){y(e);return}this._getElement().classList.remove(pt),this._emulateAnimation(()=>{this.dispose(),y(e)})}dispose(){if(!this._isAppended)return;e.off(this._element,Dt),this._element.remove(),this._isAppended=!1}_getElement(){if(!this._element){const e=document.createElement("div");e.className=this._config.className,this._config.isAnimated&&e.classList.add(Ho),this._element=e}return this._element}_configAfterMerge(e){return e.rootElement=E(e.rootElement),e}_append(){if(this._isAppended)return;const t=this._getElement();this._config.rootElement.append(t),e.on(t,Dt,()=>{y(this._config.clickCallback)}),this._isAppended=!0}_emulateAnimation(e){Yn(e,this._getElement(),this._config.isAnimated)}}const Uo="focustrap",Ko="bs.focustrap",_e=`.${Ko}`,Yo=`focusin${_e}`,Go=`keydown.tab${_e}`,Xo="Tab",Qo="forward",Rt="backward",Jo={autofocus:!0,trapElement:null},ei={autofocus:"boolean",trapElement:"element"};class Pt extends te{constructor(e){super(),this._config=this._getConfig(e),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return Jo}static get DefaultType(){return ei}static get NAME(){return Uo}activate(){if(this._isActive)return;this._config.autofocus&&this._config.trapElement.focus(),e.off(document,_e),e.on(document,Yo,e=>this._handleFocusin(e)),e.on(document,Go,e=>this._handleKeydown(e)),this._isActive=!0}deactivate(){if(!this._isActive)return;this._isActive=!1,e.off(document,_e)}_handleFocusin(e){const{trapElement:n}=this._config;if(e.target===document||e.target===n||n.contains(e.target))return;const s=t.focusableChildren(n);s.length===0?n.focus():this._lastTabNavDirection===Rt?s[s.length-1].focus():s[0].focus()}_handleKeydown(e){if(e.key!==Xo)return;this._lastTabNavDirection=e.shiftKey?Rt:Qo}}const ni="modal",si="bs.modal",d=`.${si}`,ii=".data-api",ai="Escape",ri=`hide${d}`,ci=`hidePrevented${d}`,Ht=`hidden${d}`,It=`show${d}`,ui=`shown${d}`,hi=`resize${d}`,mi=`click.dismiss${d}`,fi=`mousedown.dismiss${d}`,pi=`keydown.dismiss${d}`,gi=`click${d}${ii}`,Bt="modal-open",bi="fade",Vt="show",Ne="modal-static",_i=".modal.show",wi=".modal-dialog",Oi=".modal-body",xi='[data-bs-toggle="modal"]',Ci={backdrop:!0,focus:!0,keyboard:!0},Ei={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"};class B extends u{constructor(e,n){super(e,n),this._dialog=t.findOne(wi,this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new tt,this._addEventListeners()}static get Default(){return Ci}static get DefaultType(){return Ei}static get NAME(){return ni}toggle(e){return this._isShown?this.hide():this.show(e)}show(t){if(this._isShown||this._isTransitioning)return;const n=e.trigger(this._element,It,{relatedTarget:t});if(n.defaultPrevented)return;this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(Bt),this._adjustDialog(),this._backdrop.show(()=>this._showElement(t))}hide(){if(!this._isShown||this._isTransitioning)return;const t=e.trigger(this._element,ri);if(t.defaultPrevented)return;this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove(Vt),this._queueCallback(()=>this._hideModal(),this._element,this._isAnimated())}dispose(){for(const t of[window,this._dialog])e.off(t,d);this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new Nt({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new Pt({trapElement:this._element})}_showElement(n){document.body.contains(this._element)||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0;const s=t.findOne(Oi,this._dialog);s&&(s.scrollTop=0),ne(this._element),this._element.classList.add(Vt);const o=()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,e.trigger(this._element,ui,{relatedTarget:n})};this._queueCallback(o,this._dialog,this._isAnimated())}_addEventListeners(){e.on(this._element,pi,e=>{if(e.key!==ai)return;if(this._config.keyboard){e.preventDefault(),this.hide();return}this._triggerBackdropTransition()}),e.on(window,hi,()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()}),e.on(this._element,fi,t=>{e.one(this._element,mi,e=>{if(this._element!==t.target||this._element!==e.target)return;if(this._config.backdrop==="static"){this._triggerBackdropTransition();return}this._config.backdrop&&this.hide()})})}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide(()=>{document.body.classList.remove(Bt),this._resetAdjustments(),this._scrollBar.reset(),e.trigger(this._element,Ht)})}_isAnimated(){return this._element.classList.contains(bi)}_triggerBackdropTransition(){const n=e.trigger(this._element,ci);if(n.defaultPrevented)return;const s=this._element.scrollHeight>document.documentElement.clientHeight,t=this._element.style.overflowY;if(t==="hidden"||this._element.classList.contains(Ne))return;s||(this._element.style.overflowY="hidden"),this._element.classList.add(Ne),this._queueCallback(()=>{this._element.classList.remove(Ne),this._queueCallback(()=>{this._element.style.overflowY=t},this._dialog)},this._dialog),this._element.focus()}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),n=e>0;if(n&&!t){const t=a()?"paddingLeft":"paddingRight";this._element.style[t]=`${e}px`}if(!n&&t){const t=a()?"paddingRight":"paddingLeft";this._element.style[t]=`${e}px`}}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(e,t){return this.each(function(){const n=B.getOrCreateInstance(this,e);if(typeof e!="string")return;if(typeof n[e]=="undefined")throw new TypeError(`No method named "${e}"`);n[e](t)})}}e.on(document,gi,xi,function(n){const s=v(this);["A","AREA"].includes(this.tagName)&&n.preventDefault(),e.one(s,It,t=>{if(t.defaultPrevented)return;e.one(s,Ht,()=>{H(this)&&this.focus()})});const o=t.findOne(_i);o&&B.getInstance(o).hide();const i=B.getOrCreateInstance(s);i.toggle(this)}),Se(B),c(B);const Ai="offcanvas",Si="bs.offcanvas",j=`.${Si}`,Ut=".data-api",Ti=`load${j}${Ut}`,zi="Escape",qt="show",Yt="showing",an="hiding",Ri="offcanvas-backdrop",rn=".offcanvas.show",Hi=`show${j}`,Ii=`shown${j}`,Bi=`hide${j}`,cn=`hidePrevented${j}`,ln=`hidden${j}`,Wi=`resize${j}`,Ui=`click${j}${Ut}`,Ki=`keydown.dismiss${j}`,qi='[data-bs-toggle="offcanvas"]',Yi={backdrop:!0,keyboard:!0,scroll:!1},Gi={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"};class A extends u{constructor(e,t){super(e,t),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return Yi}static get DefaultType(){return Gi}static get NAME(){return Ai}toggle(e){return this._isShown?this.hide():this.show(e)}show(t){if(this._isShown)return;const n=e.trigger(this._element,Hi,{relatedTarget:t});if(n.defaultPrevented)return;this._isShown=!0,this._backdrop.show(),this._config.scroll||(new tt).hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add(Yt);const s=()=>{(!this._config.scroll||this._config.backdrop)&&this._focustrap.activate(),this._element.classList.add(qt),this._element.classList.remove(Yt),e.trigger(this._element,Ii,{relatedTarget:t})};this._queueCallback(s,this._element,!0)}hide(){if(!this._isShown)return;const t=e.trigger(this._element,Bi);if(t.defaultPrevented)return;this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add(an),this._backdrop.hide();const n=()=>{this._element.classList.remove(qt,an),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||(new tt).reset(),e.trigger(this._element,ln)};this._queueCallback(n,this._element,!0)}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){const n=()=>{if(this._config.backdrop==="static"){e.trigger(this._element,cn);return}this.hide()},t=Boolean(this._config.backdrop);return new Nt({className:Ri,isVisible:t,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:t?n:null})}_initializeFocusTrap(){return new Pt({trapElement:this._element})}_addEventListeners(){e.on(this._element,Ki,t=>{if(t.key!==zi)return;if(!this._config.keyboard){e.trigger(this._element,cn);return}this.hide()})}static jQueryInterface(e){return this.each(function(){const t=A.getOrCreateInstance(this,e);if(typeof e!="string")return;if(t[e]===void 0||e.startsWith("_")||e==="constructor")throw new TypeError(`No method named "${e}"`);t[e](this)})}}e.on(document,Ui,qi,function(n){const s=v(this);if(["A","AREA"].includes(this.tagName)&&n.preventDefault(),w(this))return;e.one(s,ln,()=>{H(this)&&this.focus()});const o=t.findOne(rn);o&&o!==s&&A.getInstance(o).hide();const i=A.getOrCreateInstance(s);i.toggle(this)}),e.on(window,Ti,()=>{for(const e of t.find(rn))A.getOrCreateInstance(e).show()}),e.on(window,Wi,()=>{for(const e of t.find("[aria-modal][class*=show][class*=offcanvas-]"))getComputedStyle(e).position!=="fixed"&&A.getOrCreateInstance(e).hide()}),Se(A),c(A);const Qi=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),Zi=/^aria-[\w-]*$/i,Ji=/^(?:(?:https?|mailto|ftp|tel|file|sms):|[^#&/:?]*(?:[#/?]|$))/i,ea=/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i,ta=(e,t)=>{const n=e.nodeName.toLowerCase();return t.includes(n)?!Qi.has(n)||Boolean(Ji.test(e.nodeValue)||ea.test(e.nodeValue)):t.filter(e=>e instanceof RegExp).some(e=>e.test(n))},dn={"*":["class","dir","id","lang","role",Zi],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]};function sa(e,t,n){if(!e.length)return e;if(n&&typeof n=="function")return n(e);const o=new window.DOMParser,s=o.parseFromString(e,"text/html"),i=[].concat(...s.body.querySelectorAll("*"));for(const e of i){const n=e.nodeName.toLowerCase();if(!Object.keys(t).includes(n)){e.remove();continue}const s=[].concat(...e.attributes),o=[].concat(t["*"]||[],t[n]||[]);for(const t of s)ta(t,o)||e.removeAttribute(t.nodeName)}return s.body.innerHTML}const oa="TemplateFactory",ia={allowList:dn,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:"
    "},aa={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},ra={entry:"(string|element|function|null)",selector:"(string|element)"};class ca extends te{constructor(e){super(),this._config=this._getConfig(e)}static get Default(){return ia}static get DefaultType(){return aa}static get NAME(){return oa}getContent(){return Object.values(this._config.content).map(e=>this._resolvePossibleFunction(e)).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(e){return this._checkContent(e),this._config.content={...this._config.content,...e},this}toHtml(){const e=document.createElement("div");e.innerHTML=this._maybeSanitize(this._config.template);for(const[t,n]of Object.entries(this._config.content))this._setContent(e,n,t);const t=e.children[0],n=this._resolvePossibleFunction(this._config.extraClass);return n&&t.classList.add(...n.split(" ")),t}_typeCheckConfig(e){super._typeCheckConfig(e),this._checkContent(e.content)}_checkContent(e){for(const[t,n]of Object.entries(e))super._typeCheckConfig({selector:t,entry:n},ra)}_setContent(e,n,s){const o=t.findOne(s,e);if(!o)return;if(n=this._resolvePossibleFunction(n),!n){o.remove();return}if(g(n)){this._putElementInTemplate(E(n),o);return}if(this._config.html){o.innerHTML=this._maybeSanitize(n);return}o.textContent=n}_maybeSanitize(e){return this._config.sanitize?sa(e,this._config.allowList,this._config.sanitizeFn):e}_resolvePossibleFunction(e){return typeof e=="function"?e(this):e}_putElementInTemplate(e,t){if(this._config.html){t.innerHTML="",t.append(e);return}t.textContent=e.textContent}}const la="tooltip",da=new Set(["sanitize","allowList","sanitizeFn"]),Xe="fade",ha="modal",ue="show",fa=".tooltip-inner",hn=`.${ha}`,mn="hide.bs.modal",J="hover",ot="focus",ja="click",ya="manual",_a="hide",wa="hidden",Oa="show",xa="shown",Ca="inserted",Ea="click",ka="focusin",Aa="focusout",Sa="mouseenter",Ma="mouseleave",Fa={AUTO:"auto",TOP:"top",RIGHT:a()?"left":"right",BOTTOM:"bottom",LEFT:a()?"right":"left"},Ta={allowList:dn,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,0],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'',title:"",trigger:"hover focus"},za={allowList:"object",animation:"boolean",boundary:"(string|element)",container:"(string|element|boolean)",customClass:"(string|function)",delay:"(number|object)",fallbackPlacements:"array",html:"boolean",offset:"(array|string|function)",placement:"(string|function)",popperConfig:"(null|object|function)",sanitize:"boolean",sanitizeFn:"(null|function)",selector:"(string|boolean)",template:"string",title:"(string|element|function)",trigger:"string"};class U extends u{constructor(e,t){if(typeof _t=="undefined")throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");super(e,t),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners(),this._config.selector||this._fixTitle()}static get Default(){return Ta}static get DefaultType(){return za}static get NAME(){return la}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){if(!this._isEnabled)return;if(this._activeTrigger.click=!this._activeTrigger.click,this._isShown()){this._leave();return}this._enter()}dispose(){clearTimeout(this._timeout),e.off(this._element.closest(hn),mn,this._hideModalHandler),this._element.getAttribute("data-bs-original-title")&&this._element.setAttribute("title",this._element.getAttribute("data-bs-original-title")),this._disposePopper(),super.dispose()}show(){if(this._element.style.display==="none")throw new Error("Please use show on visible elements");if(!this._isWithContent()||!this._isEnabled)return;const n=e.trigger(this._element,this.constructor.eventName(Oa)),s=Jn(this._element),o=(s||this._element.ownerDocument.documentElement).contains(this._element);if(n.defaultPrevented||!o)return;this._disposePopper();const t=this._getTipElement();this._element.setAttribute("aria-describedby",t.getAttribute("id"));const{container:i}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(i.append(t),e.trigger(this._element,this.constructor.eventName(Ca))),this._popper=this._createPopper(t),t.classList.add(ue),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))e.on(t,"mouseover",Oe);const a=()=>{e.trigger(this._element,this.constructor.eventName(xa)),this._isHovered===!1&&this._leave(),this._isHovered=!1};this._queueCallback(a,this.tip,this._isAnimated())}hide(){if(!this._isShown())return;const t=e.trigger(this._element,this.constructor.eventName(_a));if(t.defaultPrevented)return;const n=this._getTipElement();if(n.classList.remove(ue),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))e.off(t,"mouseover",Oe);this._activeTrigger[ja]=!1,this._activeTrigger[ot]=!1,this._activeTrigger[J]=!1,this._isHovered=null;const s=()=>{if(this._isWithActiveTrigger())return;this._isHovered||this._disposePopper(),this._element.removeAttribute("aria-describedby"),e.trigger(this._element,this.constructor.eventName(wa))};this._queueCallback(s,this.tip,this._isAnimated())}update(){this._popper&&this._popper.update()}_isWithContent(){return Boolean(this._getTitle())}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(e){const t=this._getTemplateFactory(e).toHtml();if(!t)return null;t.classList.remove(Xe,ue),t.classList.add(`bs-${this.constructor.NAME}-auto`);const n=Gr(this.constructor.NAME).toString();return t.setAttribute("id",n),this._isAnimated()&&t.classList.add(Xe),t}setContent(e){this._newContent=e,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(e){return this._templateFactory?this._templateFactory.changeContent(e):this._templateFactory=new ca({...this._config,content:e,extraClass:this._resolvePossibleFunction(this._config.customClass)}),this._templateFactory}_getContentForTemplate(){return{[fa]:this._getTitle()}}_getTitle(){return this._resolvePossibleFunction(this._config.title)||this._element.getAttribute("data-bs-original-title")}_initializeOnDelegatedTarget(e){return this.constructor.getOrCreateInstance(e.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(Xe)}_isShown(){return this.tip&&this.tip.classList.contains(ue)}_createPopper(e){const t=typeof this._config.placement=="function"?this._config.placement.call(this,e,this._element):this._config.placement,n=Fa[t.toUpperCase()];return he(this._element,e,this._getPopperConfig(n))}_getOffset(){const{offset:e}=this._config;return typeof e=="string"?e.split(",").map(e=>Number.parseInt(e,10)):typeof e=="function"?t=>e(t,this._element):e}_resolvePossibleFunction(e){return typeof e=="function"?e.call(this._element):e}_getPopperConfig(e){const t={placement:e,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"preSetPlacement",enabled:!0,phase:"beforeMain",fn:e=>{this._getTipElement().setAttribute("data-popper-placement",e.state.placement)}}]};return{...t,...typeof this._config.popperConfig=="function"?this._config.popperConfig(t):this._config.popperConfig}}_setListeners(){const t=this._config.trigger.split(" ");for(const n of t)if(n==="click")e.on(this._element,this.constructor.eventName(Ea),this._config.selector,e=>{const t=this._initializeOnDelegatedTarget(e);t.toggle()});else if(n!==ya){const t=n===J?this.constructor.eventName(Sa):this.constructor.eventName(ka),s=n===J?this.constructor.eventName(Ma):this.constructor.eventName(Aa);e.on(this._element,t,this._config.selector,e=>{const t=this._initializeOnDelegatedTarget(e);t._activeTrigger[e.type==="focusin"?ot:J]=!0,t._enter()}),e.on(this._element,s,this._config.selector,e=>{const t=this._initializeOnDelegatedTarget(e);t._activeTrigger[e.type==="focusout"?ot:J]=t._element.contains(e.relatedTarget),t._leave()})}this._hideModalHandler=()=>{this._element&&this.hide()},e.on(this._element.closest(hn),mn,this._hideModalHandler)}_fixTitle(){const e=this._element.getAttribute("title");if(!e)return;!this._element.getAttribute("aria-label")&&!this._element.textContent.trim()&&this._element.setAttribute("aria-label",e),this._element.setAttribute("data-bs-original-title",e),this._element.removeAttribute("title")}_enter(){if(this._isShown()||this._isHovered){this._isHovered=!0;return}this._isHovered=!0,this._setTimeout(()=>{this._isHovered&&this.show()},this._config.delay.show)}_leave(){if(this._isWithActiveTrigger())return;this._isHovered=!1,this._setTimeout(()=>{this._isHovered||this.hide()},this._config.delay.hide)}_setTimeout(e,t){clearTimeout(this._timeout),this._timeout=setTimeout(e,t)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(e){const t=b.getDataAttributes(this._element);for(const e of Object.keys(t))da.has(e)&&delete t[e];return e={...t,...typeof e=="object"&&e?e:{}},e=this._mergeConfigObj(e),e=this._configAfterMerge(e),this._typeCheckConfig(e),e}_configAfterMerge(e){return e.container=e.container===!1?document.body:E(e.container),typeof e.delay=="number"&&(e.delay={show:e.delay,hide:e.delay}),typeof e.title=="number"&&(e.title=e.title.toString()),typeof e.content=="number"&&(e.content=e.content.toString()),e}_getDelegateConfig(){const e={};for(const t in this._config)this.constructor.Default[t]!==this._config[t]&&(e[t]=this._config[t]);return e.selector=!1,e.trigger="manual",e}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(e){return this.each(function(){const t=U.getOrCreateInstance(this,e);if(typeof e!="string")return;if(typeof t[e]=="undefined")throw new TypeError(`No method named "${e}"`);t[e]()})}}c(U);const Na="popover",La=".popover-header",Ra=".popover-body",Pa={...U.Default,content:"",offset:[0,8],placement:"right",template:'',trigger:"click"},Ha={...U.DefaultType,content:"(null|string|element|function)"};class ct extends U{static get Default(){return Pa}static get DefaultType(){return Ha}static get NAME(){return Na}_isWithContent(){return this._getTitle()||this._getContent()}_getContentForTemplate(){return{[La]:this._getTitle(),[Ra]:this._getContent()}}_getContent(){return this._resolvePossibleFunction(this._config.content)}static jQueryInterface(e){return this.each(function(){const t=ct.getOrCreateInstance(this,e);if(typeof e!="string")return;if(typeof t[e]=="undefined")throw new TypeError(`No method named "${e}"`);t[e]()})}}c(ct);const Ba="scrollspy",Va="bs.scrollspy",lt=`.${Va}`,Wa=".data-api",Ua=`activate${lt}`,pn=`click${lt}`,qa=`load${lt}${Wa}`,Ya="dropdown-item",q="active",Xa='[data-bs-spy="scroll"]',mt="[href]",Za=".nav, .list-group",gn=".nav-link",er=".nav-item",tr=".list-group-item",nr=`${gn}, ${er} > ${gn}, ${tr}`,sr=".dropdown",or=".dropdown-toggle",ir={offset:null,rootMargin:"0px 0px -25%",smoothScroll:!1,target:null,threshold:[.1,.5,1]},ar={offset:"(number|null)",rootMargin:"string",smoothScroll:"boolean",target:"element",threshold:"array"};class Ee extends u{constructor(e,t){super(e,t),this._targetLinks=new Map,this._observableSections=new Map,this._rootElement=getComputedStyle(this._element).overflowY==="visible"?null:this._element,this._activeTarget=null,this._observer=null,this._previousScrollData={visibleEntryTop:0,parentScrollTop:0},this.refresh()}static get Default(){return ir}static get DefaultType(){return ar}static get NAME(){return Ba}refresh(){this._initializeTargetsAndObservables(),this._maybeEnableSmoothScroll(),this._observer?this._observer.disconnect():this._observer=this._getNewObserver();for(const e of this._observableSections.values())this._observer.observe(e)}dispose(){this._observer.disconnect(),super.dispose()}_configAfterMerge(e){return e.target=E(e.target)||document.body,e.rootMargin=e.offset?`${e.offset}px 0px -30%`:e.rootMargin,typeof e.threshold=="string"&&(e.threshold=e.threshold.split(",").map(e=>Number.parseFloat(e))),e}_maybeEnableSmoothScroll(){if(!this._config.smoothScroll)return;e.off(this._config.target,pn),e.on(this._config.target,pn,mt,e=>{const t=this._observableSections.get(e.target.hash);if(t){e.preventDefault();const n=this._rootElement||window,s=t.offsetTop-this._element.offsetTop;if(n.scrollTo){n.scrollTo({top:s,behavior:"smooth"});return}n.scrollTop=s}})}_getNewObserver(){const e={root:this._rootElement,threshold:this._config.threshold,rootMargin:this._config.rootMargin};return new IntersectionObserver(e=>this._observerCallback(e),e)}_observerCallback(e){const n=e=>this._targetLinks.get(`#${e.target.id}`),s=e=>{this._previousScrollData.visibleEntryTop=e.target.offsetTop,this._process(n(e))},t=(this._rootElement||document.documentElement).scrollTop,o=t>=this._previousScrollData.parentScrollTop;this._previousScrollData.parentScrollTop=t;for(const i of e){if(!i.isIntersecting){this._activeTarget=null,this._clearActiveClass(n(i));continue}const a=i.target.offsetTop>=this._previousScrollData.visibleEntryTop;if(o&&a){if(s(i),!t)return;continue}!o&&!a&&s(i)}}_initializeTargetsAndObservables(){this._targetLinks=new Map,this._observableSections=new Map;const e=t.find(mt,this._config.target);for(const n of e){if(!n.hash||w(n))continue;const s=t.findOne(n.hash,this._element);H(s)&&(this._targetLinks.set(n.hash,n),this._observableSections.set(n.hash,s))}}_process(t){if(this._activeTarget===t)return;this._clearActiveClass(this._config.target),this._activeTarget=t,t.classList.add(q),this._activateParents(t),e.trigger(this._element,Ua,{relatedTarget:t})}_activateParents(e){if(e.classList.contains(Ya)){t.findOne(or,e.closest(sr)).classList.add(q);return}for(const n of t.parents(e,Za))for(const e of t.prev(n,nr))e.classList.add(q)}_clearActiveClass(e){e.classList.remove(q);const n=t.find(`${mt}.${q}`,e);for(const e of n)e.classList.remove(q)}static jQueryInterface(e){return this.each(function(){const t=Ee.getOrCreateInstance(this,e);if(typeof e!="string")return;if(t[e]===void 0||e.startsWith("_")||e==="constructor")throw new TypeError(`No method named "${e}"`);t[e]()})}}e.on(window,qa,()=>{for(const e of t.find(Xa))Ee.getOrCreateInstance(e)}),c(Ee);const cr="tab",lr="bs.tab",M=`.${lr}`,ur=`hide${M}`,hr=`hidden${M}`,mr=`show${M}`,fr=`shown${M}`,pr=`click${M}`,gr=`keydown${M}`,vr=`load${M}`,br="ArrowLeft",bn="ArrowRight",yr="ArrowUp",yn="ArrowDown",N="active",Mn="fade",We="show",Cr="dropdown",Er=".dropdown-toggle",kr=".dropdown-menu",$e=":not(.dropdown-toggle)",Sr='.list-group, .nav, [role="tablist"]',Mr=".nav-item, .list-group-item",Fr=`.nav-link${$e}, .list-group-item${$e}, [role="tab"]${$e}`,Kn='[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',De=`${Fr}, ${Kn}`,Dr=`.${N}[data-bs-toggle="tab"], .${N}[data-bs-toggle="pill"], .${N}[data-bs-toggle="list"]`;class R extends u{constructor(t){if(super(t),this._parent=this._element.closest(Sr),!this._parent)return;this._setInitialAttributes(this._parent,this._getChildren()),e.on(this._element,gr,e=>this._keydown(e))}static get NAME(){return cr}show(){const t=this._element;if(this._elemIsActive(t))return;const n=this._getActiveElem(),s=n?e.trigger(n,ur,{relatedTarget:t}):null,o=e.trigger(t,mr,{relatedTarget:n});if(o.defaultPrevented||s&&s.defaultPrevented)return;this._deactivate(n,t),this._activate(t,n)}_activate(t,n){if(!t)return;t.classList.add(N),this._activate(v(t));const s=()=>{if(t.getAttribute("role")!=="tab"){t.classList.add(We);return}t.removeAttribute("tabindex"),t.setAttribute("aria-selected",!0),this._toggleDropDown(t,!0),e.trigger(t,fr,{relatedTarget:n})};this._queueCallback(s,t,t.classList.contains(Mn))}_deactivate(t,n){if(!t)return;t.classList.remove(N),t.blur(),this._deactivate(v(t));const s=()=>{if(t.getAttribute("role")!=="tab"){t.classList.remove(We);return}t.setAttribute("aria-selected",!1),t.setAttribute("tabindex","-1"),this._toggleDropDown(t,!1),e.trigger(t,hr,{relatedTarget:n})};this._queueCallback(s,t,t.classList.contains(Mn))}_keydown(e){if(![br,bn,yr,yn].includes(e.key))return;e.stopPropagation(),e.preventDefault();const n=[bn,yn].includes(e.key),t=Le(this._getChildren().filter(e=>!w(e)),e.target,n,!0);t&&(t.focus({preventScroll:!0}),R.getOrCreateInstance(t).show())}_getChildren(){return t.find(De,this._parent)}_getActiveElem(){return this._getChildren().find(e=>this._elemIsActive(e))||null}_setInitialAttributes(e,t){this._setAttributeIfNotExists(e,"role","tablist");for(const e of t)this._setInitialAttributesOnChild(e)}_setInitialAttributesOnChild(e){e=this._getInnerElement(e);const t=this._elemIsActive(e),n=this._getOuterElement(e);e.setAttribute("aria-selected",t),n!==e&&this._setAttributeIfNotExists(n,"role","presentation"),t||e.setAttribute("tabindex","-1"),this._setAttributeIfNotExists(e,"role","tab"),this._setInitialAttributesOnTargetPanel(e)}_setInitialAttributesOnTargetPanel(e){const t=v(e);if(!t)return;this._setAttributeIfNotExists(t,"role","tabpanel"),e.id&&this._setAttributeIfNotExists(t,"aria-labelledby",`#${e.id}`)}_toggleDropDown(e,n){const s=this._getOuterElement(e);if(!s.classList.contains(Cr))return;const o=(e,o)=>{const i=t.findOne(e,s);i&&i.classList.toggle(o,n)};o(Er,N),o(kr,We),s.setAttribute("aria-expanded",n)}_setAttributeIfNotExists(e,t,n){e.hasAttribute(t)||e.setAttribute(t,n)}_elemIsActive(e){return e.classList.contains(N)}_getInnerElement(e){return e.matches(De)?e:t.findOne(De,e)}_getOuterElement(e){return e.closest(Mr)||e}static jQueryInterface(e){return this.each(function(){const t=R.getOrCreateInstance(this);if(typeof e!="string")return;if(t[e]===void 0||e.startsWith("_")||e==="constructor")throw new TypeError(`No method named "${e}"`);t[e]()})}}e.on(document,pr,Kn,function(e){if(["A","AREA"].includes(this.tagName)&&e.preventDefault(),w(this))return;R.getOrCreateInstance(this).show()}),e.on(window,vr,()=>{for(const e of t.find(Dr))R.getOrCreateInstance(e)}),c(R);const Lr="toast",Rr="bs.toast",C=`.${Rr}`,Hr=`mouseover${C}`,Ir=`mouseout${C}`,Br=`focusin${C}`,Vr=`focusout${C}`,$r=`hide${C}`,Wr=`hidden${C}`,Ur=`show${C}`,Kr=`shown${C}`,qr="fade",ts="hide",ge="show",we="showing",Qr={animation:"boolean",autohide:"boolean",delay:"number"},Zr={animation:!0,autohide:!0,delay:5e3};class Ce extends u{constructor(e,t){super(e,t),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get Default(){return Zr}static get DefaultType(){return Qr}static get NAME(){return Lr}show(){const t=e.trigger(this._element,Ur);if(t.defaultPrevented)return;this._clearTimeout(),this._config.animation&&this._element.classList.add(qr);const n=()=>{this._element.classList.remove(we),e.trigger(this._element,Kr),this._maybeScheduleHide()};this._element.classList.remove(ts),ne(this._element),this._element.classList.add(ge,we),this._queueCallback(n,this._element,this._config.animation)}hide(){if(!this.isShown())return;const t=e.trigger(this._element,$r);if(t.defaultPrevented)return;const n=()=>{this._element.classList.add(ts),this._element.classList.remove(we,ge),e.trigger(this._element,Wr)};this._element.classList.add(we),this._queueCallback(n,this._element,this._config.animation)}dispose(){this._clearTimeout(),this.isShown()&&this._element.classList.remove(ge),super.dispose()}isShown(){return this._element.classList.contains(ge)}_maybeScheduleHide(){if(!this._config.autohide)return;if(this._hasMouseInteraction||this._hasKeyboardInteraction)return;this._timeout=setTimeout(()=>{this.hide()},this._config.delay)}_onInteraction(e,t){switch(e.type){case"mouseover":case"mouseout":{this._hasMouseInteraction=t;break}case"focusin":case"focusout":{this._hasKeyboardInteraction=t;break}}if(t){this._clearTimeout();return}const n=e.relatedTarget;if(this._element===n||this._element.contains(n))return;this._maybeScheduleHide()}_setListeners(){e.on(this._element,Hr,e=>this._onInteraction(e,!0)),e.on(this._element,Ir,e=>this._onInteraction(e,!1)),e.on(this._element,Br,e=>this._onInteraction(e,!0)),e.on(this._element,Vr,e=>this._onInteraction(e,!1))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(e){return this.each(function(){const t=Ce.getOrCreateInstance(this,e);if(typeof e=="string"){if(typeof t[e]=="undefined")throw new TypeError(`No method named "${e}"`);t[e](this)}})}}Se(Ce),c(Ce);const ec={Alert:de,Button:fe,Carousel:ie,Collapse:le,Dropdown:h,Modal:B,Offcanvas:A,Popover:ct,ScrollSpy:Ee,Tab:R,Toast:Ce,Tooltip:U};return ec}),function(e){"use strict";e(function(){e('[data-bs-toggle="tooltip"]').tooltip(),e('[data-bs-toggle="popover"]').popover(),e(".popover-dismiss").popover({trigger:"focus"})});function t(e){return e.offset().top+e.outerHeight()}e(function(){var n,o,i,s=e(".js-td-cover");if(!s.length)return;o=t(s),i=e(".js-navbar-scroll").offset().top,n=Math.ceil(e(".js-navbar-scroll").outerHeight()),o-i',t.href="#"+e.id,e.insertAdjacentElement("beforeend",t),e.addEventListener("mouseenter",function(){t.style.visibility="initial"}),e.addEventListener("mouseleave",function(){t.style.visibility="hidden"})}})})}(jQuery),function(e){"use strict";var t={init:function(){e(document).ready(function(){e(document).on("keypress",".td-search input",function(t){if(t.keyCode!==13)return;var n=e(this).val(),s="https://docs.communityhealthtoolkit.org/search/?q="+n;return document.location=s,!1})})}};t.init()}(jQuery) \ No newline at end of file diff --git a/js/main.min.4b0a078f96e3d03882ef5cbf9a5ad9dd5f32c230680c7bf52d13aee7cab453da.js b/js/main.min.4b0a078f96e3d03882ef5cbf9a5ad9dd5f32c230680c7bf52d13aee7cab453da.js new file mode 100644 index 0000000000..2477b7a8e1 --- /dev/null +++ b/js/main.min.4b0a078f96e3d03882ef5cbf9a5ad9dd5f32c230680c7bf52d13aee7cab453da.js @@ -0,0 +1,5 @@ +/*! + * Bootstrap v5.3.3 (https://getbootstrap.com/) + * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */(function(e,t){typeof exports=="object"&&typeof module!="undefined"?module.exports=t():typeof define=="function"&&define.amd?define(t):(e=typeof globalThis!="undefined"?globalThis:e||self,e.bootstrap=t())})(this,function(){"use strict";const C=new Map,pt={set(e,t,n){C.has(e)||C.set(e,new Map);const s=C.get(e);if(!s.has(t)&&s.size!==0){console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(s.keys())[0]}.`);return}s.set(t,n)},get(e,t){return C.has(e)?C.get(e).get(t)||null:null},remove(e,t){if(!C.has(e))return;const n=C.get(e);n.delete(t),n.size===0&&C.delete(e)}},Jr=1e6,Xr=1e3,lt="transitionend",is=e=>(e&&window.CSS&&window.CSS.escape&&(e=e.replace(/#([^\s"#']+)/g,(e,t)=>`#${CSS.escape(t)}`)),e),Gr=e=>e==null?`${e}`:Object.prototype.toString.call(e).match(/\s([a-z]+)/i)[1].toLowerCase(),Yr=e=>{do e+=Math.floor(Math.random()*Jr);while(document.getElementById(e))return e},Pr=e=>{if(!e)return 0;let{transitionDuration:t,transitionDelay:n}=window.getComputedStyle(e);const s=Number.parseFloat(t),o=Number.parseFloat(n);return!s&&!o?0:(t=t.split(",")[0],n=n.split(",")[0],(Number.parseFloat(t)+Number.parseFloat(n))*Xr)},ns=e=>{e.dispatchEvent(new Event(lt))},g=e=>!!e&&typeof e=="object"&&(typeof e.jquery!="undefined"&&(e=e[0]),typeof e.nodeType!="undefined"),w=e=>g(e)?e.jquery?e[0]:e:typeof e=="string"&&e.length>0?document.querySelector(is(e)):null,R=e=>{if(!g(e)||e.getClientRects().length===0)return!1;const n=getComputedStyle(e).getPropertyValue("visibility")==="visible",t=e.closest("details:not([open])");if(!t)return n;if(t!==e){const n=e.closest("summary");if(n&&n.parentNode!==t)return!1;if(n===null)return!1}return n},y=e=>!e||e.nodeType!==Node.ELEMENT_NODE||!!e.classList.contains("disabled")||(typeof e.disabled!="undefined"?e.disabled:e.hasAttribute("disabled")&&e.getAttribute("disabled")!=="false"),es=e=>{if(!document.documentElement.attachShadow)return null;if(typeof e.getRootNode=="function"){const t=e.getRootNode();return t instanceof ShadowRoot?t:null}return e instanceof ShadowRoot?e:e.parentNode?es(e.parentNode):null},le=()=>{},oe=e=>{e.offsetHeight},Jn=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,Ue=[],Nr=e=>{document.readyState==="loading"?(Ue.length||document.addEventListener("DOMContentLoaded",()=>{for(const e of Ue)e()}),Ue.push(e)):e()},c=()=>document.documentElement.dir==="rtl",u=e=>{Nr(()=>{const t=Jn();if(t){const n=e.NAME,s=t.fn[n];t.fn[n]=e.jQueryInterface,t.fn[n].Constructor=e,t.fn[n].noConflict=()=>(t.fn[n]=s,e.jQueryInterface)}})},o=(e,t=[],n=e)=>typeof e=="function"?e(...t):n,Zn=(e,t,n=!0)=>{if(!n){o(e);return}const a=5,r=Pr(t)+a;let s=!1;const i=({target:n})=>{if(n!==t)return;s=!0,t.removeEventListener(lt,i),o(e)};t.addEventListener(lt,i),setTimeout(()=>{s||ns(t)},r)},$e=(e,t,n,s)=>{const i=e.length;let o=e.indexOf(t);return o===-1?!n&&s?e[i-1]:e[0]:(o+=n?1:-1,s&&(o=(o+i)%i),e[Math.max(0,Math.min(o,i-1))])},zr=/[^.]*(?=\..*)\.|.*/,Tr=/\..*/,Ar=/::\d+$/,De={};let qn=1;const Un={mouseenter:"mouseover",mouseleave:"mouseout"},Er=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function Hn(e,t){return t&&`${t}::${qn++}`||e.uidEvent||qn++}function Fn(e){const t=Hn(e);return e.uidEvent=t,De[t]=De[t]||{},De[t]}function xr(t,n){return function s(o){return ht(o,{delegateTarget:t}),s.oneOff&&e.off(t,o.type,n),n.apply(t,[o])}}function Or(t,n,s){return function o(i){const a=t.querySelectorAll(n);for(let{target:r}=i;r&&r!==this;r=r.parentNode)for(const c of a){if(c!==r)continue;return ht(i,{delegateTarget:r}),o.oneOff&&e.off(t,i.type,n,s),s.apply(r,[i])}}}function Sn(e,t,n=null){return Object.values(e).find(e=>e.callable===t&&e.delegationSelector===n)}function An(e,t,n){const o=typeof t=="string",i=o?n:t||n;let s=xn(e);return Er.has(s)||(s=e),[o,i,s]}function Cn(e,t,n,s,o){if(typeof t!="string"||!e)return;let[r,i,c]=An(t,n,s);if(t in Un){const e=e=>function(t){if(!t.relatedTarget||t.relatedTarget!==t.delegateTarget&&!t.delegateTarget.contains(t.relatedTarget))return e.call(this,t)};i=e(i)}const d=Fn(e),u=d[c]||(d[c]={}),l=Sn(u,i,r?n:null);if(l){l.oneOff=l.oneOff&&o;return}const h=Hn(i,t.replace(zr,"")),a=r?Or(e,n,i):xr(e,i);a.delegationSelector=r?n:null,a.callable=i,a.oneOff=o,a.uidEvent=h,u[h]=a,e.addEventListener(c,a,r)}function dt(e,t,n,s,o){const i=Sn(t[n],s,o);if(!i)return;e.removeEventListener(n,i,Boolean(o)),delete t[n][i.uidEvent]}function wr(e,t,n,s){const o=t[n]||{};for(const[a,i]of Object.entries(o))a.includes(s)&&dt(e,t,n,i.callable,i.delegationSelector)}function xn(e){return e=e.replace(Tr,""),Un[e]||e}const e={on(e,t,n,s){Cn(e,t,n,s,!1)},one(e,t,n,s){Cn(e,t,n,s,!0)},off(e,t,n,s){if(typeof t!="string"||!e)return;const[c,a,i]=An(t,n,s),l=i!==t,o=Fn(e),r=o[i]||{},d=t.startsWith(".");if(typeof a!="undefined"){if(!Object.keys(r).length)return;dt(e,o,i,a,c?n:null);return}if(d)for(const n of Object.keys(o))wr(e,o,n,t.slice(1));for(const[s,n]of Object.entries(r)){const a=s.replace(Ar,"");(!l||t.includes(a))&&dt(e,o,i,n.callable,n.delegationSelector)}},trigger(e,t,n){if(typeof t!="string"||!e)return null;const i=Jn(),l=xn(t),d=t!==l;let s=null,a=!0,r=!0,c=!1;d&&i&&(s=i.Event(t,n),i(e).trigger(s),a=!s.isPropagationStopped(),r=!s.isImmediatePropagationStopped(),c=s.isDefaultPrevented());const o=ht(new Event(t,{bubbles:a,cancelable:!0}),n);return c&&o.preventDefault(),r&&e.dispatchEvent(o),o.defaultPrevented&&s&&s.preventDefault(),o}};function ht(e,t={}){for(const[n,s]of Object.entries(t))try{e[n]=s}catch{Object.defineProperty(e,n,{configurable:!0,get(){return s}})}return e}function On(e){if(e==="true")return!0;if(e==="false")return!1;if(e===Number(e).toString())return Number(e);if(e===""||e==="null")return null;if(typeof e!="string")return e;try{return JSON.parse(decodeURIComponent(e))}catch{return e}}function Le(e){return e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`)}const v={setDataAttribute(e,t,n){e.setAttribute(`data-bs-${Le(t)}`,n)},removeDataAttribute(e,t){e.removeAttribute(`data-bs-${Le(t)}`)},getDataAttributes(e){if(!e)return{};const t={},n=Object.keys(e.dataset).filter(e=>e.startsWith("bs")&&!e.startsWith("bsConfig"));for(const o of n){let s=o.replace(/^bs/,"");s=s.charAt(0).toLowerCase()+s.slice(1,s.length),t[s]=On(e.dataset[o])}return t},getDataAttribute(e,t){return On(e.getAttribute(`data-bs-${Le(t)}`))}};class se{static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}_getConfig(e){return e=this._mergeConfigObj(e),e=this._configAfterMerge(e),this._typeCheckConfig(e),e}_configAfterMerge(e){return e}_mergeConfigObj(e,t){const n=g(t)?v.getDataAttribute(t,"config"):{};return{...this.constructor.Default,...typeof n=="object"?n:{},...g(t)?v.getDataAttributes(t):{},...typeof e=="object"?e:{}}}_typeCheckConfig(e,t=this.constructor.DefaultType){for(const[n,s]of Object.entries(t)){const o=e[n],i=g(o)?"element":Gr(o);if(!new RegExp(s).test(i))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${n}" provided type "${i}" but expected type "${s}".`)}}}const _r="5.3.3";class h extends se{constructor(e,t){if(super(),e=w(e),!e)return;this._element=e,this._config=this._getConfig(t),pt.set(this._element,this.constructor.DATA_KEY,this)}dispose(){pt.remove(this._element,this.constructor.DATA_KEY),e.off(this._element,this.constructor.EVENT_KEY);for(const e of Object.getOwnPropertyNames(this))this[e]=null}_queueCallback(e,t,n=!0){Zn(e,t,n)}_getConfig(e){return e=this._mergeConfigObj(e,this._element),e=this._configAfterMerge(e),this._typeCheckConfig(e),e}static getInstance(e){return pt.get(w(e),this.DATA_KEY)}static getOrCreateInstance(e,t={}){return this.getInstance(e)||new this(e,typeof t=="object"?t:null)}static get VERSION(){return _r}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}static eventName(e){return`${e}${this.EVENT_KEY}`}}const tt=e=>{let t=e.getAttribute("data-bs-target");if(!t||t==="#"){let n=e.getAttribute("href");if(!n||!n.includes("#")&&!n.startsWith("."))return null;n.includes("#")&&!n.startsWith("#")&&(n=`#${n.split("#")[1]}`),t=n&&n!=="#"?n.trim():null}return t?t.split(",").map(e=>is(e)).join(","):null},t={find(e,t=document.documentElement){return[].concat(...Element.prototype.querySelectorAll.call(t,e))},findOne(e,t=document.documentElement){return Element.prototype.querySelector.call(t,e)},children(e,t){return[].concat(...e.children).filter(e=>e.matches(t))},parents(e,t){const s=[];let n=e.parentNode.closest(t);for(;n;)s.push(n),n=n.parentNode.closest(t);return s},prev(e,t){let n=e.previousElementSibling;for(;n;){if(n.matches(t))return[n];n=n.previousElementSibling}return[]},next(e,t){let n=e.nextElementSibling;for(;n;){if(n.matches(t))return[n];n=n.nextElementSibling}return[]},focusableChildren(e){const t=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map(e=>`${e}:not([tabindex^="-"])`).join(",");return this.find(t,e).filter(e=>!y(e)&&R(e))},getSelectorFromElement(e){const n=tt(e);return n?t.findOne(n)?n:null:null},getElementFromSelector(e){const n=tt(e);return n?t.findOne(n):null},getMultipleElementsFromSelector(e){const n=tt(e);return n?t.find(n):[]}},_e=(n,s="hide")=>{const i=`click.dismiss${n.EVENT_KEY}`,o=n.NAME;e.on(document,i,`[data-bs-dismiss="${o}"]`,function(e){if(["A","AREA"].includes(this.tagName)&&e.preventDefault(),y(this))return;const i=t.getElementFromSelector(this)||this.closest(`.${o}`),a=n.getOrCreateInstance(i);a[s]()})},yr="alert",jr="bs.alert",jn=`.${jr}`,vr=`close${jn}`,cr=`closed${jn}`,ir="fade",Qa="show";class de extends h{static get NAME(){return yr}close(){const t=e.trigger(this._element,vr);if(t.defaultPrevented)return;this._element.classList.remove(Qa);const n=this._element.classList.contains(ir);this._queueCallback(()=>this._destroyElement(),this._element,n)}_destroyElement(){this._element.remove(),e.trigger(this._element,cr),this.dispose()}static jQueryInterface(e){return this.each(function(){const t=de.getOrCreateInstance(this);if(typeof e!="string")return;if(t[e]===void 0||e.startsWith("_")||e==="constructor")throw new TypeError(`No method named "${e}"`);t[e](this)})}}_e(de,"close"),u(de);const Ga="button",qa="bs.button",Wa=`.${qa}`,Ba=".data-api",Pa="active",fn='[data-bs-toggle="button"]',Ta=`click${Wa}${Ba}`;class fe extends h{static get NAME(){return Ga}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle(Pa))}static jQueryInterface(e){return this.each(function(){const t=fe.getOrCreateInstance(this);e==="toggle"&&t[e]()})}}e.on(document,Ta,fn,e=>{e.preventDefault();const t=e.target.closest(fn),n=fe.getOrCreateInstance(t);n.toggle()}),u(fe);const ga="swipe",P=".bs.swipe",pa=`touchstart${P}`,fa=`touchmove${P}`,ma=`touchend${P}`,ua=`pointerdown${P}`,la=`pointerup${P}`,Qi="touch",Gi="pen",Vi="pointer-event",Bi=40,Ri={endCallback:null,leftCallback:null,rightCallback:null},Ni={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"};class Re extends se{constructor(e,t){if(super(),this._element=e,!e||!Re.isSupported())return;this._config=this._getConfig(t),this._deltaX=0,this._supportPointerEvents=Boolean(window.PointerEvent),this._initEvents()}static get Default(){return Ri}static get DefaultType(){return Ni}static get NAME(){return ga}dispose(){e.off(this._element,P)}_start(e){if(!this._supportPointerEvents){this._deltaX=e.touches[0].clientX;return}this._eventIsPointerPenTouch(e)&&(this._deltaX=e.clientX)}_end(e){this._eventIsPointerPenTouch(e)&&(this._deltaX=e.clientX-this._deltaX),this._handleSwipe(),o(this._config.endCallback)}_move(e){this._deltaX=e.touches&&e.touches.length>1?0:e.touches[0].clientX-this._deltaX}_handleSwipe(){const e=Math.abs(this._deltaX);if(e<=Bi)return;const t=e/this._deltaX;if(this._deltaX=0,!t)return;o(t>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?(e.on(this._element,ua,e=>this._start(e)),e.on(this._element,la,e=>this._end(e)),this._element.classList.add(Vi)):(e.on(this._element,pa,e=>this._start(e)),e.on(this._element,fa,e=>this._move(e)),e.on(this._element,ma,e=>this._end(e)))}_eventIsPointerPenTouch(e){return this._supportPointerEvents&&(e.pointerType===Gi||e.pointerType===Qi)}static isSupported(){return"ontouchstart"in document.documentElement||navigator.maxTouchPoints>0}}const Di="carousel",zi="bs.carousel",_=`.${zi}`,Kt=".data-api",Mi="ArrowLeft",Si="ArrowRight",Ei=500,ie="next",U="prev",K="left",ke="right",ji=`slide${_}`,Be=`slid${_}`,bi=`keydown${_}`,gi=`mouseenter${_}`,li=`mouseleave${_}`,ci=`dragstart${_}`,si=`load${_}${Kt}`,ei=`click${_}${Kt}`,Pt="carousel",Ce="active",Jo="slide",Zo="carousel-item-end",Qo="carousel-item-start",Xo="carousel-item-next",Go="carousel-item-prev",zt=".active",gt=".carousel-item",Ko=zt+gt,Bo=".carousel-item img",Po=".carousel-indicators",No="[data-bs-slide], [data-bs-slide-to]",Do='[data-bs-ride="carousel"]',To={[Mi]:ke,[Si]:K},Fo={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},vo={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"};class ee extends h{constructor(e,n){super(e,n),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=t.findOne(Po,this._element),this._addEventListeners(),this._config.ride===Pt&&this.cycle()}static get Default(){return Fo}static get DefaultType(){return vo}static get NAME(){return Di}next(){this._slide(ie)}nextWhenVisible(){!document.hidden&&R(this._element)&&this.next()}prev(){this._slide(U)}pause(){this._isSliding&&ns(this._element),this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval(()=>this.nextWhenVisible(),this._config.interval)}_maybeEnableCycle(){if(!this._config.ride)return;if(this._isSliding){e.one(this._element,Be,()=>this.cycle());return}this.cycle()}to(t){const n=this._getItems();if(t>n.length-1||t<0)return;if(this._isSliding){e.one(this._element,Be,()=>this.to(t));return}const s=this._getItemIndex(this._getActive());if(s===t)return;const o=t>s?ie:U;this._slide(o,n[t])}dispose(){this._swipeHelper&&this._swipeHelper.dispose(),super.dispose()}_configAfterMerge(e){return e.defaultInterval=e.interval,e}_addEventListeners(){this._config.keyboard&&e.on(this._element,bi,e=>this._keydown(e)),this._config.pause==="hover"&&(e.on(this._element,gi,()=>this.pause()),e.on(this._element,li,()=>this._maybeEnableCycle())),this._config.touch&&Re.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(const n of t.find(Bo,this._element))e.on(n,ci,e=>e.preventDefault());const n=()=>{if(this._config.pause!=="hover")return;this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout(()=>this._maybeEnableCycle(),Ei+this._config.interval)},s={leftCallback:()=>this._slide(this._directionToOrder(K)),rightCallback:()=>this._slide(this._directionToOrder(ke)),endCallback:n};this._swipeHelper=new Re(this._element,s)}_keydown(e){if(/input|textarea/i.test(e.target.tagName))return;const t=To[e.key];t&&(e.preventDefault(),this._slide(this._directionToOrder(t)))}_getItemIndex(e){return this._getItems().indexOf(e)}_setActiveIndicatorElement(e){if(!this._indicatorsElement)return;const s=t.findOne(zt,this._indicatorsElement);s.classList.remove(Ce),s.removeAttribute("aria-current");const n=t.findOne(`[data-bs-slide-to="${e}"]`,this._indicatorsElement);n&&(n.classList.add(Ce),n.setAttribute("aria-current","true"))}_updateInterval(){const e=this._activeElement||this._getActive();if(!e)return;const t=Number.parseInt(e.getAttribute("data-bs-interval"),10);this._config.interval=t||this._config.defaultInterval}_slide(t,n=null){if(this._isSliding)return;const o=this._getActive(),a=t===ie,s=n||$e(this._getItems(),o,a,this._config.wrap);if(s===o)return;const c=this._getItemIndex(s),l=n=>e.trigger(this._element,n,{relatedTarget:s,direction:this._orderToDirection(t),from:this._getItemIndex(o),to:c}),d=l(ji);if(d.defaultPrevented)return;if(!o||!s)return;const u=Boolean(this._interval);this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(c),this._activeElement=s;const i=a?Qo:Zo,r=a?Xo:Go;s.classList.add(r),oe(s),o.classList.add(i),s.classList.add(i);const h=()=>{s.classList.remove(i,r),s.classList.add(Ce),o.classList.remove(Ce,r,i),this._isSliding=!1,l(Be)};this._queueCallback(h,o,this._isAnimated()),u&&this.cycle()}_isAnimated(){return this._element.classList.contains(Jo)}_getActive(){return t.findOne(Ko,this._element)}_getItems(){return t.find(gt,this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(e){return c()?e===K?U:ie:e===K?ie:U}_orderToDirection(e){return c()?e===U?K:ke:e===U?ke:K}static jQueryInterface(e){return this.each(function(){const t=ee.getOrCreateInstance(this,e);if(typeof e=="number"){t.to(e);return}if(typeof e=="string"){if(t[e]===void 0||e.startsWith("_")||e==="constructor")throw new TypeError(`No method named "${e}"`);t[e]()}})}}e.on(document,ei,No,function(e){const s=t.getElementFromSelector(this);if(!s||!s.classList.contains(Pt))return;e.preventDefault();const n=ee.getOrCreateInstance(s),o=this.getAttribute("data-bs-slide-to");if(o){n.to(o),n._maybeEnableCycle();return}if(v.getDataAttribute(this,"slide")==="next"){n.next(),n._maybeEnableCycle();return}n.prev(),n._maybeEnableCycle()}),e.on(window,si,()=>{const e=t.find(Do);for(const t of e)ee.getOrCreateInstance(t)}),u(ee);const rs="collapse",po="bs.collapse",J=`.${po}`,co=".data-api",ao=`show${J}`,io=`shown${J}`,Js=`hide${J}`,Qs=`hidden${J}`,Gs=`click${J}${co}`,ct="show",L="collapse",pe="collapsing",Ys="collapsed",Ks=`:scope .${L} .${L}`,Us="collapse-horizontal",Ws="width",$s="height",Vs=".collapse.show, .collapse.collapsing",nt='[data-bs-toggle="collapse"]',Bs={parent:null,toggle:!0},Is={parent:"(null|element)",toggle:"boolean"};class te extends h{constructor(e,n){super(e,n),this._isTransitioning=!1,this._triggerArray=[];const s=t.find(nt);for(const e of s){const n=t.getSelectorFromElement(e),o=t.find(n).filter(e=>e===this._element);n!==null&&o.length&&this._triggerArray.push(e)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return Bs}static get DefaultType(){return Is}static get NAME(){return rs}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let n=[];if(this._config.parent&&(n=this._getFirstLevelChildren(Vs).filter(e=>e!==this._element).map(e=>te.getOrCreateInstance(e,{toggle:!1}))),n.length&&n[0]._isTransitioning)return;const s=e.trigger(this._element,ao);if(s.defaultPrevented)return;for(const e of n)e.hide();const t=this._getDimension();this._element.classList.remove(L),this._element.classList.add(pe),this._element.style[t]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const o=()=>{this._isTransitioning=!1,this._element.classList.remove(pe),this._element.classList.add(L,ct),this._element.style[t]="",e.trigger(this._element,io)},i=t[0].toUpperCase()+t.slice(1),a=`scroll${i}`;this._queueCallback(o,this._element,!0),this._element.style[t]=`${this._element[a]}px`}hide(){if(this._isTransitioning||!this._isShown())return;const s=e.trigger(this._element,Js);if(s.defaultPrevented)return;const n=this._getDimension();this._element.style[n]=`${this._element.getBoundingClientRect()[n]}px`,oe(this._element),this._element.classList.add(pe),this._element.classList.remove(L,ct);for(const e of this._triggerArray){const n=t.getElementFromSelector(e);n&&!this._isShown(n)&&this._addAriaAndCollapsedClass([e],!1)}this._isTransitioning=!0;const o=()=>{this._isTransitioning=!1,this._element.classList.remove(pe),this._element.classList.add(L),e.trigger(this._element,Qs)};this._element.style[n]="",this._queueCallback(o,this._element,!0)}_isShown(e=this._element){return e.classList.contains(ct)}_configAfterMerge(e){return e.toggle=Boolean(e.toggle),e.parent=w(e.parent),e}_getDimension(){return this._element.classList.contains(Us)?Ws:$s}_initializeChildren(){if(!this._config.parent)return;const e=this._getFirstLevelChildren(nt);for(const n of e){const s=t.getElementFromSelector(n);s&&this._addAriaAndCollapsedClass([n],this._isShown(s))}}_getFirstLevelChildren(e){const n=t.find(Ks,this._config.parent);return t.find(e,this._config.parent).filter(e=>!n.includes(e))}_addAriaAndCollapsedClass(e,t){if(!e.length)return;for(const n of e)n.classList.toggle(Ys,!t),n.setAttribute("aria-expanded",t)}static jQueryInterface(e){const t={};return typeof e=="string"&&/show|hide/.test(e)&&(t.toggle=!1),this.each(function(){const n=te.getOrCreateInstance(this,t);if(typeof e=="string"){if(typeof n[e]=="undefined")throw new TypeError(`No method named "${e}"`);n[e]()}})}}e.on(document,Gs,nt,function(e){(e.target.tagName==="A"||e.delegateTarget&&e.delegateTarget.tagName==="A")&&e.preventDefault();for(const e of t.getMultipleElementsFromSelector(this))te.getOrCreateInstance(e,{toggle:!1}).toggle()}),u(te);var k,A,Q,s="top",Nn,In,ae,Yn,Xn,ot,Tt,Ft,St,At,je,a="bottom",i="right",n="left",Ee="auto",Y=[s,a,i,n],T="start",q="end",Vt="clippingParents",Ve="viewport",I="popper",Ut="reference",Te=Y.reduce(function(e,t){return e.concat([t+"-"+T,t+"-"+q])},[]),Xe=[].concat(Y,[Ee]).reduce(function(e,t){return e.concat([t,t+"-"+T,t+"-"+q])},[]),Yt="beforeRead",Gt="read",Xt="afterRead",Qt="beforeMain",Zt="main",Jt="afterMain",en="beforeWrite",tn="write",nn="afterWrite",sn=[Yt,Gt,Xt,Qt,Zt,Jt,en,tn,nn];function f(e){return e?(e.nodeName||"").toLowerCase():null}function r(e){if(e==null)return window;if(e.toString()!=="[object Window]"){var t=e.ownerDocument;return t?t.defaultView||window:window}return e}function D(e){var t=r(e).Element;return e instanceof t||e instanceof Element}function l(e){var t=r(e).HTMLElement;return e instanceof t||e instanceof HTMLElement}function Me(e){if(typeof ShadowRoot=="undefined")return!1;var t=r(e).ShadowRoot;return e instanceof t||e instanceof ShadowRoot}function Hs(e){var t=e.state;Object.keys(t.elements).forEach(function(e){var o=t.styles[e]||{},s=t.attributes[e]||{},n=t.elements[e];if(!l(n)||!f(n))return;Object.assign(n.style,o),Object.keys(s).forEach(function(e){var t=s[e];t===!1?n.removeAttribute(e):n.setAttribute(e,t===!0?"":t)})})}function Ps(e){var t=e.state,n={popper:{position:t.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(t.elements.popper.style,n.popper),t.styles=n,t.elements.arrow&&Object.assign(t.elements.arrow.style,n.arrow),function(){Object.keys(t.elements).forEach(function(e){var s=t.elements[e],o=t.attributes[e]||{},i=Object.keys(t.styles.hasOwnProperty(e)?t.styles[e]:n[e]),a=i.reduce(function(e,t){return e[t]="",e},{});if(!l(s)||!f(s))return;Object.assign(s.style,a),Object.keys(o).forEach(function(e){s.removeAttribute(e)})})}}const st={name:"applyStyles",enabled:!0,phase:"write",fn:Hs,effect:Ps,requires:["computeStyles"]};function p(e){return e.split("-")[0]}k=Math.max,Q=Math.min,A=Math.round;function et(){var e=navigator.userAgentData;return e!=null&&e.brands&&Array.isArray(e.brands)?e.brands.map(function(e){return e.brand+"/"+e.version}).join(" "):navigator.userAgent}function bn(){return!/^((?!chrome|android).)*safari/i.test(et())}function X(e,t,n){t===void 0&&(t=!1),n===void 0&&(n=!1),s=e.getBoundingClientRect(),o=1,i=1,t&&l(e)&&(o=e.offsetWidth>0?A(s.width)/e.offsetWidth||1:1,i=e.offsetHeight>0?A(s.height)/e.offsetHeight||1:1);var s,o,i,f=D(e)?r(e):window,a=f.visualViewport,u=!bn()&&n,c=(s.left+(u&&a?a.offsetLeft:0))/o,d=(s.top+(u&&a?a.offsetTop:0))/i,h=s.width/o,m=s.height/i;return{width:h,height:m,top:d,right:c+h,bottom:d+m,left:c,x:c,y:d}}function ut(e){var t=X(e),n=e.offsetWidth,s=e.offsetHeight;return Math.abs(t.width-n)<=1&&(n=t.width),Math.abs(t.height-s)<=1&&(s=t.height),{x:e.offsetLeft,y:e.offsetTop,width:n,height:s}}function _n(e,t){var n,s=t.getRootNode&&t.getRootNode();if(e.contains(t))return!0;if(s&&Me(s)){n=t;do{if(n&&e.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function j(e){return r(e).getComputedStyle(e)}function Rs(e){return["table","td","th"].indexOf(f(e))>=0}function E(e){return((D(e)?e.ownerDocument:e.document)||window.document).documentElement}function ge(e){return f(e)==="html"?e:e.assignedSlot||e.parentNode||(Me(e)?e.host:null)||E(e)}function En(e){return!l(e)||j(e).position==="fixed"?null:e.offsetParent}function As(e){var t,n,o,s=/firefox/i.test(et()),i=/Trident/i.test(et());if(i&&l(e)&&(o=j(e),o.position==="fixed"))return null;for(t=ge(e),Me(t)&&(t=t.host);l(t)&&["html","body"].indexOf(f(t))<0;){if(n=j(t),n.transform!=="none"||n.perspective!=="none"||n.contain==="paint"||["transform","perspective"].indexOf(n.willChange)!==-1||s&&n.willChange==="filter"||s&&n.filter&&n.filter!=="none")return t;t=t.parentNode}return null}function ce(e){for(var n=r(e),t=En(e);t&&Rs(t)&&j(t).position==="static";)t=En(t);return t&&(f(t)==="html"||f(t)==="body"&&j(t).position==="static")?n:t||As(e)||n}function Qe(e){return["top","bottom"].indexOf(e)>=0?"x":"y"}function re(e,t,n){return k(e,Q(t,n))}function Cs(e,t,n){var s=re(e,t,n);return s>n?n:s}function Tn(){return{top:0,right:0,bottom:0,left:0}}function zn(e){return Object.assign({},Tn(),e)}function Dn(e,t){return t.reduce(function(t,n){return t[n]=e,t},{})}Nn=function(t,n){return t=typeof t=="function"?t(Object.assign({},n.rects,{placement:n.placement})):t,zn(typeof t!="number"?t:Dn(t,Y))};function Os(e){var r,c,d,u,f,g,v,b,j,y,_,O,x,C,E,t=e.state,S=e.name,A=e.options,h=t.elements.arrow,m=t.modifiersData.popperOffsets,w=p(t.placement),o=Qe(w),k=[n,i].indexOf(w)>=0,l=k?"height":"width";if(!h||!m)return;g=Nn(A.padding,t),v=ut(h),b=o==="y"?s:n,j=o==="y"?a:i,y=t.rects.reference[l]+t.rects.reference[o]-m[o]-t.rects.popper[l],_=m[o]-t.rects.reference[o],c=ce(h),f=c?o==="y"?c.clientHeight||0:c.clientWidth||0:0,O=y/2-_/2,x=g[b],C=f-v[l]-g[j],u=f/2-v[l]/2+O,d=re(x,u,C),E=o,t.modifiersData[S]=(r={},r[E]=d,r.centerOffset=d-u,r)}function ys(e){var n=e.state,o=e.options,s=o.element,t=s===void 0?"[data-popper-arrow]":s;if(t==null)return;if(typeof t=="string"&&(t=n.elements.popper.querySelector(t),!t))return;if(!_n(n.elements.popper,t))return;n.elements.arrow=t}const Pn={name:"arrow",enabled:!0,phase:"main",fn:Os,effect:ys,requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function V(e){return e.split("-")[1]}In={top:"auto",right:"auto",bottom:"auto",left:"auto"};function bs(e,t){var s=e.x,o=e.y,n=t.devicePixelRatio||1;return{x:A(s*n)/n||0,y:A(o*n)/n||0}}function Vn(e){var c,u,h,p,g,b,y,T,z,f=e.popper,N=e.popperRect,d=e.placement,A=e.variation,m=e.offsets,x=e.position,v=e.gpuAcceleration,S=e.adaptive,_=e.roundOffsets,M=e.isFixed,L=m.x,t=L===void 0?0:L,D=m.y,o=D===void 0?0:D,C=typeof _=="function"?_({x:t,y:o}):{x:t,y:o},t=C.x,o=C.y,F=m.hasOwnProperty("x"),k=m.hasOwnProperty("y"),w=n,O=s,l=window;return S&&(c=ce(f),g="clientHeight",y="clientWidth",c===r(f)&&(c=E(f),j(c).position!=="static"&&x==="absolute"&&(g="scrollHeight",y="scrollWidth")),c=c,(d===s||(d===n||d===i)&&A===q)&&(O=a,T=M&&c===l&&l.visualViewport?l.visualViewport.height:c[g],o-=T-N.height,o*=v?1:-1),(d===n||(d===s||d===a)&&A===q)&&(w=i,z=M&&c===l&&l.visualViewport?l.visualViewport.width:c[y],t-=z-N.width,t*=v?1:-1)),p=Object.assign({position:x},S&&In),b=_===!0?bs({x:t,y:o},r(f)):{x:t,y:o},t=b.x,o=b.y,v?Object.assign({},p,(h={},h[O]=k?"0":"",h[w]=F?"0":"",h.transform=(l.devicePixelRatio||1)<=1?"translate("+t+"px, "+o+"px)":"translate3d("+t+"px, "+o+"px, 0)",h)):Object.assign({},p,(u={},u[O]=k?o+"px":"",u[w]=F?t+"px":"",u.transform="",u))}function vs(e){var t=e.state,n=e.options,s=n.gpuAcceleration,c=s===void 0||s,o=n.adaptive,l=o===void 0||o,i=n.roundOffsets,a=i===void 0||i,r={placement:p(t.placement),variation:V(t.placement),popper:t.elements.popper,popperRect:t.rects.popper,gpuAcceleration:c,isFixed:t.options.strategy==="fixed"};t.modifiersData.popperOffsets!=null&&(t.styles.popper=Object.assign({},t.styles.popper,Vn(Object.assign({},r,{offsets:t.modifiersData.popperOffsets,position:t.options.strategy,adaptive:l,roundOffsets:a})))),t.modifiersData.arrow!=null&&(t.styles.arrow=Object.assign({},t.styles.arrow,Vn(Object.assign({},r,{offsets:t.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:a})))),t.attributes.popper=Object.assign({},t.attributes.popper,{"data-popper-placement":t.placement})}const Fe={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:vs,data:{}};ae={passive:!0};function gs(e){var n=e.state,t=e.instance,s=e.options,o=s.scroll,i=o===void 0||o,a=s.resize,c=a===void 0||a,l=r(n.elements.popper),d=[].concat(n.scrollParents.reference,n.scrollParents.popper);return i&&d.forEach(function(e){e.addEventListener("scroll",t.update,ae)}),c&&l.addEventListener("resize",t.update,ae),function(){i&&d.forEach(function(e){e.removeEventListener("scroll",t.update,ae)}),c&&l.removeEventListener("resize",t.update,ae)}}const ze={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:gs,data:{}};Yn={left:"right",right:"left",bottom:"top",top:"bottom"};function Ae(e){return e.replace(/left|right|bottom|top/g,function(e){return Yn[e]})}Xn={start:"end",end:"start"};function Qn(e){return e.replace(/start|end/g,function(e){return Xn[e]})}function We(e){var t=r(e),n=t.pageXOffset,s=t.pageYOffset;return{scrollLeft:n,scrollTop:s}}function Ke(e){return X(E(e)).left+We(e).scrollLeft}function hs(e,t){var s,d=r(e),o=E(e),n=d.visualViewport,i=o.clientWidth,a=o.clientHeight,c=0,l=0;return n&&(i=n.width,a=n.height,s=bn(),(s||!s&&t==="fixed")&&(c=n.offsetLeft,l=n.offsetTop)),{width:i,height:a,x:c+Ke(e),y:l}}function us(e){var s,n=E(e),o=We(e),t=(s=e.ownerDocument)==null?void 0:s.body,i=k(n.scrollWidth,n.clientWidth,t?t.scrollWidth:0,t?t.clientWidth:0),r=k(n.scrollHeight,n.clientHeight,t?t.scrollHeight:0,t?t.clientHeight:0),a=-o.scrollLeft+Ke(e),c=-o.scrollTop;return j(t||n).direction==="rtl"&&(a+=k(n.clientWidth,t?t.clientWidth:0)-i),{width:i,height:r,x:a,y:c}}function Je(e){var t=j(e),n=t.overflow,s=t.overflowX,o=t.overflowY;return/auto|scroll|overlay|hidden/.test(n+o+s)}function ss(e){return["html","body","#document"].indexOf(f(e))>=0?e.ownerDocument.body:l(e)&&Je(e)?e:ss(ge(e))}function ne(e,t){t===void 0&&(t=[]);var s,n=ss(e),o=n===((s=e.ownerDocument)==null?void 0:s.body),i=r(n),a=o?[i].concat(i.visualViewport||[],Je(n)?n:[]):n,c=t.concat(a);return o?c:c.concat(ne(ge(a)))}function rt(e){return Object.assign({},e,{left:e.x,top:e.y,right:e.x+e.width,bottom:e.y+e.height})}function Fi(e,t){var n=X(e,!1,t==="fixed");return n.top=n.top+e.clientTop,n.left=n.left+e.clientLeft,n.bottom=n.top+e.clientHeight,n.right=n.left+e.clientWidth,n.width=e.clientWidth,n.height=e.clientHeight,n.x=n.left,n.y=n.top,n}function Mt(e,t,n){return t===Ve?rt(hs(e,n)):D(t)?Fi(t,n):rt(us(E(e)))}function ls(e){var n=ne(ge(e)),s=["absolute","fixed"].indexOf(j(e).position)>=0,t=s&&l(e)?ce(e):e;return D(t)?n.filter(function(e){return D(e)&&_n(e,t)&&f(e)!=="body"}):[]}function ds(e,t,n,s){var a=t==="clippingParents"?ls(e):[].concat(t),i=[].concat(a,[n]),r=i[0],o=i.reduce(function(t,n){var o=Mt(e,n,s);return t.top=k(o.top,t.top),t.right=Q(o.right,t.right),t.bottom=Q(o.bottom,t.bottom),t.left=k(o.left,t.left),t},Mt(e,r,s));return o.width=o.right-o.left,o.height=o.bottom-o.top,o.x=o.left,o.y=o.top,o}function ts(e){var o,r,l,t=e.reference,c=e.element,d=e.placement,u=d?p(d):null,f=d?V(d):null,h=t.x+t.width/2-c.width/2,m=t.y+t.height/2-c.height/2;switch(u){case s:o={x:h,y:t.y-c.height};break;case a:o={x:h,y:t.y+t.height};break;case i:o={x:t.x+t.width,y:m};break;case n:o={x:t.x-c.width,y:m};break;default:o={x:t.x,y:t.y}}if(r=u?Qe(u):null,r!=null)switch(l=r==="y"?"height":"width",f){case T:o[r]=o[r]-(t[l]/2-c[l]/2);break;case q:o[r]=o[r]+(t[l]/2-c[l]/2);break}return o}function N(e,t){t===void 0&&(t={});var _,n=t,v=n.placement,j=v===void 0?e.placement:v,f=n.strategy,T=f===void 0?e.strategy:f,p=n.boundary,C=p===void 0?Vt:p,O=n.rootBoundary,F=O===void 0?Ve:O,x=n.elementContext,c=x===void 0?I:x,m=n.altBoundary,M=m!==void 0&&m,b=n.padding,d=b===void 0?0:b,o=zn(typeof d!="number"?d:Dn(d,Y)),S=c===I?Ut:I,w=e.rects.popper,h=e.elements[M?S:c],r=ds(D(h)?h:h.contextElement||E(e.elements.popper),C,F,T),y=X(e.elements.reference),k=ts({reference:y,element:w,strategy:"absolute",placement:j}),A=rt(Object.assign({},w,k)),l=c===I?A:y,u={top:r.top-l.top+o.top,bottom:l.bottom-r.bottom+o.bottom,left:r.left-l.left+o.left,right:l.right-r.right+o.right},g=e.modifiersData.offset;return c===I&&g&&(_=g[j],Object.keys(u).forEach(function(e){var t=[i,a].indexOf(e)>=0?1:-1,n=[s,a].indexOf(e)>=0?"y":"x";u[e]+=_[n]*t})),u}function ms(e,t){t===void 0&&(t={});var s,n=t,c=n.placement,l=n.boundary,d=n.rootBoundary,u=n.padding,h=n.flipVariations,i=n.allowedAutoPlacements,m=i===void 0?Xe:i,a=V(c),r=a?h?Te:Te.filter(function(e){return V(e)===a}):Y,o=r.filter(function(e){return m.indexOf(e)>=0});return o.length===0&&(o=r),s=o.reduce(function(t,n){return t[n]=N(e,{placement:n,boundary:l,rootBoundary:d,padding:u})[p(n)],t},{}),Object.keys(s).sort(function(e,t){return s[e]-s[t]})}function fs(e){if(p(e)===Ee)return[];var t=Ae(e);return[Qn(e),t,Qn(t)]}function ps(e){var r,c,l,u,h,g,v,y,_,x,E,k,z,t=e.state,o=e.options,C=e.name;if(t.modifiersData[C]._skip)return;for(var M=o.mainAxis,I=M===void 0||M,D=o.altAxis,P=D===void 0||D,R=o.fallbackPlacements,L=o.padding,w=o.boundary,O=o.rootBoundary,B=o.altBoundary,F=o.flipVariations,j=F===void 0||F,$=o.allowedAutoPlacements,d=t.options.placement,K=p(d),H=K===d,q=R||(H||!j?[Ae(d)]:fs(d)),f=[d].concat(q).reduce(function(e,n){return e.concat(p(n)===Ee?ms(t,{placement:n,boundary:w,rootBoundary:O,padding:L,flipVariations:j,allowedAutoPlacements:$}):n)},[]),U=t.rects.reference,W=t.rects.popper,A=new Map,S=!0,m=f[0],b=0;b=0,_=y?"width":"height",h=N(t,{placement:r,boundary:w,rootBoundary:O,altBoundary:B,padding:L}),l=y?g?i:n:g?a:s,U[_]>W[_]&&(l=Ae(l)),z=Ae(l),c=[],I&&c.push(h[v]<=0),P&&c.push(h[l]<=0,h[z]<=0),c.every(function(e){return e})){m=r,S=!1;break}A.set(r,c)}if(S)for(k=j?3:1,E=function(t){var n=f.find(function(e){var n=A.get(e);if(n)return n.slice(0,t).every(function(e){return e})});if(n)return m=n,"break"},u=k;u>0;u--)if(x=E(u),x==="break")break;t.placement!==m&&(t.modifiersData[C]._skip=!0,t.placement=m,t.reset=!0)}const Kn={name:"flip",enabled:!0,phase:"main",fn:ps,requiresIfExists:["offset"],data:{_skip:!1}};function $n(e,t,n){return n===void 0&&(n={x:0,y:0}),{top:e.top-t.height-n.y,right:e.right-t.width+n.x,bottom:e.bottom-t.height+n.y,left:e.left-t.width-n.x}}function Bn(e){return[s,i,a,n].some(function(t){return e[t]>=0})}function js(e){var t=e.state,a=e.name,r=t.rects.reference,c=t.rects.popper,l=t.modifiersData.preventOverflow,d=N(t,{elementContext:"reference"}),u=N(t,{altBoundary:!0}),n=$n(d,r),s=$n(u,c,l),o=Bn(n),i=Bn(s);t.modifiersData[a]={referenceClippingOffsets:n,popperEscapeOffsets:s,isReferenceHidden:o,hasPopperEscaped:i},t.attributes.popper=Object.assign({},t.attributes.popper,{"data-popper-reference-hidden":o,"data-popper-escaped":i})}const Rn={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:js};function _s(e,t,o){var c=p(e),d=[n,s].indexOf(c)>=0?-1:1,l=typeof o=="function"?o(Object.assign({},t,{placement:e})):o,a=l[0],r=l[1],a=a||0,r=(r||0)*d;return[n,i].indexOf(c)>=0?{x:r,y:a}:{x:a,y:r}}function ws(e){var t=e.state,i=e.options,a=e.name,n=i.offset,r=n===void 0?[0,0]:n,s=Xe.reduce(function(e,n){return e[n]=_s(n,t.rects,r),e},{}),o=s[t.placement],c=o.x,l=o.y;t.modifiersData.popperOffsets!=null&&(t.modifiersData.popperOffsets.x+=c,t.modifiersData.popperOffsets.y+=l),t.modifiersData[a]=s}const Ln={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:ws};function xs(e){var t=e.state,n=e.name;t.modifiersData[n]=ts({reference:t.rects.reference,element:t.rects.popper,strategy:"absolute",placement:t.placement})}const He={name:"popperOffsets",enabled:!0,phase:"read",fn:xs,data:{}};function Es(e){return e==="x"?"y":"x"}function ks(e){var r,c,h,f,v,w,x,E,A,M,F,z,D,R,P,H,I,B,$,W,U,K,q,Y,G,X,Z,J,t=e.state,l=e.options,be=e.name,fe,ue,te,ne,oe,ae,le,me,pe=l.mainAxis,ge=pe===void 0||pe,se=l.altAxis,we=se!==void 0&&se,_e=l.boundary,ye=l.rootBoundary,ve=l.altBoundary,je=l.padding,de=l.tether,d=de===void 0||de,ie=l.tetherOffset,S=ie===void 0?0:ie,O=N(t,{boundary:_e,rootBoundary:ye,padding:je,altBoundary:ve}),ee=p(t.placement),C=V(t.placement),he=!C,o=Qe(ee),j=Es(o),b=t.modifiersData.popperOffsets,u=t.rects.reference,g=t.rects.popper,_=typeof S=="function"?S(Object.assign({},t.rects,{placement:t.placement})):S,m=typeof _=="number"?{mainAxis:_,altAxis:_}:Object.assign({mainAxis:0,altAxis:0},_),y=t.modifiersData.offset?t.modifiersData.offset[t.placement]:null,L={x:0,y:0};if(!b)return;ge&&(P=o==="y"?s:n,H=o==="y"?a:i,r=o==="y"?"height":"width",h=b[o],$=h+O[P],W=h-O[H],U=d?-g[r]/2:0,J=C===T?u[r]:g[r],Z=C===T?-g[r]:-u[r],Y=t.elements.arrow,ue=d&&Y?ut(Y):{width:0,height:0},E=t.modifiersData["arrow#persistent"]?t.modifiersData["arrow#persistent"].padding:Tn(),q=E[P],K=E[H],v=re(0,u[r],ue[r]),te=he?u[r]/2-U-v-q-m.mainAxis:J-v-q-m.mainAxis,ne=he?-u[r]/2+U+v+K+m.mainAxis:Z+v+K+m.mainAxis,x=t.elements.arrow&&ce(t.elements.arrow),oe=x?o==="y"?x.clientTop||0:x.clientLeft||0:0,B=(fe=y?.[o])!=null?fe:0,ae=h+te-B-oe,le=h+ne-B,I=re(d?Q($,ae):$,h,d?k(W,le):W),b[o]=I,L[o]=I-h),we&&(G=o==="x"?s:n,me=o==="x"?a:i,c=b[j],f=j==="y"?"height":"width",R=c+O[G],D=c-O[me],w=[s,n].indexOf(ee)!==-1,z=(X=y?.[j])!=null?X:0,F=w?R:c-u[f]-g[f]-z+m.altAxis,M=w?c+u[f]+g[f]-z-m.altAxis:D,A=d&&w?Cs(F,c,M):re(d?F:R,c,d?M:D),b[j]=A,L[j]=A-c),t.modifiersData[be]=L}const kn={name:"preventOverflow",enabled:!0,phase:"main",fn:ks,requiresIfExists:["offset"]};function Ss(e){return{scrollLeft:e.scrollLeft,scrollTop:e.scrollTop}}function Ms(e){return e===r(e)||!l(e)?We(e):Ss(e)}function Fs(e){var t=e.getBoundingClientRect(),n=A(t.width)/e.offsetWidth||1,s=A(t.height)/e.offsetHeight||1;return n!==1||s!==1}function Ts(e,t,n){n===void 0&&(n=!1);var r=l(t),c=l(t)&&Fs(t),i=E(t),o=X(e,c,n),a={scrollLeft:0,scrollTop:0},s={x:0,y:0};return(r||!r&&!n)&&((f(t)!=="body"||Je(i))&&(a=Ms(t)),l(t)?(s=X(t,!0),s.x+=t.clientLeft,s.y+=t.clientTop):i&&(s.x=Ke(i))),{x:o.left+a.scrollLeft-s.x,y:o.top+a.scrollTop-s.y,width:o.width,height:o.height}}function zs(e){var n=new Map,t=new Set,s=[];e.forEach(function(e){n.set(e.name,e)});function o(e){t.add(e.name);var i=[].concat(e.requires||[],e.requiresIfExists||[]);i.forEach(function(e){if(!t.has(e)){var s=n.get(e);s&&o(s)}}),s.push(e)}return e.forEach(function(e){t.has(e.name)||o(e)}),s}function Ds(e){var t=zs(e);return sn.reduce(function(e,n){return e.concat(t.filter(function(e){return e.phase===n}))},[])}function Ns(e){var t;return function(){return t||(t=new Promise(function(n){Promise.resolve().then(function(){t=void 0,n(e())})})),t}}function Ls(e){var t=e.reduce(function(e,t){var n=e[t.name];return e[t.name]=n?Object.assign({},n,t,{options:Object.assign({},n.options,t.options),data:Object.assign({},n.data,t.data)}):t,e},{});return Object.keys(t).map(function(e){return t[e]})}ot={placement:"bottom",modifiers:[],strategy:"absolute"};function un(){for(var t=arguments.length,n=new Array(t),e=0;eNumber.parseInt(e,10)):typeof e=="function"?t=>e(t,this._element):e}_getPopperConfig(){const e={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return(this._inNavbar||this._config.display==="static")&&(v.setDataAttribute(this._menu,"popper","static"),e.modifiers=[{name:"applyStyles",enabled:!1}]),{...e,...o(this._config.popperConfig,[e])}}_selectMenuItem({key:e,target:n}){const s=t.find(yo,this._menu).filter(e=>R(e));if(!s.length)return;$e(s,n,e===bt,!s.includes(n)).focus()}static jQueryInterface(e){return this.each(function(){const t=m.getOrCreateInstance(this,e);if(typeof e!="string")return;if(typeof t[e]=="undefined")throw new TypeError(`No method named "${e}"`);t[e]()})}static clearMenus(e){if(e.button===eo||e.type==="keyup"&&e.key!==jt)return;const n=t.find(go);for(const a of n){const t=m.getInstance(a);if(!t||t._config.autoClose===!1)continue;const s=e.composedPath(),o=s.includes(t._menu);if(s.includes(t._element)||t._config.autoClose==="inside"&&!o||t._config.autoClose==="outside"&&o)continue;if(t._menu.contains(e.target)&&(e.type==="keyup"&&e.key===jt||/input|select|option|textarea|form/i.test(e.target.tagName)))continue;const i={relatedTarget:t._element};e.type==="click"&&(i.clickEvent=e),t._completeHide(i)}}static dataApiKeydownHandler(e){const a=/input|textarea/i.test(e.target.tagName),s=e.key===Xs,o=[Zs,bt].includes(e.key);if(!o&&!s)return;if(a&&!s)return;e.preventDefault();const i=this.matches(z)?this:t.prev(this,z)[0]||t.next(this,z)[0]||t.findOne(z,e.delegateTarget.parentNode),n=m.getOrCreateInstance(i);if(o){e.stopPropagation(),n.show(),n._selectMenuItem(e);return}n._isShown()&&(e.stopPropagation(),n.hide(),i.focus())}}e.on(document,yt,z,m.dataApiKeydownHandler),e.on(document,yt,me,m.dataApiKeydownHandler),e.on(document,vt,m.clearMenus),e.on(document,ro,m.clearMenus),e.on(document,vt,z,function(e){e.preventDefault(),m.getOrCreateInstance(this).toggle()}),u(m);const _t="backdrop",zo="fade",wt="show",Ot=`mousedown.bs.${_t}`,Lo={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},Ro={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"};class xt extends se{constructor(e){super(),this._config=this._getConfig(e),this._isAppended=!1,this._element=null}static get Default(){return Lo}static get DefaultType(){return Ro}static get NAME(){return _t}show(e){if(!this._config.isVisible){o(e);return}this._append();const t=this._getElement();this._config.isAnimated&&oe(t),t.classList.add(wt),this._emulateAnimation(()=>{o(e)})}hide(e){if(!this._config.isVisible){o(e);return}this._getElement().classList.remove(wt),this._emulateAnimation(()=>{this.dispose(),o(e)})}dispose(){if(!this._isAppended)return;e.off(this._element,Ot),this._element.remove(),this._isAppended=!1}_getElement(){if(!this._element){const e=document.createElement("div");e.className=this._config.className,this._config.isAnimated&&e.classList.add(zo),this._element=e}return this._element}_configAfterMerge(e){return e.rootElement=w(e.rootElement),e}_append(){if(this._isAppended)return;const t=this._getElement();this._config.rootElement.append(t),e.on(t,Ot,()=>{o(this._config.clickCallback)}),this._isAppended=!0}_emulateAnimation(e){Zn(e,this._getElement(),this._config.isAnimated)}}const Ho="focustrap",Io="bs.focustrap",be=`.${Io}`,Vo=`focusin${be}`,$o=`keydown.tab${be}`,Wo="Tab",Uo="forward",kt="backward",qo={autofocus:!0,trapElement:null},Yo={autofocus:"boolean",trapElement:"element"};class Dt extends se{constructor(e){super(),this._config=this._getConfig(e),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return qo}static get DefaultType(){return Yo}static get NAME(){return Ho}activate(){if(this._isActive)return;this._config.autofocus&&this._config.trapElement.focus(),e.off(document,be),e.on(document,Vo,e=>this._handleFocusin(e)),e.on(document,$o,e=>this._handleKeydown(e)),this._isActive=!0}deactivate(){if(!this._isActive)return;this._isActive=!1,e.off(document,be)}_handleFocusin(e){const{trapElement:n}=this._config;if(e.target===document||e.target===n||n.contains(e.target))return;const s=t.focusableChildren(n);s.length===0?n.focus():this._lastTabNavDirection===kt?s[s.length-1].focus():s[0].focus()}_handleKeydown(e){if(e.key!==Wo)return;this._lastTabNavDirection=e.shiftKey?kt:Uo}}const Nt=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",Lt=".sticky-top",Oe="padding-right",Rt="margin-right";class qe{constructor(){this._element=document.body}getWidth(){const e=document.documentElement.clientWidth;return Math.abs(window.innerWidth-e)}hide(){const e=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,Oe,t=>t+e),this._setElementAttributes(Nt,Oe,t=>t+e),this._setElementAttributes(Lt,Rt,t=>t-e)}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,Oe),this._resetElementAttributes(Nt,Oe),this._resetElementAttributes(Lt,Rt)}isOverflowing(){return this.getWidth()>0}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(e,t,n){const s=this.getWidth(),o=e=>{if(e!==this._element&&window.innerWidth>e.clientWidth+s)return;this._saveInitialAttribute(e,t);const o=window.getComputedStyle(e).getPropertyValue(t);e.style.setProperty(t,`${n(Number.parseFloat(o))}px`)};this._applyManipulationCallback(e,o)}_saveInitialAttribute(e,t){const n=e.style.getPropertyValue(t);n&&v.setDataAttribute(e,t,n)}_resetElementAttributes(e,t){const n=e=>{const n=v.getDataAttribute(e,t);if(n===null){e.style.removeProperty(t);return}v.removeDataAttribute(e,t),e.style.setProperty(t,n)};this._applyManipulationCallback(e,n)}_applyManipulationCallback(e,n){if(g(e)){n(e);return}for(const s of t.find(e,this._element))n(s)}}const ti="modal",ni="bs.modal",d=`.${ni}`,oi=".data-api",ii="Escape",ai=`hide${d}`,ri=`hidePrevented${d}`,Ht=`hidden${d}`,It=`show${d}`,di=`shown${d}`,ui=`resize${d}`,hi=`click.dismiss${d}`,mi=`mousedown.dismiss${d}`,fi=`keydown.dismiss${d}`,pi=`click${d}${oi}`,Bt="modal-open",vi="fade",$t="show",Se="modal-static",yi=".modal.show",_i=".modal-dialog",wi=".modal-body",Oi='[data-bs-toggle="modal"]',xi={backdrop:!0,focus:!0,keyboard:!0},Ci={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"};class $ extends h{constructor(e,n){super(e,n),this._dialog=t.findOne(_i,this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new qe,this._addEventListeners()}static get Default(){return xi}static get DefaultType(){return Ci}static get NAME(){return ti}toggle(e){return this._isShown?this.hide():this.show(e)}show(t){if(this._isShown||this._isTransitioning)return;const n=e.trigger(this._element,It,{relatedTarget:t});if(n.defaultPrevented)return;this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(Bt),this._adjustDialog(),this._backdrop.show(()=>this._showElement(t))}hide(){if(!this._isShown||this._isTransitioning)return;const t=e.trigger(this._element,ai);if(t.defaultPrevented)return;this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove($t),this._queueCallback(()=>this._hideModal(),this._element,this._isAnimated())}dispose(){e.off(window,d),e.off(this._dialog,d),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new xt({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new Dt({trapElement:this._element})}_showElement(n){document.body.contains(this._element)||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0;const s=t.findOne(wi,this._dialog);s&&(s.scrollTop=0),oe(this._element),this._element.classList.add($t);const o=()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,e.trigger(this._element,di,{relatedTarget:n})};this._queueCallback(o,this._dialog,this._isAnimated())}_addEventListeners(){e.on(this._element,fi,e=>{if(e.key!==ii)return;if(this._config.keyboard){this.hide();return}this._triggerBackdropTransition()}),e.on(window,ui,()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()}),e.on(this._element,mi,t=>{e.one(this._element,hi,e=>{if(this._element!==t.target||this._element!==e.target)return;if(this._config.backdrop==="static"){this._triggerBackdropTransition();return}this._config.backdrop&&this.hide()})})}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide(()=>{document.body.classList.remove(Bt),this._resetAdjustments(),this._scrollBar.reset(),e.trigger(this._element,Ht)})}_isAnimated(){return this._element.classList.contains(vi)}_triggerBackdropTransition(){const n=e.trigger(this._element,ri);if(n.defaultPrevented)return;const s=this._element.scrollHeight>document.documentElement.clientHeight,t=this._element.style.overflowY;if(t==="hidden"||this._element.classList.contains(Se))return;s||(this._element.style.overflowY="hidden"),this._element.classList.add(Se),this._queueCallback(()=>{this._element.classList.remove(Se),this._queueCallback(()=>{this._element.style.overflowY=t},this._dialog)},this._dialog),this._element.focus()}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),n=e>0;if(n&&!t){const t=c()?"paddingLeft":"paddingRight";this._element.style[t]=`${e}px`}if(!n&&t){const t=c()?"paddingRight":"paddingLeft";this._element.style[t]=`${e}px`}}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(e,t){return this.each(function(){const n=$.getOrCreateInstance(this,e);if(typeof e!="string")return;if(typeof n[e]=="undefined")throw new TypeError(`No method named "${e}"`);n[e](t)})}}e.on(document,pi,Oi,function(n){const s=t.getElementFromSelector(this);["A","AREA"].includes(this.tagName)&&n.preventDefault(),e.one(s,It,t=>{if(t.defaultPrevented)return;e.one(s,Ht,()=>{R(this)&&this.focus()})});const o=t.findOne(yi);o&&$.getInstance(o).hide();const i=$.getOrCreateInstance(s);i.toggle(this)}),_e($),u($);const ki="offcanvas",Ai="bs.offcanvas",b=`.${Ai}`,Wt=".data-api",cs=`load${b}${Wt}`,Ti="Escape",qt="show",on="showing",an="hiding",Li="offcanvas-backdrop",rn=".offcanvas.show",Pi=`show${b}`,Hi=`shown${b}`,Ii=`hide${b}`,cn=`hidePrevented${b}`,ln=`hidden${b}`,$i=`resize${b}`,Wi=`click${b}${Wt}`,Ui=`keydown.dismiss${b}`,Ki='[data-bs-toggle="offcanvas"]',qi={backdrop:!0,keyboard:!0,scroll:!1},Yi={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"};class O extends h{constructor(e,t){super(e,t),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return qi}static get DefaultType(){return Yi}static get NAME(){return ki}toggle(e){return this._isShown?this.hide():this.show(e)}show(t){if(this._isShown)return;const n=e.trigger(this._element,Pi,{relatedTarget:t});if(n.defaultPrevented)return;this._isShown=!0,this._backdrop.show(),this._config.scroll||(new qe).hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add(on);const s=()=>{(!this._config.scroll||this._config.backdrop)&&this._focustrap.activate(),this._element.classList.add(qt),this._element.classList.remove(on),e.trigger(this._element,Hi,{relatedTarget:t})};this._queueCallback(s,this._element,!0)}hide(){if(!this._isShown)return;const t=e.trigger(this._element,Ii);if(t.defaultPrevented)return;this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add(an),this._backdrop.hide();const n=()=>{this._element.classList.remove(qt,an),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||(new qe).reset(),e.trigger(this._element,ln)};this._queueCallback(n,this._element,!0)}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){const n=()=>{if(this._config.backdrop==="static"){e.trigger(this._element,cn);return}this.hide()},t=Boolean(this._config.backdrop);return new xt({className:Li,isVisible:t,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:t?n:null})}_initializeFocusTrap(){return new Dt({trapElement:this._element})}_addEventListeners(){e.on(this._element,Ui,t=>{if(t.key!==Ti)return;if(this._config.keyboard){this.hide();return}e.trigger(this._element,cn)})}static jQueryInterface(e){return this.each(function(){const t=O.getOrCreateInstance(this,e);if(typeof e!="string")return;if(t[e]===void 0||e.startsWith("_")||e==="constructor")throw new TypeError(`No method named "${e}"`);t[e](this)})}}e.on(document,Wi,Ki,function(n){const s=t.getElementFromSelector(this);if(["A","AREA"].includes(this.tagName)&&n.preventDefault(),y(this))return;e.one(s,ln,()=>{R(this)&&this.focus()});const o=t.findOne(rn);o&&o!==s&&O.getInstance(o).hide();const i=O.getOrCreateInstance(s);i.toggle(this)}),e.on(window,cs,()=>{for(const e of t.find(rn))O.getOrCreateInstance(e).show()}),e.on(window,$i,()=>{for(const e of t.find("[aria-modal][class*=show][class*=offcanvas-]"))getComputedStyle(e).position!=="fixed"&&O.getOrCreateInstance(e).hide()}),_e(O),u(O);const Xi=/^aria-[\w-]*$/i,dn={"*":["class","dir","id","lang","role",Xi],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],dd:[],div:[],dl:[],dt:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},Zi=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),Ji=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i,ea=(e,t)=>{const n=e.nodeName.toLowerCase();return t.includes(n)?!Zi.has(n)||Boolean(Ji.test(e.nodeValue)):t.filter(e=>e instanceof RegExp).some(e=>e.test(n))};function ta(e,t,n){if(!e.length)return e;if(n&&typeof n=="function")return n(e);const o=new window.DOMParser,s=o.parseFromString(e,"text/html"),i=[].concat(...s.body.querySelectorAll("*"));for(const e of i){const n=e.nodeName.toLowerCase();if(!Object.keys(t).includes(n)){e.remove();continue}const s=[].concat(...e.attributes),o=[].concat(t["*"]||[],t[n]||[]);for(const t of s)ea(t,o)||e.removeAttribute(t.nodeName)}return s.body.innerHTML}const na="TemplateFactory",sa={allowList:dn,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:"
    "},oa={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},ia={entry:"(string|element|function|null)",selector:"(string|element)"};class aa extends se{constructor(e){super(),this._config=this._getConfig(e)}static get Default(){return sa}static get DefaultType(){return oa}static get NAME(){return na}getContent(){return Object.values(this._config.content).map(e=>this._resolvePossibleFunction(e)).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(e){return this._checkContent(e),this._config.content={...this._config.content,...e},this}toHtml(){const e=document.createElement("div");e.innerHTML=this._maybeSanitize(this._config.template);for(const[t,n]of Object.entries(this._config.content))this._setContent(e,n,t);const t=e.children[0],n=this._resolvePossibleFunction(this._config.extraClass);return n&&t.classList.add(...n.split(" ")),t}_typeCheckConfig(e){super._typeCheckConfig(e),this._checkContent(e.content)}_checkContent(e){for(const[t,n]of Object.entries(e))super._typeCheckConfig({selector:t,entry:n},ia)}_setContent(e,n,s){const o=t.findOne(s,e);if(!o)return;if(n=this._resolvePossibleFunction(n),!n){o.remove();return}if(g(n)){this._putElementInTemplate(w(n),o);return}if(this._config.html){o.innerHTML=this._maybeSanitize(n);return}o.textContent=n}_maybeSanitize(e){return this._config.sanitize?ta(e,this._config.allowList,this._config.sanitizeFn):e}_resolvePossibleFunction(e){return o(e,[this])}_putElementInTemplate(e,t){if(this._config.html){t.innerHTML="",t.append(e);return}t.textContent=e.textContent}}const ra="tooltip",ca=new Set(["sanitize","allowList","sanitizeFn"]),Ze="fade",da="modal",ye="show",ha=".tooltip-inner",hn=`.${da}`,mn="hide.bs.modal",Z="hover",at="focus",va="click",ba="manual",ja="hide",ya="hidden",_a="show",wa="shown",Oa="inserted",xa="click",Ca="focusin",Ea="focusout",ka="mouseenter",Aa="mouseleave",Sa={AUTO:"auto",TOP:"top",RIGHT:c()?"left":"right",BOTTOM:"bottom",LEFT:c()?"right":"left"},Ma={allowList:dn,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,6],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'',title:"",trigger:"hover focus"},Fa={allowList:"object",animation:"boolean",boundary:"(string|element)",container:"(string|element|boolean)",customClass:"(string|function)",delay:"(number|object)",fallbackPlacements:"array",html:"boolean",offset:"(array|string|function)",placement:"(string|function)",popperConfig:"(null|object|function)",sanitize:"boolean",sanitizeFn:"(null|function)",selector:"(string|boolean)",template:"string",title:"(string|element|function)",trigger:"string"};class H extends h{constructor(e,t){if(typeof Et=="undefined")throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");super(e,t),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners(),this._config.selector||this._fixTitle()}static get Default(){return Ma}static get DefaultType(){return Fa}static get NAME(){return ra}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){if(!this._isEnabled)return;if(this._activeTrigger.click=!this._activeTrigger.click,this._isShown()){this._leave();return}this._enter()}dispose(){clearTimeout(this._timeout),e.off(this._element.closest(hn),mn,this._hideModalHandler),this._element.getAttribute("data-bs-original-title")&&this._element.setAttribute("title",this._element.getAttribute("data-bs-original-title")),this._disposePopper(),super.dispose()}show(){if(this._element.style.display==="none")throw new Error("Please use show on visible elements");if(!this._isWithContent()||!this._isEnabled)return;const n=e.trigger(this._element,this.constructor.eventName(_a)),s=es(this._element),o=(s||this._element.ownerDocument.documentElement).contains(this._element);if(n.defaultPrevented||!o)return;this._disposePopper();const t=this._getTipElement();this._element.setAttribute("aria-describedby",t.getAttribute("id"));const{container:i}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(i.append(t),e.trigger(this._element,this.constructor.eventName(Oa))),this._popper=this._createPopper(t),t.classList.add(ye),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))e.on(t,"mouseover",le);const a=()=>{e.trigger(this._element,this.constructor.eventName(wa)),this._isHovered===!1&&this._leave(),this._isHovered=!1};this._queueCallback(a,this.tip,this._isAnimated())}hide(){if(!this._isShown())return;const t=e.trigger(this._element,this.constructor.eventName(ja));if(t.defaultPrevented)return;const n=this._getTipElement();if(n.classList.remove(ye),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))e.off(t,"mouseover",le);this._activeTrigger[va]=!1,this._activeTrigger[at]=!1,this._activeTrigger[Z]=!1,this._isHovered=null;const s=()=>{if(this._isWithActiveTrigger())return;this._isHovered||this._disposePopper(),this._element.removeAttribute("aria-describedby"),e.trigger(this._element,this.constructor.eventName(ya))};this._queueCallback(s,this.tip,this._isAnimated())}update(){this._popper&&this._popper.update()}_isWithContent(){return Boolean(this._getTitle())}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(e){const t=this._getTemplateFactory(e).toHtml();if(!t)return null;t.classList.remove(Ze,ye),t.classList.add(`bs-${this.constructor.NAME}-auto`);const n=Yr(this.constructor.NAME).toString();return t.setAttribute("id",n),this._isAnimated()&&t.classList.add(Ze),t}setContent(e){this._newContent=e,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(e){return this._templateFactory?this._templateFactory.changeContent(e):this._templateFactory=new aa({...this._config,content:e,extraClass:this._resolvePossibleFunction(this._config.customClass)}),this._templateFactory}_getContentForTemplate(){return{[ha]:this._getTitle()}}_getTitle(){return this._resolvePossibleFunction(this._config.title)||this._element.getAttribute("data-bs-original-title")}_initializeOnDelegatedTarget(e){return this.constructor.getOrCreateInstance(e.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(Ze)}_isShown(){return this.tip&&this.tip.classList.contains(ye)}_createPopper(e){const t=o(this._config.placement,[this,e,this._element]),n=Sa[t.toUpperCase()];return je(this._element,e,this._getPopperConfig(n))}_getOffset(){const{offset:e}=this._config;return typeof e=="string"?e.split(",").map(e=>Number.parseInt(e,10)):typeof e=="function"?t=>e(t,this._element):e}_resolvePossibleFunction(e){return o(e,[this._element])}_getPopperConfig(e){const t={placement:e,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"preSetPlacement",enabled:!0,phase:"beforeMain",fn:e=>{this._getTipElement().setAttribute("data-popper-placement",e.state.placement)}}]};return{...t,...o(this._config.popperConfig,[t])}}_setListeners(){const t=this._config.trigger.split(" ");for(const n of t)if(n==="click")e.on(this._element,this.constructor.eventName(xa),this._config.selector,e=>{const t=this._initializeOnDelegatedTarget(e);t.toggle()});else if(n!==ba){const t=n===Z?this.constructor.eventName(ka):this.constructor.eventName(Ca),s=n===Z?this.constructor.eventName(Aa):this.constructor.eventName(Ea);e.on(this._element,t,this._config.selector,e=>{const t=this._initializeOnDelegatedTarget(e);t._activeTrigger[e.type==="focusin"?at:Z]=!0,t._enter()}),e.on(this._element,s,this._config.selector,e=>{const t=this._initializeOnDelegatedTarget(e);t._activeTrigger[e.type==="focusout"?at:Z]=t._element.contains(e.relatedTarget),t._leave()})}this._hideModalHandler=()=>{this._element&&this.hide()},e.on(this._element.closest(hn),mn,this._hideModalHandler)}_fixTitle(){const e=this._element.getAttribute("title");if(!e)return;!this._element.getAttribute("aria-label")&&!this._element.textContent.trim()&&this._element.setAttribute("aria-label",e),this._element.setAttribute("data-bs-original-title",e),this._element.removeAttribute("title")}_enter(){if(this._isShown()||this._isHovered){this._isHovered=!0;return}this._isHovered=!0,this._setTimeout(()=>{this._isHovered&&this.show()},this._config.delay.show)}_leave(){if(this._isWithActiveTrigger())return;this._isHovered=!1,this._setTimeout(()=>{this._isHovered||this.hide()},this._config.delay.hide)}_setTimeout(e,t){clearTimeout(this._timeout),this._timeout=setTimeout(e,t)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(e){const t=v.getDataAttributes(this._element);for(const e of Object.keys(t))ca.has(e)&&delete t[e];return e={...t,...typeof e=="object"&&e?e:{}},e=this._mergeConfigObj(e),e=this._configAfterMerge(e),this._typeCheckConfig(e),e}_configAfterMerge(e){return e.container=e.container===!1?document.body:w(e.container),typeof e.delay=="number"&&(e.delay={show:e.delay,hide:e.delay}),typeof e.title=="number"&&(e.title=e.title.toString()),typeof e.content=="number"&&(e.content=e.content.toString()),e}_getDelegateConfig(){const e={};for(const[t,n]of Object.entries(this._config))this.constructor.Default[t]!==n&&(e[t]=n);return e.selector=!1,e.trigger="manual",e}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(e){return this.each(function(){const t=H.getOrCreateInstance(this,e);if(typeof e!="string")return;if(typeof t[e]=="undefined")throw new TypeError(`No method named "${e}"`);t[e]()})}}u(H);const za="popover",Da=".popover-header",Na=".popover-body",La={...H.Default,content:"",offset:[0,8],placement:"right",template:'',trigger:"click"},Ra={...H.DefaultType,content:"(null|string|element|function)"};class mt extends H{static get Default(){return La}static get DefaultType(){return Ra}static get NAME(){return za}_isWithContent(){return this._getTitle()||this._getContent()}_getContentForTemplate(){return{[Da]:this._getTitle(),[Na]:this._getContent()}}_getContent(){return this._resolvePossibleFunction(this._config.content)}static jQueryInterface(e){return this.each(function(){const t=mt.getOrCreateInstance(this,e);if(typeof e!="string")return;if(typeof t[e]=="undefined")throw new TypeError(`No method named "${e}"`);t[e]()})}}u(mt);const Ha="scrollspy",Ia="bs.scrollspy",Ie=`.${Ia}`,Va=".data-api",$a=`activate${Ie}`,pn=`click${Ie}`,Ua=`load${Ie}${Va}`,Ka="dropdown-item",W="active",Ya='[data-bs-spy="scroll"]',Ge="[href]",Xa=".nav, .list-group",gn=".nav-link",Za=".nav-item",Ja=".list-group-item",er=`${gn}, ${Za} > ${gn}, ${Ja}`,tr=".dropdown",nr=".dropdown-toggle",sr={offset:null,rootMargin:"0px 0px -25%",smoothScroll:!1,target:null,threshold:[.1,.5,1]},or={offset:"(number|null)",rootMargin:"string",smoothScroll:"boolean",target:"element",threshold:"array"};class xe extends h{constructor(e,t){super(e,t),this._targetLinks=new Map,this._observableSections=new Map,this._rootElement=getComputedStyle(this._element).overflowY==="visible"?null:this._element,this._activeTarget=null,this._observer=null,this._previousScrollData={visibleEntryTop:0,parentScrollTop:0},this.refresh()}static get Default(){return sr}static get DefaultType(){return or}static get NAME(){return Ha}refresh(){this._initializeTargetsAndObservables(),this._maybeEnableSmoothScroll(),this._observer?this._observer.disconnect():this._observer=this._getNewObserver();for(const e of this._observableSections.values())this._observer.observe(e)}dispose(){this._observer.disconnect(),super.dispose()}_configAfterMerge(e){return e.target=w(e.target)||document.body,e.rootMargin=e.offset?`${e.offset}px 0px -30%`:e.rootMargin,typeof e.threshold=="string"&&(e.threshold=e.threshold.split(",").map(e=>Number.parseFloat(e))),e}_maybeEnableSmoothScroll(){if(!this._config.smoothScroll)return;e.off(this._config.target,pn),e.on(this._config.target,pn,Ge,e=>{const t=this._observableSections.get(e.target.hash);if(t){e.preventDefault();const n=this._rootElement||window,s=t.offsetTop-this._element.offsetTop;if(n.scrollTo){n.scrollTo({top:s,behavior:"smooth"});return}n.scrollTop=s}})}_getNewObserver(){const e={root:this._rootElement,threshold:this._config.threshold,rootMargin:this._config.rootMargin};return new IntersectionObserver(e=>this._observerCallback(e),e)}_observerCallback(e){const n=e=>this._targetLinks.get(`#${e.target.id}`),s=e=>{this._previousScrollData.visibleEntryTop=e.target.offsetTop,this._process(n(e))},t=(this._rootElement||document.documentElement).scrollTop,o=t>=this._previousScrollData.parentScrollTop;this._previousScrollData.parentScrollTop=t;for(const i of e){if(!i.isIntersecting){this._activeTarget=null,this._clearActiveClass(n(i));continue}const a=i.target.offsetTop>=this._previousScrollData.visibleEntryTop;if(o&&a){if(s(i),!t)return;continue}!o&&!a&&s(i)}}_initializeTargetsAndObservables(){this._targetLinks=new Map,this._observableSections=new Map;const e=t.find(Ge,this._config.target);for(const n of e){if(!n.hash||y(n))continue;const s=t.findOne(decodeURI(n.hash),this._element);R(s)&&(this._targetLinks.set(decodeURI(n.hash),n),this._observableSections.set(n.hash,s))}}_process(t){if(this._activeTarget===t)return;this._clearActiveClass(this._config.target),this._activeTarget=t,t.classList.add(W),this._activateParents(t),e.trigger(this._element,$a,{relatedTarget:t})}_activateParents(e){if(e.classList.contains(Ka)){t.findOne(nr,e.closest(tr)).classList.add(W);return}for(const n of t.parents(e,Xa))for(const e of t.prev(n,er))e.classList.add(W)}_clearActiveClass(e){e.classList.remove(W);const n=t.find(`${Ge}.${W}`,e);for(const e of n)e.classList.remove(W)}static jQueryInterface(e){return this.each(function(){const t=xe.getOrCreateInstance(this,e);if(typeof e!="string")return;if(t[e]===void 0||e.startsWith("_")||e==="constructor")throw new TypeError(`No method named "${e}"`);t[e]()})}}e.on(window,Ua,()=>{for(const e of t.find(Ya))xe.getOrCreateInstance(e)}),u(xe);const ar="tab",rr="bs.tab",M=`.${rr}`,lr=`hide${M}`,dr=`hidden${M}`,ur=`show${M}`,hr=`shown${M}`,mr=`click${M}`,fr=`keydown${M}`,pr=`load${M}`,gr="ArrowLeft",vn="ArrowRight",br="ArrowUp",yn="ArrowDown",ft="Home",wn="End",S="active",Mn="fade",Ye="show",Cr="dropdown",Wn=".dropdown-toggle",kr=".dropdown-menu",Ne=`:not(${Wn})`,Sr='.list-group, .nav, [role="tablist"]',Mr=".nav-item, .list-group-item",Fr=`.nav-link${Ne}, .list-group-item${Ne}, [role="tab"]${Ne}`,Gn='[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',Pe=`${Fr}, ${Gn}`,Dr=`.${S}[data-bs-toggle="tab"], .${S}[data-bs-toggle="pill"], .${S}[data-bs-toggle="list"]`;class B extends h{constructor(t){if(super(t),this._parent=this._element.closest(Sr),!this._parent)return;this._setInitialAttributes(this._parent,this._getChildren()),e.on(this._element,fr,e=>this._keydown(e))}static get NAME(){return ar}show(){const t=this._element;if(this._elemIsActive(t))return;const n=this._getActiveElem(),s=n?e.trigger(n,lr,{relatedTarget:t}):null,o=e.trigger(t,ur,{relatedTarget:n});if(o.defaultPrevented||s&&s.defaultPrevented)return;this._deactivate(n,t),this._activate(t,n)}_activate(n,s){if(!n)return;n.classList.add(S),this._activate(t.getElementFromSelector(n));const o=()=>{if(n.getAttribute("role")!=="tab"){n.classList.add(Ye);return}n.removeAttribute("tabindex"),n.setAttribute("aria-selected",!0),this._toggleDropDown(n,!0),e.trigger(n,hr,{relatedTarget:s})};this._queueCallback(o,n,n.classList.contains(Mn))}_deactivate(n,s){if(!n)return;n.classList.remove(S),n.blur(),this._deactivate(t.getElementFromSelector(n));const o=()=>{if(n.getAttribute("role")!=="tab"){n.classList.remove(Ye);return}n.setAttribute("aria-selected",!1),n.setAttribute("tabindex","-1"),this._toggleDropDown(n,!1),e.trigger(n,dr,{relatedTarget:s})};this._queueCallback(o,n,n.classList.contains(Mn))}_keydown(e){if(![gr,vn,br,yn,ft,wn].includes(e.key))return;e.stopPropagation(),e.preventDefault();const n=this._getChildren().filter(e=>!y(e));let t;if([ft,wn].includes(e.key))t=n[e.key===ft?0:n.length-1];else{const s=[vn,yn].includes(e.key);t=$e(n,e.target,s,!0)}t&&(t.focus({preventScroll:!0}),B.getOrCreateInstance(t).show())}_getChildren(){return t.find(Pe,this._parent)}_getActiveElem(){return this._getChildren().find(e=>this._elemIsActive(e))||null}_setInitialAttributes(e,t){this._setAttributeIfNotExists(e,"role","tablist");for(const e of t)this._setInitialAttributesOnChild(e)}_setInitialAttributesOnChild(e){e=this._getInnerElement(e);const t=this._elemIsActive(e),n=this._getOuterElement(e);e.setAttribute("aria-selected",t),n!==e&&this._setAttributeIfNotExists(n,"role","presentation"),t||e.setAttribute("tabindex","-1"),this._setAttributeIfNotExists(e,"role","tab"),this._setInitialAttributesOnTargetPanel(e)}_setInitialAttributesOnTargetPanel(e){const n=t.getElementFromSelector(e);if(!n)return;this._setAttributeIfNotExists(n,"role","tabpanel"),e.id&&this._setAttributeIfNotExists(n,"aria-labelledby",`${e.id}`)}_toggleDropDown(e,n){const s=this._getOuterElement(e);if(!s.classList.contains(Cr))return;const o=(e,o)=>{const i=t.findOne(e,s);i&&i.classList.toggle(o,n)};o(Wn,S),o(kr,Ye),s.setAttribute("aria-expanded",n)}_setAttributeIfNotExists(e,t,n){e.hasAttribute(t)||e.setAttribute(t,n)}_elemIsActive(e){return e.classList.contains(S)}_getInnerElement(e){return e.matches(Pe)?e:t.findOne(Pe,e)}_getOuterElement(e){return e.closest(Mr)||e}static jQueryInterface(e){return this.each(function(){const t=B.getOrCreateInstance(this);if(typeof e!="string")return;if(t[e]===void 0||e.startsWith("_")||e==="constructor")throw new TypeError(`No method named "${e}"`);t[e]()})}}e.on(document,mr,Gn,function(e){if(["A","AREA"].includes(this.tagName)&&e.preventDefault(),y(this))return;B.getOrCreateInstance(this).show()}),e.on(window,pr,()=>{for(const e of t.find(Dr))B.getOrCreateInstance(e)}),u(B);const Lr="toast",Rr="bs.toast",x=`.${Rr}`,Hr=`mouseover${x}`,Ir=`mouseout${x}`,Br=`focusin${x}`,Vr=`focusout${x}`,$r=`hide${x}`,Wr=`hidden${x}`,Ur=`show${x}`,Kr=`shown${x}`,qr="fade",os="hide",ve="show",he="showing",Qr={animation:"boolean",autohide:"boolean",delay:"number"},Zr={animation:!0,autohide:!0,delay:5e3};class ue extends h{constructor(e,t){super(e,t),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get Default(){return Zr}static get DefaultType(){return Qr}static get NAME(){return Lr}show(){const t=e.trigger(this._element,Ur);if(t.defaultPrevented)return;this._clearTimeout(),this._config.animation&&this._element.classList.add(qr);const n=()=>{this._element.classList.remove(he),e.trigger(this._element,Kr),this._maybeScheduleHide()};this._element.classList.remove(os),oe(this._element),this._element.classList.add(ve,he),this._queueCallback(n,this._element,this._config.animation)}hide(){if(!this.isShown())return;const t=e.trigger(this._element,$r);if(t.defaultPrevented)return;const n=()=>{this._element.classList.add(os),this._element.classList.remove(he,ve),e.trigger(this._element,Wr)};this._element.classList.add(he),this._queueCallback(n,this._element,this._config.animation)}dispose(){this._clearTimeout(),this.isShown()&&this._element.classList.remove(ve),super.dispose()}isShown(){return this._element.classList.contains(ve)}_maybeScheduleHide(){if(!this._config.autohide)return;if(this._hasMouseInteraction||this._hasKeyboardInteraction)return;this._timeout=setTimeout(()=>{this.hide()},this._config.delay)}_onInteraction(e,t){switch(e.type){case"mouseover":case"mouseout":{this._hasMouseInteraction=t;break}case"focusin":case"focusout":{this._hasKeyboardInteraction=t;break}}if(t){this._clearTimeout();return}const n=e.relatedTarget;if(this._element===n||this._element.contains(n))return;this._maybeScheduleHide()}_setListeners(){e.on(this._element,Hr,e=>this._onInteraction(e,!0)),e.on(this._element,Ir,e=>this._onInteraction(e,!1)),e.on(this._element,Br,e=>this._onInteraction(e,!0)),e.on(this._element,Vr,e=>this._onInteraction(e,!1))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(e){return this.each(function(){const t=ue.getOrCreateInstance(this,e);if(typeof e=="string"){if(typeof t[e]=="undefined")throw new TypeError(`No method named "${e}"`);t[e](this)}})}}_e(ue),u(ue);const ec={Alert:de,Button:fe,Carousel:ee,Collapse:te,Dropdown:m,Modal:$,Offcanvas:O,Popover:mt,ScrollSpy:xe,Tab:B,Toast:ue,Tooltip:H};return ec}),function(e){"use strict";e(function(){e('[data-bs-toggle="tooltip"]').tooltip(),e('[data-bs-toggle="popover"]').popover(),e(".popover-dismiss").popover({trigger:"focus"})});function t(e){return e.offset().top+e.outerHeight()}e(function(){var n,o,i,s=e(".js-td-cover");if(!s.length)return;o=t(s),i=e(".js-navbar-scroll").offset().top,n=Math.ceil(e(".js-navbar-scroll").outerHeight()),o-i>>0;if(""+n!==t||4294967295===n)return NaN;t=n}return t<0?C(e)+t:t}function O(){return!0}function A(e,t,n){return(0===e||void 0!==n&&e<=-n)&&(void 0===t||void 0!==n&&t>=n)}function T(e,t){return P(e,t,0)}function j(e,t){return P(e,t,t)}function P(e,t,n){return void 0===e?n:e<0?Math.max(0,t+e):void 0===t?e:Math.min(t,e)}var I=0,M=1,N=2,R="function"==typeof Symbol&&Symbol.iterator,D="@@iterator",L=R||D;function U(e){this.next=e}function q(e,t,n,r){var o=0===e?t:1===e?n:[t,n];return r?r.value=o:r={value:o,done:!1},r}function F(){return{value:void 0,done:!0}}function B(e){return!!H(e)}function z(e){return e&&"function"==typeof e.next}function V(e){var t=H(e);return t&&t.call(e)}function H(e){var t=e&&(R&&e[R]||e[D]);if("function"==typeof t)return t}function W(e){return e&&"number"==typeof e.length}function J(e){return null==e?ie():a(e)?e.toSeq():function(e){var t=ue(e)||"object"==typeof e&&new te(e);if(!t)throw new TypeError("Expected Array or iterable object of values, or keyed object: "+e);return t}(e)}function K(e){return null==e?ie().toKeyedSeq():a(e)?s(e)?e.toSeq():e.fromEntrySeq():ae(e)}function Y(e){return null==e?ie():a(e)?s(e)?e.entrySeq():e.toIndexedSeq():se(e)}function $(e){return(null==e?ie():a(e)?s(e)?e.entrySeq():e:se(e)).toSetSeq()}U.prototype.toString=function(){return"[Iterator]"},U.KEYS=I,U.VALUES=M,U.ENTRIES=N,U.prototype.inspect=U.prototype.toSource=function(){return this.toString()},U.prototype[L]=function(){return this},t(J,n),J.of=function(){return J(arguments)},J.prototype.toSeq=function(){return this},J.prototype.toString=function(){return this.__toString("Seq {","}")},J.prototype.cacheResult=function(){return!this._cache&&this.__iterateUncached&&(this._cache=this.entrySeq().toArray(),this.size=this._cache.length),this},J.prototype.__iterate=function(e,t){return ce(this,e,t,!0)},J.prototype.__iterator=function(e,t){return le(this,e,t,!0)},t(K,J),K.prototype.toKeyedSeq=function(){return this},t(Y,J),Y.of=function(){return Y(arguments)},Y.prototype.toIndexedSeq=function(){return this},Y.prototype.toString=function(){return this.__toString("Seq [","]")},Y.prototype.__iterate=function(e,t){return ce(this,e,t,!1)},Y.prototype.__iterator=function(e,t){return le(this,e,t,!1)},t($,J),$.of=function(){return $(arguments)},$.prototype.toSetSeq=function(){return this},J.isSeq=oe,J.Keyed=K,J.Set=$,J.Indexed=Y;var G,Z,X,Q="@@__IMMUTABLE_SEQ__@@";function ee(e){this._array=e,this.size=e.length}function te(e){var t=Object.keys(e);this._object=e,this._keys=t,this.size=t.length}function ne(e){this._iterable=e,this.size=e.length||e.size}function re(e){this._iterator=e,this._iteratorCache=[]}function oe(e){return!(!e||!e[Q])}function ie(){return G||(G=new ee([]))}function ae(e){var t=Array.isArray(e)?new ee(e).fromEntrySeq():z(e)?new re(e).fromEntrySeq():B(e)?new ne(e).fromEntrySeq():"object"==typeof e?new te(e):void 0;if(!t)throw new TypeError("Expected Array or iterable object of [k, v] entries, or keyed object: "+e);return t}function se(e){var t=ue(e);if(!t)throw new TypeError("Expected Array or iterable object of values: "+e);return t}function ue(e){return W(e)?new ee(e):z(e)?new re(e):B(e)?new ne(e):void 0}function ce(e,t,n,r){var o=e._cache;if(o){for(var i=o.length-1,a=0;a<=i;a++){var s=o[n?i-a:a];if(!1===t(s[1],r?s[0]:a,e))return a+1}return a}return e.__iterateUncached(t,n)}function le(e,t,n,r){var o=e._cache;if(o){var i=o.length-1,a=0;return new U(function(){var e=o[n?i-a:a];return a++>i?{value:void 0,done:!0}:q(t,r?e[0]:a-1,e[1])})}return e.__iteratorUncached(t,n)}function pe(e,t){return t?function e(t,n,r,o){return Array.isArray(n)?t.call(o,r,Y(n).map(function(r,o){return e(t,r,o,n)})):he(n)?t.call(o,r,K(n).map(function(r,o){return e(t,r,o,n)})):n}(t,e,"",{"":e}):fe(e)}function fe(e){return Array.isArray(e)?Y(e).map(fe).toList():he(e)?K(e).map(fe).toMap():e}function he(e){return e&&(e.constructor===Object||void 0===e.constructor)}function de(e,t){if(e===t||e!=e&&t!=t)return!0;if(!e||!t)return!1;if("function"==typeof e.valueOf&&"function"==typeof t.valueOf){if((e=e.valueOf())===(t=t.valueOf())||e!=e&&t!=t)return!0;if(!e||!t)return!1}return!("function"!=typeof e.equals||"function"!=typeof t.equals||!e.equals(t))}function me(e,t){if(e===t)return!0;if(!a(t)||void 0!==e.size&&void 0!==t.size&&e.size!==t.size||void 0!==e.__hash&&void 0!==t.__hash&&e.__hash!==t.__hash||s(e)!==s(t)||u(e)!==u(t)||l(e)!==l(t))return!1;if(0===e.size&&0===t.size)return!0;var n=!c(e);if(l(e)){var r=e.entries();return t.every(function(e,t){var o=r.next().value;return o&&de(o[1],e)&&(n||de(o[0],t))})&&r.next().done}var o=!1;if(void 0===e.size)if(void 0===t.size)"function"==typeof e.cacheResult&&e.cacheResult();else{o=!0;var i=e;e=t,t=i}var p=!0,f=t.__iterate(function(t,r){if(n?!e.has(t):o?!de(t,e.get(r,y)):!de(e.get(r,y),t))return p=!1,!1});return p&&e.size===f}function ve(e,t){if(!(this instanceof ve))return new ve(e,t);if(this._value=e,this.size=void 0===t?1/0:Math.max(0,t),0===this.size){if(Z)return Z;Z=this}}function ge(e,t){if(!e)throw new Error(t)}function ye(e,t,n){if(!(this instanceof ye))return new ye(e,t,n);if(ge(0!==n,"Cannot step a Range by 0"),e=e||0,void 0===t&&(t=1/0),n=void 0===n?1:Math.abs(n),tr?{value:void 0,done:!0}:q(e,o,n[t?r-o++:o++])})},t(te,K),te.prototype.get=function(e,t){return void 0===t||this.has(e)?this._object[e]:t},te.prototype.has=function(e){return this._object.hasOwnProperty(e)},te.prototype.__iterate=function(e,t){for(var n=this._object,r=this._keys,o=r.length-1,i=0;i<=o;i++){var a=r[t?o-i:i];if(!1===e(n[a],a,this))return i+1}return i},te.prototype.__iterator=function(e,t){var n=this._object,r=this._keys,o=r.length-1,i=0;return new U(function(){var a=r[t?o-i:i];return i++>o?{value:void 0,done:!0}:q(e,a,n[a])})},te.prototype[d]=!0,t(ne,Y),ne.prototype.__iterateUncached=function(e,t){if(t)return this.cacheResult().__iterate(e,t);var n=V(this._iterable),r=0;if(z(n))for(var o;!(o=n.next()).done&&!1!==e(o.value,r++,this););return r},ne.prototype.__iteratorUncached=function(e,t){if(t)return this.cacheResult().__iterator(e,t);var n=V(this._iterable);if(!z(n))return new U(F);var r=0;return new U(function(){var t=n.next();return t.done?t:q(e,r++,t.value)})},t(re,Y),re.prototype.__iterateUncached=function(e,t){if(t)return this.cacheResult().__iterate(e,t);for(var n,r=this._iterator,o=this._iteratorCache,i=0;i=r.length){var t=n.next();if(t.done)return t;r[o]=t.value}return q(e,o,r[o++])})},t(ve,Y),ve.prototype.toString=function(){return 0===this.size?"Repeat []":"Repeat [ "+this._value+" "+this.size+" times ]"},ve.prototype.get=function(e,t){return this.has(e)?this._value:t},ve.prototype.includes=function(e){return de(this._value,e)},ve.prototype.slice=function(e,t){var n=this.size;return A(e,t,n)?this:new ve(this._value,j(t,n)-T(e,n))},ve.prototype.reverse=function(){return this},ve.prototype.indexOf=function(e){return de(this._value,e)?0:-1},ve.prototype.lastIndexOf=function(e){return de(this._value,e)?this.size:-1},ve.prototype.__iterate=function(e,t){for(var n=0;n=0&&t=0&&nn?{value:void 0,done:!0}:q(e,i++,a)})},ye.prototype.equals=function(e){return e instanceof ye?this._start===e._start&&this._end===e._end&&this._step===e._step:me(this,e)},t(be,n),t(_e,be),t(we,be),t(xe,be),be.Keyed=_e,be.Indexed=we,be.Set=xe;var Ee="function"==typeof Math.imul&&-2===Math.imul(4294967295,2)?Math.imul:function(e,t){var n=65535&(e|=0),r=65535&(t|=0);return n*r+((e>>>16)*r+n*(t>>>16)<<16>>>0)|0};function Se(e){return e>>>1&1073741824|3221225471&e}function Ce(e){if(!1===e||null==e)return 0;if("function"==typeof e.valueOf&&(!1===(e=e.valueOf())||null==e))return 0;if(!0===e)return 1;var t=typeof e;if("number"===t){if(e!=e||e===1/0)return 0;var n=0|e;for(n!==e&&(n^=4294967295*e);e>4294967295;)n^=e/=4294967295;return Se(n)}if("string"===t)return e.length>Me?function(e){var t=De[e];return void 0===t&&(t=ke(e),Re===Ne&&(Re=0,De={}),Re++,De[e]=t),t}(e):ke(e);if("function"==typeof e.hashCode)return e.hashCode();if("object"===t)return function(e){var t;if(je&&void 0!==(t=Oe.get(e)))return t;if(void 0!==(t=e[Ie]))return t;if(!Te){if(void 0!==(t=e.propertyIsEnumerable&&e.propertyIsEnumerable[Ie]))return t;if(void 0!==(t=function(e){if(e&&e.nodeType>0)switch(e.nodeType){case 1:return e.uniqueID;case 9:return e.documentElement&&e.documentElement.uniqueID}}(e)))return t}if(t=++Pe,1073741824&Pe&&(Pe=0),je)Oe.set(e,t);else{if(void 0!==Ae&&!1===Ae(e))throw new Error("Non-extensible objects are not allowed as keys.");if(Te)Object.defineProperty(e,Ie,{enumerable:!1,configurable:!1,writable:!1,value:t});else if(void 0!==e.propertyIsEnumerable&&e.propertyIsEnumerable===e.constructor.prototype.propertyIsEnumerable)e.propertyIsEnumerable=function(){return this.constructor.prototype.propertyIsEnumerable.apply(this,arguments)},e.propertyIsEnumerable[Ie]=t;else{if(void 0===e.nodeType)throw new Error("Unable to set a non-enumerable property on object.");e[Ie]=t}}return t}(e);if("function"==typeof e.toString)return ke(e.toString());throw new Error("Value type "+t+" cannot be hashed.")}function ke(e){for(var t=0,n=0;n=t.length)throw new Error("Missing value for key: "+t[n]);e.set(t[n],t[n+1])}})},Ue.prototype.toString=function(){return this.__toString("Map {","}")},Ue.prototype.get=function(e,t){return this._root?this._root.get(0,void 0,e,t):t},Ue.prototype.set=function(e,t){return Qe(this,e,t)},Ue.prototype.setIn=function(e,t){return this.updateIn(e,y,function(){return t})},Ue.prototype.remove=function(e){return Qe(this,e,y)},Ue.prototype.deleteIn=function(e){return this.updateIn(e,function(){return y})},Ue.prototype.update=function(e,t,n){return 1===arguments.length?e(this):this.updateIn([e],t,n)},Ue.prototype.updateIn=function(e,t,n){n||(n=t,t=void 0);var r=function e(t,n,r,o){var i=t===y,a=n.next();if(a.done){var s=i?r:t,u=o(s);return u===s?t:u}ge(i||t&&t.set,"invalid keyPath");var c=a.value,l=i?y:t.get(c,y),p=e(l,n,r,o);return p===l?t:p===y?t.remove(c):(i?Xe():t).set(c,p)}(this,rn(e),t,n);return r===y?void 0:r},Ue.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._root=null,this.__hash=void 0,this.__altered=!0,this):Xe()},Ue.prototype.merge=function(){return rt(this,void 0,arguments)},Ue.prototype.mergeWith=function(t){var n=e.call(arguments,1);return rt(this,t,n)},Ue.prototype.mergeIn=function(t){var n=e.call(arguments,1);return this.updateIn(t,Xe(),function(e){return"function"==typeof e.merge?e.merge.apply(e,n):n[n.length-1]})},Ue.prototype.mergeDeep=function(){return rt(this,ot,arguments)},Ue.prototype.mergeDeepWith=function(t){var n=e.call(arguments,1);return rt(this,it(t),n)},Ue.prototype.mergeDeepIn=function(t){var n=e.call(arguments,1);return this.updateIn(t,Xe(),function(e){return"function"==typeof e.mergeDeep?e.mergeDeep.apply(e,n):n[n.length-1]})},Ue.prototype.sort=function(e){return Tt(Jt(this,e))},Ue.prototype.sortBy=function(e,t){return Tt(Jt(this,t,e))},Ue.prototype.withMutations=function(e){var t=this.asMutable();return e(t),t.wasAltered()?t.__ensureOwner(this.__ownerID):this},Ue.prototype.asMutable=function(){return this.__ownerID?this:this.__ensureOwner(new E)},Ue.prototype.asImmutable=function(){return this.__ensureOwner()},Ue.prototype.wasAltered=function(){return this.__altered},Ue.prototype.__iterator=function(e,t){return new Ye(this,e,t)},Ue.prototype.__iterate=function(e,t){var n=this,r=0;return this._root&&this._root.iterate(function(t){return r++,e(t[1],t[0],n)},t),r},Ue.prototype.__ensureOwner=function(e){return e===this.__ownerID?this:e?Ze(this.size,this._root,e,this.__hash):(this.__ownerID=e,this.__altered=!1,this)},Ue.isMap=qe;var Fe,Be="@@__IMMUTABLE_MAP__@@",ze=Ue.prototype;function Ve(e,t){this.ownerID=e,this.entries=t}function He(e,t,n){this.ownerID=e,this.bitmap=t,this.nodes=n}function We(e,t,n){this.ownerID=e,this.count=t,this.nodes=n}function Je(e,t,n){this.ownerID=e,this.keyHash=t,this.entries=n}function Ke(e,t,n){this.ownerID=e,this.keyHash=t,this.entry=n}function Ye(e,t,n){this._type=t,this._reverse=n,this._stack=e._root&&Ge(e._root)}function $e(e,t){return q(e,t[0],t[1])}function Ge(e,t){return{node:e,index:0,__prev:t}}function Ze(e,t,n,r){var o=Object.create(ze);return o.size=e,o._root=t,o.__ownerID=n,o.__hash=r,o.__altered=!1,o}function Xe(){return Fe||(Fe=Ze(0))}function Qe(e,t,n){var r,o;if(e._root){var i=w(b),a=w(_);if(r=et(e._root,e.__ownerID,0,void 0,t,n,i,a),!a.value)return e;o=e.size+(i.value?n===y?-1:1:0)}else{if(n===y)return e;o=1,r=new Ve(e.__ownerID,[[t,n]])}return e.__ownerID?(e.size=o,e._root=r,e.__hash=void 0,e.__altered=!0,e):r?Ze(o,r):Xe()}function et(e,t,n,r,o,i,a,s){return e?e.update(t,n,r,o,i,a,s):i===y?e:(x(s),x(a),new Ke(t,r,[o,i]))}function tt(e){return e.constructor===Ke||e.constructor===Je}function nt(e,t,n,r,o){if(e.keyHash===r)return new Je(t,r,[e.entry,o]);var i,a=(0===n?e.keyHash:e.keyHash>>>n)&g,s=(0===n?r:r>>>n)&g;return new He(t,1<>1&1431655765))+(e>>2&858993459))+(e>>4)&252645135,e+=e>>8,127&(e+=e>>16)}function ut(e,t,n,r){var o=r?e:S(e);return o[t]=n,o}ze[Be]=!0,ze.delete=ze.remove,ze.removeIn=ze.deleteIn,Ve.prototype.get=function(e,t,n,r){for(var o=this.entries,i=0,a=o.length;i=ct)return function(e,t,n,r){e||(e=new E);for(var o=new Ke(e,Ce(n),[n,r]),i=0;i>>e)&g),i=this.bitmap;return 0==(i&o)?r:this.nodes[st(i&o-1)].get(e+m,t,n,r)},He.prototype.update=function(e,t,n,r,o,i,a){void 0===n&&(n=Ce(r));var s=(0===t?n:n>>>t)&g,u=1<=lt)return function(e,t,n,r,o){for(var i=0,a=new Array(v),s=0;0!==n;s++,n>>>=1)a[s]=1&n?t[i++]:void 0;return a[r]=o,new We(e,i+1,a)}(e,f,c,s,d);if(l&&!d&&2===f.length&&tt(f[1^p]))return f[1^p];if(l&&d&&1===f.length&&tt(d))return d;var b=e&&e===this.ownerID,_=l?d?c:c^u:c|u,w=l?d?ut(f,p,d,b):function(e,t,n){var r=e.length-1;if(n&&t===r)return e.pop(),e;for(var o=new Array(r),i=0,a=0;a>>e)&g,i=this.nodes[o];return i?i.get(e+m,t,n,r):r},We.prototype.update=function(e,t,n,r,o,i,a){void 0===n&&(n=Ce(r));var s=(0===t?n:n>>>t)&g,u=o===y,c=this.nodes,l=c[s];if(u&&!l)return this;var p=et(l,e,t+m,n,r,o,i,a);if(p===l)return this;var f=this.count;if(l){if(!p&&--f0&&r=0&&e=e.size||t<0)return e.withMutations(function(e){t<0?kt(e,t).set(0,n):kt(e,0,t+1).set(t,n)});t+=e._origin;var r=e._tail,o=e._root,i=w(_);return t>=At(e._capacity)?r=Et(r,e.__ownerID,0,t,n,i):o=Et(o,e.__ownerID,e._level,t,n,i),i.value?e.__ownerID?(e._root=o,e._tail=r,e.__hash=void 0,e.__altered=!0,e):wt(e._origin,e._capacity,e._level,o,r):e}(this,e,t)},ft.prototype.remove=function(e){return this.has(e)?0===e?this.shift():e===this.size-1?this.pop():this.splice(e,1):this},ft.prototype.insert=function(e,t){return this.splice(e,0,t)},ft.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=this._origin=this._capacity=0,this._level=m,this._root=this._tail=null,this.__hash=void 0,this.__altered=!0,this):xt()},ft.prototype.push=function(){var e=arguments,t=this.size;return this.withMutations(function(n){kt(n,0,t+e.length);for(var r=0;r>>t&g;if(r>=this.array.length)return new vt([],e);var o,i=0===r;if(t>0){var a=this.array[r];if((o=a&&a.removeBefore(e,t-m,n))===a&&i)return this}if(i&&!o)return this;var s=St(this,e);if(!i)for(var u=0;u>>t&g;if(o>=this.array.length)return this;if(t>0){var i=this.array[o];if((r=i&&i.removeAfter(e,t-m,n))===i&&o===this.array.length-1)return this}var a=St(this,e);return a.array.splice(o+1),r&&(a.array[o]=r),a};var gt,yt,bt={};function _t(e,t){var n=e._origin,r=e._capacity,o=At(r),i=e._tail;return a(e._root,e._level,0);function a(e,s,u){return 0===s?function(e,a){var s=a===o?i&&i.array:e&&e.array,u=a>n?0:n-a,c=r-a;return c>v&&(c=v),function(){if(u===c)return bt;var e=t?--c:u++;return s&&s[e]}}(e,u):function(e,o,i){var s,u=e&&e.array,c=i>n?0:n-i>>o,l=1+(r-i>>o);return l>v&&(l=v),function(){for(;;){if(s){var e=s();if(e!==bt)return e;s=null}if(c===l)return bt;var n=t?--l:c++;s=a(u&&u[n],o-m,i+(n<>>n&g,u=e&&s0){var c=e&&e.array[s],l=Et(c,t,n-m,r,o,i);return l===c?e:((a=St(e,t)).array[s]=l,a)}return u&&e.array[s]===o?e:(x(i),a=St(e,t),void 0===o&&s===a.array.length-1?a.array.pop():a.array[s]=o,a)}function St(e,t){return t&&e&&t===e.ownerID?e:new vt(e?e.array.slice():[],t)}function Ct(e,t){if(t>=At(e._capacity))return e._tail;if(t<1<0;)n=n.array[t>>>r&g],r-=m;return n}}function kt(e,t,n){void 0!==t&&(t|=0),void 0!==n&&(n|=0);var r=e.__ownerID||new E,o=e._origin,i=e._capacity,a=o+t,s=void 0===n?i:n<0?i+n:o+n;if(a===o&&s===i)return e;if(a>=s)return e.clear();for(var u=e._level,c=e._root,l=0;a+l<0;)c=new vt(c&&c.array.length?[void 0,c]:[],r),l+=1<<(u+=m);l&&(a+=l,o+=l,s+=l,i+=l);for(var p=At(i),f=At(s);f>=1<p?new vt([],r):h;if(h&&f>p&&am;y-=m){var b=p>>>y&g;v=v.array[b]=St(v.array[b],r)}v.array[p>>>m&g]=h}if(s=f)a-=f,s-=f,u=m,c=null,d=d&&d.removeBefore(r,0,a);else if(a>o||f>>u&g;if(_!==f>>>u&g)break;_&&(l+=(1<o&&(c=c.removeBefore(r,u,a-l)),c&&fi&&(i=c.size),a(u)||(c=c.map(function(e){return pe(e)})),r.push(c)}return i>e.size&&(e=e.setSize(i)),at(e,t,r)}function At(e){return e>>m<=v&&a.size>=2*i.size?(r=(o=a.filter(function(e,t){return void 0!==e&&s!==t})).toKeyedSeq().map(function(e){return e[0]}).flip().toMap(),e.__ownerID&&(r.__ownerID=o.__ownerID=e.__ownerID)):(r=i.remove(t),o=s===a.size-1?a.pop():a.set(s,void 0))}else if(u){if(n===a.get(s)[1])return e;r=i,o=a.set(s,[t,n])}else r=i.set(t,a.size),o=a.set(a.size,[t,n]);return e.__ownerID?(e.size=r.size,e._map=r,e._list=o,e.__hash=void 0,e):Pt(r,o)}function Nt(e,t){this._iter=e,this._useKeys=t,this.size=e.size}function Rt(e){this._iter=e,this.size=e.size}function Dt(e){this._iter=e,this.size=e.size}function Lt(e){this._iter=e,this.size=e.size}function Ut(e){var t=en(e);return t._iter=e,t.size=e.size,t.flip=function(){return e},t.reverse=function(){var t=e.reverse.apply(this);return t.flip=function(){return e.reverse()},t},t.has=function(t){return e.includes(t)},t.includes=function(t){return e.has(t)},t.cacheResult=tn,t.__iterateUncached=function(t,n){var r=this;return e.__iterate(function(e,n){return!1!==t(n,e,r)},n)},t.__iteratorUncached=function(t,n){if(t===N){var r=e.__iterator(t,n);return new U(function(){var e=r.next();if(!e.done){var t=e.value[0];e.value[0]=e.value[1],e.value[1]=t}return e})}return e.__iterator(t===M?I:M,n)},t}function qt(e,t,n){var r=en(e);return r.size=e.size,r.has=function(t){return e.has(t)},r.get=function(r,o){var i=e.get(r,y);return i===y?o:t.call(n,i,r,e)},r.__iterateUncached=function(r,o){var i=this;return e.__iterate(function(e,o,a){return!1!==r(t.call(n,e,o,a),o,i)},o)},r.__iteratorUncached=function(r,o){var i=e.__iterator(N,o);return new U(function(){var o=i.next();if(o.done)return o;var a=o.value,s=a[0];return q(r,s,t.call(n,a[1],s,e),o)})},r}function Ft(e,t){var n=en(e);return n._iter=e,n.size=e.size,n.reverse=function(){return e},e.flip&&(n.flip=function(){var t=Ut(e);return t.reverse=function(){return e.flip()},t}),n.get=function(n,r){return e.get(t?n:-1-n,r)},n.has=function(n){return e.has(t?n:-1-n)},n.includes=function(t){return e.includes(t)},n.cacheResult=tn,n.__iterate=function(t,n){var r=this;return e.__iterate(function(e,n){return t(e,n,r)},!n)},n.__iterator=function(t,n){return e.__iterator(t,!n)},n}function Bt(e,t,n,r){var o=en(e);return r&&(o.has=function(r){var o=e.get(r,y);return o!==y&&!!t.call(n,o,r,e)},o.get=function(r,o){var i=e.get(r,y);return i!==y&&t.call(n,i,r,e)?i:o}),o.__iterateUncached=function(o,i){var a=this,s=0;return e.__iterate(function(e,i,u){if(t.call(n,e,i,u))return s++,o(e,r?i:s-1,a)},i),s},o.__iteratorUncached=function(o,i){var a=e.__iterator(N,i),s=0;return new U(function(){for(;;){var i=a.next();if(i.done)return i;var u=i.value,c=u[0],l=u[1];if(t.call(n,l,c,e))return q(o,r?c:s++,l,i)}})},o}function zt(e,t,n,r){var o=e.size;if(void 0!==t&&(t|=0),void 0!==n&&(n===1/0?n=o:n|=0),A(t,n,o))return e;var i=T(t,o),a=j(n,o);if(i!=i||a!=a)return zt(e.toSeq().cacheResult(),t,n,r);var s,u=a-i;u==u&&(s=u<0?0:u);var c=en(e);return c.size=0===s?s:e.size&&s||void 0,!r&&oe(e)&&s>=0&&(c.get=function(t,n){return(t=k(this,t))>=0&&ts)return{value:void 0,done:!0};var e=o.next();return r||t===M?e:q(t,u-1,t===I?void 0:e.value[1],e)})},c}function Vt(e,t,n,r){var o=en(e);return o.__iterateUncached=function(o,i){var a=this;if(i)return this.cacheResult().__iterate(o,i);var s=!0,u=0;return e.__iterate(function(e,i,c){if(!s||!(s=t.call(n,e,i,c)))return u++,o(e,r?i:u-1,a)}),u},o.__iteratorUncached=function(o,i){var a=this;if(i)return this.cacheResult().__iterator(o,i);var s=e.__iterator(N,i),u=!0,c=0;return new U(function(){var e,i,l;do{if((e=s.next()).done)return r||o===M?e:q(o,c++,o===I?void 0:e.value[1],e);var p=e.value;i=p[0],l=p[1],u&&(u=t.call(n,l,i,a))}while(u);return o===N?e:q(o,i,l,e)})},o}function Ht(e,t){var n=s(e),o=[e].concat(t).map(function(e){return a(e)?n&&(e=r(e)):e=n?ae(e):se(Array.isArray(e)?e:[e]),e}).filter(function(e){return 0!==e.size});if(0===o.length)return e;if(1===o.length){var i=o[0];if(i===e||n&&s(i)||u(e)&&u(i))return i}var c=new ee(o);return n?c=c.toKeyedSeq():u(e)||(c=c.toSetSeq()),(c=c.flatten(!0)).size=o.reduce(function(e,t){if(void 0!==e){var n=t.size;if(void 0!==n)return e+n}},0),c}function Wt(e,t,n){var r=en(e);return r.__iterateUncached=function(r,o){var i=0,s=!1;return function e(u,c){var l=this;u.__iterate(function(o,u){return(!t||c0}function $t(e,t,r){var o=en(e);return o.size=new ee(r).map(function(e){return e.size}).min(),o.__iterate=function(e,t){for(var n,r=this.__iterator(M,t),o=0;!(n=r.next()).done&&!1!==e(n.value,o++,this););return o},o.__iteratorUncached=function(e,o){var i=r.map(function(e){return e=n(e),V(o?e.reverse():e)}),a=0,s=!1;return new U(function(){var n;return s||(n=i.map(function(e){return e.next()}),s=n.some(function(e){return e.done})),s?{value:void 0,done:!0}:q(e,a++,t.apply(null,n.map(function(e){return e.value})))})},o}function Gt(e,t){return oe(e)?t:e.constructor(t)}function Zt(e){if(e!==Object(e))throw new TypeError("Expected [K, V] tuple: "+e)}function Xt(e){return Le(e.size),C(e)}function Qt(e){return s(e)?r:u(e)?o:i}function en(e){return Object.create((s(e)?K:u(e)?Y:$).prototype)}function tn(){return this._iter.cacheResult?(this._iter.cacheResult(),this.size=this._iter.size,this):J.prototype.cacheResult.call(this)}function nn(e,t){return e>t?1:e=0;n--)t={value:arguments[n],next:t};return this.__ownerID?(this.size=e,this._head=t,this.__hash=void 0,this.__altered=!0,this):An(e,t)},En.prototype.pushAll=function(e){if(0===(e=o(e)).size)return this;Le(e.size);var t=this.size,n=this._head;return e.reverse().forEach(function(e){t++,n={value:e,next:n}}),this.__ownerID?(this.size=t,this._head=n,this.__hash=void 0,this.__altered=!0,this):An(t,n)},En.prototype.pop=function(){return this.slice(1)},En.prototype.unshift=function(){return this.push.apply(this,arguments)},En.prototype.unshiftAll=function(e){return this.pushAll(e)},En.prototype.shift=function(){return this.pop.apply(this,arguments)},En.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._head=void 0,this.__hash=void 0,this.__altered=!0,this):Tn()},En.prototype.slice=function(e,t){if(A(e,t,this.size))return this;var n=T(e,this.size);if(j(t,this.size)!==this.size)return we.prototype.slice.call(this,e,t);for(var r=this.size-n,o=this._head;n--;)o=o.next;return this.__ownerID?(this.size=r,this._head=o,this.__hash=void 0,this.__altered=!0,this):An(r,o)},En.prototype.__ensureOwner=function(e){return e===this.__ownerID?this:e?An(this.size,this._head,e,this.__hash):(this.__ownerID=e,this.__altered=!1,this)},En.prototype.__iterate=function(e,t){if(t)return this.reverse().__iterate(e);for(var n=0,r=this._head;r&&!1!==e(r.value,n++,this);)r=r.next;return n},En.prototype.__iterator=function(e,t){if(t)return this.reverse().__iterator(e);var n=0,r=this._head;return new U(function(){if(r){var t=r.value;return r=r.next,q(e,n++,t)}return{value:void 0,done:!0}})},En.isStack=Sn;var Cn,kn="@@__IMMUTABLE_STACK__@@",On=En.prototype;function An(e,t,n,r){var o=Object.create(On);return o.size=e,o._head=t,o.__ownerID=n,o.__hash=r,o.__altered=!1,o}function Tn(){return Cn||(Cn=An(0))}function jn(e,t){var n=function(n){e.prototype[n]=t[n]};return Object.keys(t).forEach(n),Object.getOwnPropertySymbols&&Object.getOwnPropertySymbols(t).forEach(n),e}On[kn]=!0,On.withMutations=ze.withMutations,On.asMutable=ze.asMutable,On.asImmutable=ze.asImmutable,On.wasAltered=ze.wasAltered,n.Iterator=U,jn(n,{toArray:function(){Le(this.size);var e=new Array(this.size||0);return this.valueSeq().__iterate(function(t,n){e[n]=t}),e},toIndexedSeq:function(){return new Rt(this)},toJS:function(){return this.toSeq().map(function(e){return e&&"function"==typeof e.toJS?e.toJS():e}).__toJS()},toJSON:function(){return this.toSeq().map(function(e){return e&&"function"==typeof e.toJSON?e.toJSON():e}).__toJS()},toKeyedSeq:function(){return new Nt(this,!0)},toMap:function(){return Ue(this.toKeyedSeq())},toObject:function(){Le(this.size);var e={};return this.__iterate(function(t,n){e[n]=t}),e},toOrderedMap:function(){return Tt(this.toKeyedSeq())},toOrderedSet:function(){return gn(s(this)?this.valueSeq():this)},toSet:function(){return cn(s(this)?this.valueSeq():this)},toSetSeq:function(){return new Dt(this)},toSeq:function(){return u(this)?this.toIndexedSeq():s(this)?this.toKeyedSeq():this.toSetSeq()},toStack:function(){return En(s(this)?this.valueSeq():this)},toList:function(){return ft(s(this)?this.valueSeq():this)},toString:function(){return"[Iterable]"},__toString:function(e,t){return 0===this.size?e+t:e+" "+this.toSeq().map(this.__toStringMapper).join(", ")+" "+t},concat:function(){var t=e.call(arguments,0);return Gt(this,Ht(this,t))},includes:function(e){return this.some(function(t){return de(t,e)})},entries:function(){return this.__iterator(N)},every:function(e,t){Le(this.size);var n=!0;return this.__iterate(function(r,o,i){if(!e.call(t,r,o,i))return n=!1,!1}),n},filter:function(e,t){return Gt(this,Bt(this,e,t,!0))},find:function(e,t,n){var r=this.findEntry(e,t);return r?r[1]:n},forEach:function(e,t){return Le(this.size),this.__iterate(t?e.bind(t):e)},join:function(e){Le(this.size),e=void 0!==e?""+e:",";var t="",n=!0;return this.__iterate(function(r){n?n=!1:t+=e,t+=null!=r?r.toString():""}),t},keys:function(){return this.__iterator(I)},map:function(e,t){return Gt(this,qt(this,e,t))},reduce:function(e,t,n){var r,o;return Le(this.size),arguments.length<2?o=!0:r=t,this.__iterate(function(t,i,a){o?(o=!1,r=t):r=e.call(n,r,t,i,a)}),r},reduceRight:function(e,t,n){var r=this.toKeyedSeq().reverse();return r.reduce.apply(r,arguments)},reverse:function(){return Gt(this,Ft(this,!0))},slice:function(e,t){return Gt(this,zt(this,e,t,!0))},some:function(e,t){return!this.every(Rn(e),t)},sort:function(e){return Gt(this,Jt(this,e))},values:function(){return this.__iterator(M)},butLast:function(){return this.slice(0,-1)},isEmpty:function(){return void 0!==this.size?0===this.size:!this.some(function(){return!0})},count:function(e,t){return C(e?this.toSeq().filter(e,t):this)},countBy:function(e,t){return function(e,t,n){var r=Ue().asMutable();return e.__iterate(function(o,i){r.update(t.call(n,o,i,e),0,function(e){return e+1})}),r.asImmutable()}(this,e,t)},equals:function(e){return me(this,e)},entrySeq:function(){var e=this;if(e._cache)return new ee(e._cache);var t=e.toSeq().map(Nn).toIndexedSeq();return t.fromEntrySeq=function(){return e.toSeq()},t},filterNot:function(e,t){return this.filter(Rn(e),t)},findEntry:function(e,t,n){var r=n;return this.__iterate(function(n,o,i){if(e.call(t,n,o,i))return r=[o,n],!1}),r},findKey:function(e,t){var n=this.findEntry(e,t);return n&&n[0]},findLast:function(e,t,n){return this.toKeyedSeq().reverse().find(e,t,n)},findLastEntry:function(e,t,n){return this.toKeyedSeq().reverse().findEntry(e,t,n)},findLastKey:function(e,t){return this.toKeyedSeq().reverse().findKey(e,t)},first:function(){return this.find(O)},flatMap:function(e,t){return Gt(this,function(e,t,n){var r=Qt(e);return e.toSeq().map(function(o,i){return r(t.call(n,o,i,e))}).flatten(!0)}(this,e,t))},flatten:function(e){return Gt(this,Wt(this,e,!0))},fromEntrySeq:function(){return new Lt(this)},get:function(e,t){return this.find(function(t,n){return de(n,e)},void 0,t)},getIn:function(e,t){for(var n,r=this,o=rn(e);!(n=o.next()).done;){var i=n.value;if((r=r&&r.get?r.get(i,y):y)===y)return t}return r},groupBy:function(e,t){return function(e,t,n){var r=s(e),o=(l(e)?Tt():Ue()).asMutable();e.__iterate(function(i,a){o.update(t.call(n,i,a,e),function(e){return(e=e||[]).push(r?[a,i]:i),e})});var i=Qt(e);return o.map(function(t){return Gt(e,i(t))})}(this,e,t)},has:function(e){return this.get(e,y)!==y},hasIn:function(e){return this.getIn(e,y)!==y},isSubset:function(e){return e="function"==typeof e.includes?e:n(e),this.every(function(t){return e.includes(t)})},isSuperset:function(e){return(e="function"==typeof e.isSubset?e:n(e)).isSubset(this)},keyOf:function(e){return this.findKey(function(t){return de(t,e)})},keySeq:function(){return this.toSeq().map(Mn).toIndexedSeq()},last:function(){return this.toSeq().reverse().first()},lastKeyOf:function(e){return this.toKeyedSeq().reverse().keyOf(e)},max:function(e){return Kt(this,e)},maxBy:function(e,t){return Kt(this,t,e)},min:function(e){return Kt(this,e?Dn(e):qn)},minBy:function(e,t){return Kt(this,t?Dn(t):qn,e)},rest:function(){return this.slice(1)},skip:function(e){return this.slice(Math.max(0,e))},skipLast:function(e){return Gt(this,this.toSeq().reverse().skip(e).reverse())},skipWhile:function(e,t){return Gt(this,Vt(this,e,t,!0))},skipUntil:function(e,t){return this.skipWhile(Rn(e),t)},sortBy:function(e,t){return Gt(this,Jt(this,t,e))},take:function(e){return this.slice(0,Math.max(0,e))},takeLast:function(e){return Gt(this,this.toSeq().reverse().take(e).reverse())},takeWhile:function(e,t){return Gt(this,function(e,t,n){var r=en(e);return r.__iterateUncached=function(r,o){var i=this;if(o)return this.cacheResult().__iterate(r,o);var a=0;return e.__iterate(function(e,o,s){return t.call(n,e,o,s)&&++a&&r(e,o,i)}),a},r.__iteratorUncached=function(r,o){var i=this;if(o)return this.cacheResult().__iterator(r,o);var a=e.__iterator(N,o),s=!0;return new U(function(){if(!s)return{value:void 0,done:!0};var e=a.next();if(e.done)return e;var o=e.value,u=o[0],c=o[1];return t.call(n,c,u,i)?r===N?e:q(r,u,c,e):(s=!1,{value:void 0,done:!0})})},r}(this,e,t))},takeUntil:function(e,t){return this.takeWhile(Rn(e),t)},valueSeq:function(){return this.toIndexedSeq()},hashCode:function(){return this.__hash||(this.__hash=function(e){if(e.size===1/0)return 0;var t=l(e),n=s(e),r=t?1:0;return function(e,t){return t=Ee(t,3432918353),t=Ee(t<<15|t>>>-15,461845907),t=Ee(t<<13|t>>>-13,5),t=Ee((t=(t+3864292196|0)^e)^t>>>16,2246822507),t=Se((t=Ee(t^t>>>13,3266489909))^t>>>16)}(e.__iterate(n?t?function(e,t){r=31*r+Fn(Ce(e),Ce(t))|0}:function(e,t){r=r+Fn(Ce(e),Ce(t))|0}:t?function(e){r=31*r+Ce(e)|0}:function(e){r=r+Ce(e)|0}),r)}(this))}});var Pn=n.prototype;Pn[p]=!0,Pn[L]=Pn.values,Pn.__toJS=Pn.toArray,Pn.__toStringMapper=Ln,Pn.inspect=Pn.toSource=function(){return this.toString()},Pn.chain=Pn.flatMap,Pn.contains=Pn.includes,jn(r,{flip:function(){return Gt(this,Ut(this))},mapEntries:function(e,t){var n=this,r=0;return Gt(this,this.toSeq().map(function(o,i){return e.call(t,[i,o],r++,n)}).fromEntrySeq())},mapKeys:function(e,t){var n=this;return Gt(this,this.toSeq().flip().map(function(r,o){return e.call(t,r,o,n)}).flip())}});var In=r.prototype;function Mn(e,t){return t}function Nn(e,t){return[t,e]}function Rn(e){return function(){return!e.apply(this,arguments)}}function Dn(e){return function(){return-e.apply(this,arguments)}}function Ln(e){return"string"==typeof e?JSON.stringify(e):String(e)}function Un(){return S(arguments)}function qn(e,t){return et?-1:0}function Fn(e,t){return e^t+2654435769+(e<<6)+(e>>2)|0}return In[f]=!0,In[L]=Pn.entries,In.__toJS=Pn.toObject,In.__toStringMapper=function(e,t){return JSON.stringify(t)+": "+Ln(e)},jn(o,{toKeyedSeq:function(){return new Nt(this,!1)},filter:function(e,t){return Gt(this,Bt(this,e,t,!1))},findIndex:function(e,t){var n=this.findEntry(e,t);return n?n[0]:-1},indexOf:function(e){var t=this.keyOf(e);return void 0===t?-1:t},lastIndexOf:function(e){var t=this.lastKeyOf(e);return void 0===t?-1:t},reverse:function(){return Gt(this,Ft(this,!1))},slice:function(e,t){return Gt(this,zt(this,e,t,!1))},splice:function(e,t){var n=arguments.length;if(t=Math.max(0|t,0),0===n||2===n&&!t)return this;e=T(e,e<0?this.count():this.size);var r=this.slice(0,e);return Gt(this,1===n?r:r.concat(S(arguments,2),this.slice(e+t)))},findLastIndex:function(e,t){var n=this.findLastEntry(e,t);return n?n[0]:-1},first:function(){return this.get(0)},flatten:function(e){return Gt(this,Wt(this,e,!1))},get:function(e,t){return(e=k(this,e))<0||this.size===1/0||void 0!==this.size&&e>this.size?t:this.find(function(t,n){return n===e},void 0,t)},has:function(e){return(e=k(this,e))>=0&&(void 0!==this.size?this.size===1/0||e5e3)return e.textContent;return function(e){for(var n,r,o,i,a,s=e.textContent,u=0,c=s[0],l=1,p=e.innerHTML="",f=0;r=n,n=f<7&&"\\"==n?1:l;){if(l=c,c=s[++u],i=p.length>1,!l||f>8&&"\n"==l||[/\S/.test(l),1,1,!/[$\w]/.test(l),("/"==n||"\n"==n)&&i,'"'==n&&i,"'"==n&&i,s[u-4]+r+n=="--\x3e",r+n=="*/"][f])for(p&&(e.appendChild(a=t.createElement("span")).setAttribute("style",["color: #555; font-weight: bold;","","","color: #555;",""][f?f<3?2:f>6?4:f>3?3:+/^(a(bstract|lias|nd|rguments|rray|s(m|sert)?|uto)|b(ase|egin|ool(ean)?|reak|yte)|c(ase|atch|har|hecked|lass|lone|ompl|onst|ontinue)|de(bugger|cimal|clare|f(ault|er)?|init|l(egate|ete)?)|do|double|e(cho|ls?if|lse(if)?|nd|nsure|num|vent|x(cept|ec|p(licit|ort)|te(nds|nsion|rn)))|f(allthrough|alse|inal(ly)?|ixed|loat|or(each)?|riend|rom|unc(tion)?)|global|goto|guard|i(f|mp(lements|licit|ort)|n(it|clude(_once)?|line|out|stanceof|t(erface|ernal)?)?|s)|l(ambda|et|ock|ong)|m(icrolight|odule|utable)|NaN|n(amespace|ative|ext|ew|il|ot|ull)|o(bject|perator|r|ut|verride)|p(ackage|arams|rivate|rotected|rotocol|ublic)|r(aise|e(adonly|do|f|gister|peat|quire(_once)?|scue|strict|try|turn))|s(byte|ealed|elf|hort|igned|izeof|tatic|tring|truct|ubscript|uper|ynchronized|witch)|t(emplate|hen|his|hrows?|ransient|rue|ry|ype(alias|def|id|name|of))|u(n(checked|def(ined)?|ion|less|signed|til)|se|sing)|v(ar|irtual|oid|olatile)|w(char_t|hen|here|hile|ith)|xor|yield)$/.test(p):0]),a.appendChild(t.createTextNode(p))),o=f&&f<7?f:o,p="",f=11;![1,/[\/{}[(\-+*=<>:;|\\.,?!&@~]/.test(l),/[\])]/.test(l),/[$\w]/.test(l),"/"==l&&o<2&&"<"!=n,'"'==l,"'"==l,l+c+s[u+1]+s[u+2]=="\x3c!--",l+c=="/*",l+c=="//","#"==l][--f];);p+=l}}(e)}function Q(e){var t;if([/filename\*=[^']+'\w*'"([^"]+)";?/i,/filename\*=[^']+'\w*'([^;]+);?/i,/filename="([^;]*);?"/i,/filename=([^;]*);?/i].some(function(n){return null!==(t=n.exec(e))}),null!==t&&t.length>1)try{return decodeURIComponent(t[1])}catch(e){console.error(e)}return null}function ee(e){return t=e.replace(/\.[^.\/]*$/,""),b()(g()(t));var t}var te=function(e,t){if(e>t)return"Value must be less than Maximum"},ne=function(e,t){if(et)return"Value must be less than MaxLength"},pe=function(e,t){if(e.length2&&void 0!==arguments[2]?arguments[2]:{},r=n.isOAS3,o=void 0!==r&&r,i=n.bypassRequiredCheck,a=void 0!==i&&i,s=[],u=e.get("required"),c=Object(P.a)(e,{isOAS3:o}),p=c.schema,h=c.parameterContentMediaType;if(!p)return s;var m=p.get("required"),v=p.get("maximum"),g=p.get("minimum"),y=p.get("type"),b=p.get("format"),_=p.get("maxLength"),w=p.get("minLength"),x=p.get("pattern");if(y&&(u||m||t)){var E="string"===y&&t,S="array"===y&&l()(t)&&t.length,C="array"===y&&d.a.List.isList(t)&&t.count(),k="array"===y&&"string"==typeof t&&t,O="file"===y&&t instanceof A.a.File,T="boolean"===y&&(t||!1===t),j="number"===y&&(t||0===t),I="integer"===y&&(t||0===t),M="object"===y&&"object"===f()(t)&&null!==t,N="object"===y&&"string"==typeof t&&t,R=[E,S,C,k,O,T,j,I,M,N],D=R.some(function(e){return!!e});if((u||m)&&!D&&!a)return s.push("Required field is not provided"),s;if("object"===y&&"string"==typeof t&&(null===h||"application/json"===h))try{JSON.parse(t)}catch(e){return s.push("Parameter string value must be valid JSON"),s}if(x){var L=fe(t,x);L&&s.push(L)}if(_||0===_){var U=le(t,_);U&&s.push(U)}if(w){var q=pe(t,w);q&&s.push(q)}if(v||0===v){var F=te(t,v);F&&s.push(F)}if(g||0===g){var B=ne(t,g);B&&s.push(B)}if("string"===y){var z;if(!(z="date-time"===b?ue(t):"uuid"===b?ce(t):se(t)))return s;s.push(z)}else if("boolean"===y){var V=ae(t);if(!V)return s;s.push(V)}else if("number"===y){var H=re(t);if(!H)return s;s.push(H)}else if("integer"===y){var W=oe(t);if(!W)return s;s.push(W)}else if("array"===y){var J;if(!C||!t.count())return s;J=p.getIn(["items","type"]),t.forEach(function(e,t){var n;"number"===J?n=re(e):"integer"===J?n=oe(e):"string"===J&&(n=se(e)),n&&s.push({index:t,error:n})})}else if("file"===y){var K=ie(t);if(!K)return s;s.push(K)}}return s},de=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};if(/xml/.test(t)){if(!e.xml||!e.xml.name){if(e.xml=e.xml||{},!e.$$ref)return e.type||e.items||e.properties||e.additionalProperties?'\n\x3c!-- XML example cannot be generated; root element name is undefined --\x3e':null;var r=e.$$ref.match(/\S*\/(\S+)$/);e.xml.name=r[1]}return Object(k.memoizedCreateXMLExample)(e,n)}var i=Object(k.memoizedSampleFromSchema)(e,n);return"object"===f()(i)?o()(i,null,2):i},me=function(){var e={},t=A.a.location.search;if(!t)return{};if(""!=t){var n=t.substr(1).split("&");for(var r in n)n.hasOwnProperty(r)&&(r=n[r].split("="),e[decodeURIComponent(r[0])]=r[1]&&decodeURIComponent(r[1])||"")}return e},ve=function(t){return(t instanceof e?t:new e(t.toString(),"utf-8")).toString("base64")},ge={operationsSorter:{alpha:function(e,t){return e.get("path").localeCompare(t.get("path"))},method:function(e,t){return e.get("method").localeCompare(t.get("method"))}},tagsSorter:{alpha:function(e,t){return e.localeCompare(t)}}},ye=function(e){var t=[];for(var n in e){var r=e[n];void 0!==r&&""!==r&&t.push([n,"=",encodeURIComponent(r).replace(/%20/g,"+")].join(""))}return t.join("&")},be=function(e,t,n){return!!E()(n,function(n){return C()(e[n],t[n])})};function _e(e){return"string"!=typeof e||""===e?"":Object(m.sanitizeUrl)(e)}function we(e){if(!d.a.OrderedMap.isOrderedMap(e))return null;if(!e.size)return null;var t=e.find(function(e,t){return t.startsWith("2")&&u()(e.get("content")||{}).length>0}),n=e.get("default")||d.a.OrderedMap(),r=(n.get("content")||d.a.OrderedMap()).keySeq().toJS().length?n:null;return t||r}var xe=function(e){return"string"==typeof e||e instanceof String?e.trim().replace(/\s/g,"%20"):""},Ee=function(e){return j()(xe(e).replace(/%20/g,"_"))},Se=function(e){return e.filter(function(e,t){return/^x-/.test(t)})},Ce=function(e){return e.filter(function(e,t){return/^pattern|maxLength|minLength|maximum|minimum/.test(t)})};function ke(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:function(){return!0};if("object"!==f()(e)||l()(e)||null===e||!t)return e;var r=a()({},e);return u()(r).forEach(function(e){e===t&&n(r[e],e)?delete r[e]:r[e]=ke(r[e],t,n)}),r}function Oe(e){if("string"==typeof e)return e;if(e&&e.toJS&&(e=e.toJS()),"object"===f()(e)&&null!==e)try{return o()(e,null,2)}catch(t){return String(e)}return null==e?"":e.toString()}function Ae(e){return"number"==typeof e?e.toString():e}function Te(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.returnAll,r=void 0!==n&&n,o=t.allowHashes,i=void 0===o||o;if(!d.a.Map.isMap(e))throw new Error("paramToIdentifier: received a non-Im.Map parameter as input");var a=e.get("name"),s=e.get("in"),u=[];return e&&e.hashCode&&s&&a&&i&&u.push("".concat(s,".").concat(a,".hash-").concat(e.hashCode())),s&&a&&u.push("".concat(s,".").concat(a)),u.push(a),r?u:u[0]||""}function je(e,t){return Te(e,{returnAll:!0}).map(function(e){return t[e]}).filter(function(e){return void 0!==e})[0]}function Pe(){return Me(M()(32).toString("base64"))}function Ie(e){return Me(R()("sha256").update(e).digest("base64"))}function Me(e){return e.replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}}).call(this,n(64).Buffer)},function(e,t){e.exports=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}},function(e,t,n){var r=n(54);function o(e,t){for(var n=0;n1?t-1:0),o=1;o2?n-2:0),i=2;i>",i={listOf:function(e){return c(e,"List",r.List.isList)},mapOf:function(e,t){return l(e,t,"Map",r.Map.isMap)},orderedMapOf:function(e,t){return l(e,t,"OrderedMap",r.OrderedMap.isOrderedMap)},setOf:function(e){return c(e,"Set",r.Set.isSet)},orderedSetOf:function(e){return c(e,"OrderedSet",r.OrderedSet.isOrderedSet)},stackOf:function(e){return c(e,"Stack",r.Stack.isStack)},iterableOf:function(e){return c(e,"Iterable",r.Iterable.isIterable)},recordOf:function(e){return s(function(t,n,o,i,s){for(var u=arguments.length,c=Array(u>5?u-5:0),l=5;l6?u-6:0),l=6;l5?c-5:0),p=5;p5?i-5:0),s=5;s key("+l[p]+")"].concat(a));if(h instanceof Error)return h}})).apply(void 0,i);var u})}function p(e){var t=void 0===arguments[1]?"Iterable":arguments[1],n=void 0===arguments[2]?r.Iterable.isIterable:arguments[2];return s(function(r,o,i,s,u){for(var c=arguments.length,l=Array(c>5?c-5:0),p=5;p4)}function u(e){var t=e.get("swagger");return"string"==typeof t&&t.startsWith("2.0")}function c(e){return function(t,n){return function(r){return n&&n.specSelectors&&n.specSelectors.specJson?s(n.specSelectors.specJson())?a.a.createElement(e,o()({},r,n,{Ori:t})):a.a.createElement(t,r):(console.warn("OAS3 wrapper: couldn't get spec"),null)}}}},function(e,t,n){"use strict"; -/* -object-assign -(c) Sindre Sorhus -@license MIT -*/var r=Object.getOwnPropertySymbols,o=Object.prototype.hasOwnProperty,i=Object.prototype.propertyIsEnumerable;function a(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map(function(e){return t[e]}).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach(function(e){r[e]=e}),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(e){return!1}}()?Object.assign:function(e,t){for(var n,s,u=a(e),c=1;c0){var o=n.map(function(e){return console.error(e),e.line=e.fullPath?g(y,e.fullPath):null,e.path=e.fullPath?e.fullPath.join("."):null,e.level="error",e.type="thrown",e.source="resolver",A()(e,"message",{enumerable:!0,value:e.message}),e});i.newThrownErrBatch(o)}return r.updateResolved(t)})}},_e=[],we=V()(k()(S.a.mark(function e(){var t,n,r,o,i,a,s,u,c,l,p,f,h,d,m,v,g;return S.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(t=_e.system){e.next=4;break}return console.error("debResolveSubtrees: don't have a system to operate on, aborting."),e.abrupt("return");case 4:if(n=t.errActions,r=t.errSelectors,o=t.fn,i=o.resolveSubtree,a=o.AST,s=void 0===a?{}:a,u=t.specSelectors,c=t.specActions,i){e.next=8;break}return console.error("Error: Swagger-Client did not provide a `resolveSubtree` method, doing nothing."),e.abrupt("return");case 8:return l=s.getLineNumberForPath?s.getLineNumberForPath:function(){},p=u.specStr(),f=t.getConfigs(),h=f.modelPropertyMacro,d=f.parameterMacro,m=f.requestInterceptor,v=f.responseInterceptor,e.prev=11,e.next=14,_e.reduce(function(){var e=k()(S.a.mark(function e(t,o){var a,s,c,f,g,y,b;return S.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,t;case 2:return a=e.sent,s=a.resultMap,c=a.specWithCurrentSubtrees,e.next=7,i(c,o,{baseDoc:u.url(),modelPropertyMacro:h,parameterMacro:d,requestInterceptor:m,responseInterceptor:v});case 7:return f=e.sent,g=f.errors,y=f.spec,r.allErrors().size&&n.clearBy(function(e){return"thrown"!==e.get("type")||"resolver"!==e.get("source")||!e.get("fullPath").every(function(e,t){return e===o[t]||void 0===o[t]})}),j()(g)&&g.length>0&&(b=g.map(function(e){return e.line=e.fullPath?l(p,e.fullPath):null,e.path=e.fullPath?e.fullPath.join("."):null,e.level="error",e.type="thrown",e.source="resolver",A()(e,"message",{enumerable:!0,value:e.message}),e}),n.newThrownErrBatch(b)),W()(s,o,y),W()(c,o,y),e.abrupt("return",{resultMap:s,specWithCurrentSubtrees:c});case 15:case"end":return e.stop()}},e)}));return function(t,n){return e.apply(this,arguments)}}(),x.a.resolve({resultMap:(u.specResolvedSubtree([])||Object(R.Map)()).toJS(),specWithCurrentSubtrees:u.specJson().toJS()}));case 14:g=e.sent,delete _e.system,_e=[],e.next=22;break;case 19:e.prev=19,e.t0=e.catch(11),console.error(e.t0);case 22:c.updateResolvedSubtree([],g.resultMap);case 23:case"end":return e.stop()}},e,null,[[11,19]])})),35),xe=function(e){return function(t){_e.map(function(e){return e.join("@@")}).indexOf(e.join("@@"))>-1||(_e.push(e),_e.system=t,we())}};function Ee(e,t,n,r,o){return{type:X,payload:{path:e,value:r,paramName:t,paramIn:n,isXml:o}}}function Se(e,t,n,r){return{type:X,payload:{path:e,param:t,value:n,isXml:r}}}var Ce=function(e,t){return{type:le,payload:{path:e,value:t}}},ke=function(){return{type:le,payload:{path:[],value:Object(R.Map)()}}},Oe=function(e,t){return{type:ee,payload:{pathMethod:e,isOAS3:t}}},Ae=function(e,t,n,r){return{type:Q,payload:{pathMethod:e,paramName:t,paramIn:n,includeEmptyValue:r}}};function Te(e){return{type:se,payload:{pathMethod:e}}}function je(e,t){return{type:ue,payload:{path:e,value:t,key:"consumes_value"}}}function Pe(e,t){return{type:ue,payload:{path:e,value:t,key:"produces_value"}}}var Ie=function(e,t,n){return{payload:{path:e,method:t,res:n},type:te}},Me=function(e,t,n){return{payload:{path:e,method:t,req:n},type:ne}},Ne=function(e,t,n){return{payload:{path:e,method:t,req:n},type:re}},Re=function(e){return{payload:e,type:oe}},De=function(e){return function(t){var n=t.fn,r=t.specActions,o=t.specSelectors,i=t.getConfigs,a=t.oas3Selectors,s=e.pathName,u=e.method,c=e.operation,l=i(),p=l.requestInterceptor,f=l.responseInterceptor,h=c.toJS();if(c&&c.get("parameters")&&c.get("parameters").filter(function(e){return e&&!0===e.get("allowEmptyValue")}).forEach(function(t){if(o.parameterInclusionSettingFor([s,u],t.get("name"),t.get("in"))){e.parameters=e.parameters||{};var n=Object(J.C)(t,e.parameters);(!n||n&&0===n.size)&&(e.parameters[t.get("name")]="")}}),e.contextUrl=L()(o.url()).toString(),h&&h.operationId?e.operationId=h.operationId:h&&s&&u&&(e.operationId=n.opId(h,s,u)),o.isOAS3()){var d="".concat(s,":").concat(u);e.server=a.selectedServer(d)||a.selectedServer();var m=a.serverVariables({server:e.server,namespace:d}).toJS(),g=a.serverVariables({server:e.server}).toJS();e.serverVariables=_()(m).length?m:g,e.requestContentType=a.requestContentType(s,u),e.responseContentType=a.responseContentType(s,u)||"*/*";var b=a.requestBodyValue(s,u);Object(J.t)(b)?e.requestBody=JSON.parse(b):b&&b.toJS?e.requestBody=b.toJS():e.requestBody=b}var w=y()({},e);w=n.buildRequest(w),r.setRequest(e.pathName,e.method,w);e.requestInterceptor=function(t){var n=p.apply(this,[t]),o=y()({},n);return r.setMutatedRequest(e.pathName,e.method,o),n},e.responseInterceptor=f;var x=v()();return n.execute(e).then(function(t){t.duration=v()()-x,r.setResponse(e.pathName,e.method,t)}).catch(function(t){console.error(t),r.setResponse(e.pathName,e.method,{error:!0,err:q()(t)})})}},Le=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.path,n=e.method,r=d()(e,["path","method"]);return function(e){var o=e.fn.fetch,i=e.specSelectors,a=e.specActions,s=i.specJsonWithResolvedSubtrees().toJS(),u=i.operationScheme(t,n),c=i.contentTypeValues([t,n]).toJS(),l=c.requestContentType,p=c.responseContentType,f=/xml/i.test(l),h=i.parameterValues([t,n],f).toJS();return a.executeRequest(Y({},r,{fetch:o,spec:s,pathName:t,method:n,parameters:h,requestContentType:l,scheme:u,responseContentType:p}))}};function Ue(e,t){return{type:ie,payload:{path:e,method:t}}}function qe(e,t){return{type:ae,payload:{path:e,method:t}}}function Fe(e,t,n){return{type:pe,payload:{scheme:e,path:t,method:n}}}},function(e,t,n){var r=n(32),o=n(22),i=n(63),a=n(77),s=n(75),u=function(e,t,n){var c,l,p,f=e&u.F,h=e&u.G,d=e&u.S,m=e&u.P,v=e&u.B,g=e&u.W,y=h?o:o[t]||(o[t]={}),b=y.prototype,_=h?r:d?r[t]:(r[t]||{}).prototype;for(c in h&&(n=t),n)(l=!f&&_&&void 0!==_[c])&&s(y,c)||(p=l?_[c]:n[c],y[c]=h&&"function"!=typeof _[c]?n[c]:v&&l?i(p,r):g&&_[c]==p?function(e){var t=function(t,n,r){if(this instanceof e){switch(arguments.length){case 0:return new e;case 1:return new e(t);case 2:return new e(t,n)}return new e(t,n,r)}return e.apply(this,arguments)};return t.prototype=e.prototype,t}(p):m&&"function"==typeof p?i(Function.call,p):p,m&&((y.virtual||(y.virtual={}))[c]=p,e&u.R&&b&&!b[c]&&a(b,c,p)))};u.F=1,u.G=2,u.S=4,u.P=8,u.B=16,u.W=32,u.U=64,u.R=128,e.exports=u},function(e,t,n){"use strict";var r=n(138),o=["kind","resolve","construct","instanceOf","predicate","represent","defaultStyle","styleAliases"],i=["scalar","sequence","mapping"];e.exports=function(e,t){var n,a;if(t=t||{},Object.keys(t).forEach(function(t){if(-1===o.indexOf(t))throw new r('Unknown option "'+t+'" is met in definition of "'+e+'" YAML type.')}),this.tag=e,this.kind=t.kind||null,this.resolve=t.resolve||function(){return!0},this.construct=t.construct||function(e){return e},this.instanceOf=t.instanceOf||null,this.predicate=t.predicate||null,this.represent=t.represent||null,this.defaultStyle=t.defaultStyle||null,this.styleAliases=(n=t.styleAliases||null,a={},null!==n&&Object.keys(n).forEach(function(e){n[e].forEach(function(t){a[String(t)]=e})}),a),-1===i.indexOf(this.kind))throw new r('Unknown kind "'+this.kind+'" is specified for "'+e+'" YAML type.')}},function(e,t){var n=e.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},function(e,t,n){var r=n(197)("wks"),o=n(199),i=n(41).Symbol,a="function"==typeof i;(e.exports=function(e){return r[e]||(r[e]=a&&i[e]||(a?i:o)("Symbol."+e))}).store=r},function(e,t,n){var r=n(214)("wks"),o=n(159),i=n(32).Symbol,a="function"==typeof i;(e.exports=function(e){return r[e]||(r[e]=a&&i[e]||(a?i:o)("Symbol."+e))}).store=r},function(e,t,n){var r=n(41),o=n(72),i=n(81),a=n(97),s=n(153),u=function(e,t,n){var c,l,p,f,h=e&u.F,d=e&u.G,m=e&u.S,v=e&u.P,g=e&u.B,y=d?r:m?r[t]||(r[t]={}):(r[t]||{}).prototype,b=d?o:o[t]||(o[t]={}),_=b.prototype||(b.prototype={});for(c in d&&(n=t),n)p=((l=!h&&y&&void 0!==y[c])?y:n)[c],f=g&&l?s(p,r):v&&"function"==typeof p?s(Function.call,p):p,y&&a(y,c,p,e&u.U),b[c]!=p&&i(b,c,f),v&&_[c]!=p&&(_[c]=p)};r.core=o,u.F=1,u.G=2,u.S=4,u.P=8,u.B=16,u.W=32,u.U=64,u.R=128,e.exports=u},function(e,t){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(e){"object"==typeof window&&(n=window)}e.exports=n},function(e,t){var n=Array.isArray;e.exports=n},function(e,t,n){"use strict";var r=!("undefined"==typeof window||!window.document||!window.document.createElement),o={canUseDOM:r,canUseWorkers:"undefined"!=typeof Worker,canUseEventListeners:r&&!(!window.addEventListener&&!window.attachEvent),canUseViewport:r&&!!window.screen,isInWorker:!r};e.exports=o},function(e,t,n){"use strict";var r=Object.prototype.hasOwnProperty;function o(e,t){return!!e&&r.call(e,t)}var i=/\\([\\!"#$%&'()*+,.\/:;<=>?@[\]^_`{|}~-])/g;function a(e){return!(e>=55296&&e<=57343)&&(!(e>=64976&&e<=65007)&&(65535!=(65535&e)&&65534!=(65535&e)&&(!(e>=0&&e<=8)&&(11!==e&&(!(e>=14&&e<=31)&&(!(e>=127&&e<=159)&&!(e>1114111)))))))}function s(e){if(e>65535){var t=55296+((e-=65536)>>10),n=56320+(1023&e);return String.fromCharCode(t,n)}return String.fromCharCode(e)}var u=/&([a-z#][a-z0-9]{1,31});/gi,c=/^#((?:x[a-f0-9]{1,8}|[0-9]{1,8}))/i,l=n(463);function p(e,t){var n=0;return o(l,t)?l[t]:35===t.charCodeAt(0)&&c.test(t)&&a(n="x"===t[1].toLowerCase()?parseInt(t.slice(2),16):parseInt(t.slice(1),10))?s(n):e}var f=/[&<>"]/,h=/[&<>"]/g,d={"&":"&","<":"<",">":">",'"':"""};function m(e){return d[e]}t.assign=function(e){return[].slice.call(arguments,1).forEach(function(t){if(t){if("object"!=typeof t)throw new TypeError(t+"must be object");Object.keys(t).forEach(function(n){e[n]=t[n]})}}),e},t.isString=function(e){return"[object String]"===function(e){return Object.prototype.toString.call(e)}(e)},t.has=o,t.unescapeMd=function(e){return e.indexOf("\\")<0?e:e.replace(i,"$1")},t.isValidEntityCode=a,t.fromCodePoint=s,t.replaceEntities=function(e){return e.indexOf("&")<0?e:e.replace(u,p)},t.escapeHtml=function(e){return f.test(e)?e.replace(h,m):e}},function(e,t,n){var r=n(55),o=n(771);e.exports=function(e,t){if(null==e)return{};var n,i,a=o(e,t);if(r){var s=r(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}},function(e,t){var n=e.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},function(e,t,n){var r=n(35),o=n(99),i=n(73),a=/"/g,s=function(e,t,n,r){var o=String(i(e)),s="<"+t;return""!==n&&(s+=" "+n+'="'+String(r).replace(a,""")+'"'),s+">"+o+""};e.exports=function(e,t){var n={};n[e]=t(s),r(r.P+r.F*o(function(){var t=""[e]('"');return t!==t.toLowerCase()||t.split('"').length>3}),"String",n)}},function(e,t){e.exports=function(e){return"object"==typeof e?null!==e:"function"==typeof e}},function(e,t,n){"use strict";n.r(t),n.d(t,"NEW_THROWN_ERR",function(){return i}),n.d(t,"NEW_THROWN_ERR_BATCH",function(){return a}),n.d(t,"NEW_SPEC_ERR",function(){return s}),n.d(t,"NEW_SPEC_ERR_BATCH",function(){return u}),n.d(t,"NEW_AUTH_ERR",function(){return c}),n.d(t,"CLEAR",function(){return l}),n.d(t,"CLEAR_BY",function(){return p}),n.d(t,"newThrownErr",function(){return f}),n.d(t,"newThrownErrBatch",function(){return h}),n.d(t,"newSpecErr",function(){return d}),n.d(t,"newSpecErrBatch",function(){return m}),n.d(t,"newAuthErr",function(){return v}),n.d(t,"clear",function(){return g}),n.d(t,"clearBy",function(){return y});var r=n(119),o=n.n(r),i="err_new_thrown_err",a="err_new_thrown_err_batch",s="err_new_spec_err",u="err_new_spec_err_batch",c="err_new_auth_err",l="err_clear",p="err_clear_by";function f(e){return{type:i,payload:o()(e)}}function h(e){return{type:a,payload:e}}function d(e){return{type:s,payload:e}}function m(e){return{type:u,payload:e}}function v(e){return{type:c,payload:e}}function g(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{type:l,payload:e}}function y(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:function(){return!0};return{type:p,payload:e}}},function(e,t,n){var r=n(98);e.exports=function(e){if(!r(e))throw TypeError(e+" is not an object!");return e}},function(e,t,n){var r=n(43);e.exports=function(e){if(!r(e))throw TypeError(e+" is not an object!");return e}},function(e,t){"function"==typeof Object.create?e.exports=function(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})}:e.exports=function(e,t){e.super_=t;var n=function(){};n.prototype=t.prototype,e.prototype=new n,e.prototype.constructor=e}},function(e,t,n){var r=n(64),o=r.Buffer;function i(e,t){for(var n in e)t[n]=e[n]}function a(e,t,n){return o(e,t,n)}o.from&&o.alloc&&o.allocUnsafe&&o.allocUnsafeSlow?e.exports=r:(i(r,t),t.Buffer=a),i(o,a),a.from=function(e,t,n){if("number"==typeof e)throw new TypeError("Argument must not be a number");return o(e,t,n)},a.alloc=function(e,t,n){if("number"!=typeof e)throw new TypeError("Argument must be a number");var r=o(e);return void 0!==t?"string"==typeof n?r.fill(t,n):r.fill(t):r.fill(0),r},a.allocUnsafe=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return o(e)},a.allocUnsafeSlow=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return r.SlowBuffer(e)}},function(e,t,n){var r=n(46),o=n(349),i=n(218),a=Object.defineProperty;t.f=n(50)?Object.defineProperty:function(e,t,n){if(r(e),t=i(t,!0),r(n),o)try{return a(e,t,n)}catch(e){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(e[t]=n.value),e}},function(e,t,n){e.exports=!n(82)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(e,t,n){var r=n(366),o="object"==typeof self&&self&&self.Object===Object&&self,i=r||o||Function("return this")();e.exports=i},function(e,t){e.exports=function(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}},function(e,t,n){"use strict";e.exports={debugTool:null}},function(e,t,n){e.exports=n(573)},function(e,t,n){e.exports=n(770)},function(e,t,n){e.exports=function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=45)}([function(e,t){e.exports=n(17)},function(e,t){e.exports=n(14)},function(e,t){e.exports=n(26)},function(e,t){e.exports=n(16)},function(e,t){e.exports=n(123)},function(e,t){e.exports=n(60)},function(e,t){e.exports=n(61)},function(e,t){e.exports=n(55)},function(e,t){e.exports=n(2)},function(e,t){e.exports=n(54)},function(e,t){e.exports=n(94)},function(e,t){e.exports=n(28)},function(e,t){e.exports=n(930)},function(e,t){e.exports=n(12)},function(e,t){e.exports=n(192)},function(e,t){e.exports=n(936)},function(e,t){e.exports=n(93)},function(e,t){e.exports=n(193)},function(e,t){e.exports=n(939)},function(e,t){e.exports=n(943)},function(e,t){e.exports=n(944)},function(e,t){e.exports=n(92)},function(e,t){e.exports=n(13)},function(e,t){e.exports=n(146)},function(e,t){e.exports=n(4)},function(e,t){e.exports=n(5)},function(e,t){e.exports=n(946)},function(e,t){e.exports=n(421)},function(e,t){e.exports=n(949)},function(e,t){e.exports=n(52)},function(e,t){e.exports=n(64)},function(e,t){e.exports=n(283)},function(e,t){e.exports=n(272)},function(e,t){e.exports=n(950)},function(e,t){e.exports=n(145)},function(e,t){e.exports=n(951)},function(e,t){e.exports=n(959)},function(e,t){e.exports=n(960)},function(e,t){e.exports=n(961)},function(e,t){e.exports=n(40)},function(e,t){e.exports=n(264)},function(e,t){e.exports=n(37)},function(e,t){e.exports=n(964)},function(e,t){e.exports=n(965)},function(e,t){e.exports=n(966)},function(e,t,n){e.exports=n(50)},function(e,t){e.exports=n(967)},function(e,t){e.exports=n(968)},function(e,t){e.exports=n(969)},function(e,t){e.exports=n(970)},function(e,t,n){"use strict";n.r(t);var r={};n.r(r),n.d(r,"path",function(){return mn}),n.d(r,"query",function(){return vn}),n.d(r,"header",function(){return yn}),n.d(r,"cookie",function(){return bn});var o=n(9),i=n.n(o),a=n(10),s=n.n(a),u=n(5),c=n.n(u),l=n(6),p=n.n(l),f=n(7),h=n.n(f),d=n(0),m=n.n(d),v=n(8),g=n.n(v),y=(n(46),n(15)),b=n.n(y),_=n(20),w=n.n(_),x=n(12),E=n.n(x),S=n(4),C=n.n(S),k=n(22),O=n.n(k),A=n(11),T=n.n(A),j=n(2),P=n.n(j),I=n(1),M=n.n(I),N=n(17),R=n.n(N),D=(n(47),n(26)),L=n.n(D),U=n(23),q=n.n(U),F=n(31),B=n.n(F),z={serializeRes:J,mergeInQueryOrForm:Z};function V(e){return H.apply(this,arguments)}function H(){return(H=R()(C.a.mark(function e(t){var n,r,o,i,a,s=arguments;return C.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(n=s.length>1&&void 0!==s[1]?s[1]:{},"object"===P()(t)&&(t=(n=t).url),n.headers=n.headers||{},z.mergeInQueryOrForm(n),n.headers&&m()(n.headers).forEach(function(e){var t=n.headers[e];"string"==typeof t&&(n.headers[e]=t.replace(/\n+/g," "))}),!n.requestInterceptor){e.next=12;break}return e.next=8,n.requestInterceptor(n);case 8:if(e.t0=e.sent,e.t0){e.next=11;break}e.t0=n;case 11:n=e.t0;case 12:return r=n.headers["content-type"]||n.headers["Content-Type"],/multipart\/form-data/i.test(r)&&(delete n.headers["content-type"],delete n.headers["Content-Type"]),e.prev=14,e.next=17,(n.userFetch||fetch)(n.url,n);case 17:return o=e.sent,e.next=20,z.serializeRes(o,t,n);case 20:if(o=e.sent,!n.responseInterceptor){e.next=28;break}return e.next=24,n.responseInterceptor(o);case 24:if(e.t1=e.sent,e.t1){e.next=27;break}e.t1=o;case 27:o=e.t1;case 28:e.next=38;break;case 30:if(e.prev=30,e.t2=e.catch(14),o){e.next=34;break}throw e.t2;case 34:throw(i=new Error(o.statusText)).statusCode=i.status=o.status,i.responseError=e.t2,i;case 38:if(o.ok){e.next=43;break}throw(a=new Error(o.statusText)).statusCode=a.status=o.status,a.response=o,a;case 43:return e.abrupt("return",o);case 44:case"end":return e.stop()}},e,null,[[14,30]])}))).apply(this,arguments)}var W=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return/(json|xml|yaml|text)\b/.test(e)};function J(e,t){var n=(arguments.length>2&&void 0!==arguments[2]?arguments[2]:{}).loadSpec,r=void 0!==n&&n,o={ok:e.ok,url:e.url||t,status:e.status,statusText:e.statusText,headers:K(e.headers)},i=o.headers["content-type"],a=r||W(i);return(a?e.text:e.blob||e.buffer).call(e).then(function(e){if(o.text=e,o.data=e,a)try{var t=function(e,t){return t&&(0===t.indexOf("application/json")||t.indexOf("+json")>0)?JSON.parse(e):q.a.safeLoad(e)}(e,i);o.body=t,o.obj=t}catch(e){o.parseError=e}return o})}function K(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t={};return"function"==typeof e.forEach?(e.forEach(function(e,n){void 0!==t[n]?(t[n]=M()(t[n])?t[n]:[t[n]],t[n].push(e)):t[n]=e}),t):t}function Y(e,t){return t||"undefined"==typeof navigator||(t=navigator),t&&"ReactNative"===t.product?!(!e||"object"!==P()(e)||"string"!=typeof e.uri):"undefined"!=typeof File?e instanceof File:null!==e&&"object"===P()(e)&&"function"==typeof e.pipe}function $(e,t){var n=e.collectionFormat,r=e.allowEmptyValue,o="object"===P()(e)?e.value:e;if(void 0===o&&r)return"";if(Y(o)||"boolean"==typeof o)return o;var i=encodeURIComponent;return t&&(i=B()(o)?function(e){return e}:function(e){return T()(e)}),"object"!==P()(o)||M()(o)?M()(o)?M()(o)&&!n?o.map(i).join(","):"multi"===n?o.map(i):o.map(i).join({csv:",",ssv:"%20",tsv:"%09",pipes:"|"}[n]):i(o):""}function G(e){var t=m()(e).reduce(function(t,n){var r,o=e[n],i=!!o.skipEncoding,a=i?n:encodeURIComponent(n),s=(r=o)&&"object"===P()(r)&&!M()(o);return t[a]=$(s?o:{value:o},i),t},{});return L.a.stringify(t,{encode:!1,indices:!1})||""}function Z(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.url,r=void 0===t?"":t,o=e.query,i=e.form;if(i){var a=m()(i).some(function(e){return Y(i[e].value)}),s=e.headers["content-type"]||e.headers["Content-Type"];if(a||/multipart\/form-data/i.test(s)){var u=n(48);e.body=new u,m()(i).forEach(function(t){e.body.append(t,$(i[t],!0))})}else e.body=G(i);delete e.form}if(o){var c=r.split("?"),l=O()(c,2),p=l[0],f=l[1],h="";if(f){var d=L.a.parse(f);m()(o).forEach(function(e){return delete d[e]}),h=L.a.stringify(d,{encode:!0})}var v=function(){for(var e=arguments.length,t=new Array(e),n=0;n0){var o=t(e,n[n.length-1],n);o&&(r=r.concat(o))}if(M()(e)){var i=e.map(function(e,r){return Ce(e,t,n.concat(r))});i&&(r=r.concat(i))}else if(Te(e)){var a=m()(e).map(function(r){return Ce(e[r],t,n.concat(r))});a&&(r=r.concat(a))}return r=Oe(r)}function ke(e){return M()(e)?e:[e]}function Oe(e){var t;return(t=[]).concat.apply(t,he()(e.map(function(e){return M()(e)?Oe(e):e})))}function Ae(e){return e.filter(function(e){return void 0!==e})}function Te(e){return e&&"object"===P()(e)}function je(e){return e&&"function"==typeof e}function Pe(e){if(Ne(e)){var t=e.op;return"add"===t||"remove"===t||"replace"===t}return!1}function Ie(e){return Pe(e)||Ne(e)&&"mutation"===e.type}function Me(e){return Ie(e)&&("add"===e.op||"replace"===e.op||"merge"===e.op||"mergeDeep"===e.op)}function Ne(e){return e&&"object"===P()(e)}function Re(e,t){try{return me.a.getValueByPointer(e,t)}catch(e){return console.error(e),{}}}var De=n(35),Le=n.n(De),Ue=n(36),qe=n(28),Fe=n.n(qe);function Be(e,t){function n(){Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=(new Error).stack;for(var e=arguments.length,n=new Array(e),r=0;r-1&&-1===We.indexOf(n)||Je.indexOf(r)>-1||Ke.some(function(e){return r.indexOf(e)>-1})}function $e(e,t){var n=e.split("#"),r=O()(n,2),o=r[0],i=r[1],a=E.a.resolve(o||"",t||"");return i?"".concat(a,"#").concat(i):a}var Ge="application/json, application/yaml",Ze=new RegExp("^([a-z]+://|//)","i"),Xe=Be("JSONRefError",function(e,t,n){this.originalError=n,ie()(this,t||{})}),Qe={},et=new Le.a,tt=[function(e){return"paths"===e[0]&&"responses"===e[3]&&"content"===e[5]&&"example"===e[7]},function(e){return"paths"===e[0]&&"requestBody"===e[3]&&"content"===e[4]&&"example"===e[6]}],nt={key:"$ref",plugin:function(e,t,n,r){var o=r.getInstance(),i=n.slice(0,-1);if(!Ye(i)&&(a=i,!tt.some(function(e){return e(a)}))){var a,s=r.getContext(n).baseDoc;if("string"!=typeof e)return new Xe("$ref: must be a string (JSON-Ref)",{$ref:e,baseDoc:s,fullPath:n});var u,c,l,p=st(e),f=p[0],h=p[1]||"";try{u=s||f?it(f,s):null}catch(t){return at(t,{pointer:h,$ref:e,basePath:u,fullPath:n})}if(function(e,t,n,r){var o=et.get(r);o||(o={},et.set(r,o));var i=function(e){if(0===e.length)return"";return"/".concat(e.map(ht).join("/"))}(n),a="".concat(t||"","#").concat(e),s=i.replace(/allOf\/\d+\/?/g,""),u=r.contextTree.get([]).baseDoc;if(t==u&&mt(s,e))return!0;var c="";if(n.some(function(e){return c="".concat(c,"/").concat(ht(e)),o[c]&&o[c].some(function(e){return mt(e,a)||mt(a,e)})}))return!0;o[s]=(o[s]||[]).concat(a)}(h,u,i,r)&&!o.useCircularStructures){var d=$e(e,u);return e===d?null:_e.replace(n,d)}if(null==u?(l=pt(h),void 0===(c=r.get(l))&&(c=new Xe("Could not resolve reference: ".concat(e),{pointer:h,$ref:e,baseDoc:s,fullPath:n}))):c=null!=(c=ut(u,h)).__value?c.__value:c.catch(function(t){throw at(t,{pointer:h,$ref:e,baseDoc:s,fullPath:n})}),c instanceof Error)return[_e.remove(n),c];var v=$e(e,u),g=_e.replace(i,c,{$$ref:v});if(u&&u!==s)return[g,_e.context(i,{baseDoc:u})];try{if(!function(e,t){var n=[e];return t.path.reduce(function(e,t){return n.push(e[t]),e[t]},e),function e(t){return _e.isObject(t)&&(n.indexOf(t)>=0||m()(t).some(function(n){return e(t[n])}))}(t.value)}(r.state,g)||o.useCircularStructures)return g}catch(e){return null}}}},rt=ie()(nt,{docCache:Qe,absoluteify:it,clearCache:function(e){void 0!==e?delete Qe[e]:m()(Qe).forEach(function(e){delete Qe[e]})},JSONRefError:Xe,wrapError:at,getDoc:ct,split:st,extractFromDoc:ut,fetchJSON:function(e){return Object(Ue.fetch)(e,{headers:{Accept:Ge},loadSpec:!0}).then(function(e){return e.text()}).then(function(e){return q.a.safeLoad(e)})},extract:lt,jsonPointerToArray:pt,unescapeJsonPointerToken:ft}),ot=rt;function it(e,t){if(!Ze.test(e)){if(!t)throw new Xe("Tried to resolve a relative URL, without having a basePath. path: '".concat(e,"' basePath: '").concat(t,"'"));return E.a.resolve(t,e)}return e}function at(e,t){var n;return n=e&&e.response&&e.response.body?"".concat(e.response.body.code," ").concat(e.response.body.message):e.message,new Xe("Could not resolve reference: ".concat(n),t,e)}function st(e){return(e+"").split("#")}function ut(e,t){var n=Qe[e];if(n&&!_e.isPromise(n))try{var r=lt(t,n);return ie()(Q.a.resolve(r),{__value:r})}catch(e){return Q.a.reject(e)}return ct(e).then(function(e){return lt(t,e)})}function ct(e){var t=Qe[e];return t?_e.isPromise(t)?t:Q.a.resolve(t):(Qe[e]=rt.fetchJSON(e).then(function(t){return Qe[e]=t,t}),Qe[e])}function lt(e,t){var n=pt(e);if(n.length<1)return t;var r=_e.getIn(t,n);if(void 0===r)throw new Xe("Could not resolve pointer: ".concat(e," does not exist in document"),{pointer:e});return r}function pt(e){if("string"!=typeof e)throw new TypeError("Expected a string, got a ".concat(P()(e)));return"/"===e[0]&&(e=e.substr(1)),""===e?[]:e.split("/").map(ft)}function ft(e){return"string"!=typeof e?e:Fe.a.unescape(e.replace(/~1/g,"/").replace(/~0/g,"~"))}function ht(e){return Fe.a.escape(e.replace(/~/g,"~0").replace(/\//g,"~1"))}var dt=function(e){return!e||"/"===e||"#"===e};function mt(e,t){if(dt(t))return!0;var n=e.charAt(t.length),r=t.slice(-1);return 0===e.indexOf(t)&&(!n||"/"===n||"#"===n)&&"#"!==r}var vt={key:"allOf",plugin:function(e,t,n,r,o){if(!o.meta||!o.meta.$$ref){var i=n.slice(0,-1);if(!Ye(i)){if(!M()(e)){var a=new TypeError("allOf must be an array");return a.fullPath=n,a}var s=!1,u=o.value;i.forEach(function(e){u&&(u=u[e])}),delete(u=ie()({},u)).allOf;var c=[];return c.push(r.replace(i,{})),e.forEach(function(e,t){if(!r.isObject(e)){if(s)return null;s=!0;var o=new TypeError("Elements in allOf must be objects");return o.fullPath=n,c.push(o)}c.push(r.mergeDeep(i,e));var a=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=n.specmap,o=n.getBaseUrlForNodePath,i=void 0===o?function(e){return r.getContext([].concat(he()(t),he()(e))).baseDoc}:o,a=n.targetKeys,s=void 0===a?["$ref","$$ref"]:a,u=[];return Ve()(e).forEach(function(){if(s.indexOf(this.key)>-1){var e=this.path,n=t.concat(this.path),o=$e(this.node,i(e));u.push(r.replace(n,o))}}),u}(e,n.slice(0,-1),{getBaseUrlForNodePath:function(e){return r.getContext([].concat(he()(n),[t],he()(e))).baseDoc},specmap:r});c.push.apply(c,he()(a))}),c.push(r.mergeDeep(i,u)),u.$$ref||c.push(r.remove([].concat(i,"$$ref"))),c}}}},gt={key:"parameters",plugin:function(e,t,n,r,o){if(M()(e)&&e.length){var i=ie()([],e),a=n.slice(0,-1),s=ie()({},_e.getIn(r.spec,a));return e.forEach(function(e,t){try{i[t].default=r.parameterMacro(s,e)}catch(e){var o=new Error(e);return o.fullPath=n,o}}),_e.replace(n,i)}return _e.replace(n,e)}},yt={key:"properties",plugin:function(e,t,n,r){var o=ie()({},e);for(var i in e)try{o[i].default=r.modelPropertyMacro(o[i])}catch(e){var a=new Error(e);return a.fullPath=n,a}return _e.replace(n,o)}};function bt(e,t){var n=m()(e);if(h.a){var r=h()(e);t&&(r=r.filter(function(t){return p()(e,t).enumerable})),n.push.apply(n,r)}return n}var _t=function(){function e(t){se()(this,e),this.root=wt(t||{})}return ce()(e,[{key:"set",value:function(e,t){var n=this.getParent(e,!0);if(n){var r=e[e.length-1],o=n.children;o[r]?xt(o[r],t,n):o[r]=wt(t,n)}else xt(this.root,t,null)}},{key:"get",value:function(e){if((e=e||[]).length<1)return this.root.value;for(var t,n,r=this.root,o=0;o1?n-1:0),o=1;o1?n-1:0),o=1;o0})}},{key:"nextPromisedPatch",value:function(){if(this.promisedPatches.length>0)return Q.a.race(this.promisedPatches.map(function(e){return e.value}))}},{key:"getPluginHistory",value:function(e){var t=this.getPluginName(e);return this.pluginHistory[t]||[]}},{key:"getPluginRunCount",value:function(e){return this.getPluginHistory(e).length}},{key:"getPluginHistoryTip",value:function(e){var t=this.getPluginHistory(e);return t&&t[t.length-1]||{}}},{key:"getPluginMutationIndex",value:function(e){var t=this.getPluginHistoryTip(e).mutationIndex;return"number"!=typeof t?-1:t}},{key:"getPluginName",value:function(e){return e.pluginName}},{key:"updatePluginHistory",value:function(e,t){var n=this.getPluginName(e);(this.pluginHistory[n]=this.pluginHistory[n]||[]).push(t)}},{key:"updatePatches",value:function(e,t){var n=this;_e.normalizeArray(e).forEach(function(e){if(e instanceof Error)n.errors.push(e);else try{if(!_e.isObject(e))return void n.debug("updatePatches","Got a non-object patch",e);if(n.showDebug&&n.allPatches.push(e),_e.isPromise(e.value))return n.promisedPatches.push(e),void n.promisedPatchThen(e);if(_e.isContextPatch(e))return void n.setContext(e.path,e.value);if(_e.isMutation(e))return void n.updateMutations(e)}catch(e){console.error(e),n.errors.push(e)}})}},{key:"updateMutations",value:function(e){"object"===P()(e.value)&&!M()(e.value)&&this.allowMetaPatches&&(e.value=ie()({},e.value));var t=_e.applyPatch(this.state,e,{allowMetaPatches:this.allowMetaPatches});t&&(this.mutations.push(e),this.state=t)}},{key:"removePromisedPatch",value:function(e){var t=this.promisedPatches.indexOf(e);t<0?this.debug("Tried to remove a promisedPatch that isn't there!"):this.promisedPatches.splice(t,1)}},{key:"promisedPatchThen",value:function(e){var t=this;return e.value=e.value.then(function(n){var r=ie()({},e,{value:n});t.removePromisedPatch(e),t.updatePatches(r)}).catch(function(n){t.removePromisedPatch(e),t.updatePatches(n)})}},{key:"getMutations",value:function(e,t){return e=e||0,"number"!=typeof t&&(t=this.mutations.length),this.mutations.slice(e,t)}},{key:"getCurrentMutations",value:function(){return this.getMutationsForPlugin(this.getCurrentPlugin())}},{key:"getMutationsForPlugin",value:function(e){var t=this.getPluginMutationIndex(e);return this.getMutations(t+1)}},{key:"getCurrentPlugin",value:function(){return this.currentPlugin}},{key:"getPatchesOfType",value:function(e,t){return e.filter(t)}},{key:"getLib",value:function(){return this.libMethods}},{key:"_get",value:function(e){return _e.getIn(this.state,e)}},{key:"_getContext",value:function(e){return this.contextTree.get(e)}},{key:"setContext",value:function(e,t){return this.contextTree.set(e,t)}},{key:"_hasRun",value:function(e){return this.getPluginRunCount(this.getCurrentPlugin())>(e||0)}},{key:"_clone",value:function(e){return JSON.parse(T()(e))}},{key:"dispatch",value:function(){var e=this,t=this,n=this.nextPlugin();if(!n){var r=this.nextPromisedPatch();if(r)return r.then(function(){return e.dispatch()}).catch(function(){return e.dispatch()});var o={spec:this.state,errors:this.errors};return this.showDebug&&(o.patches=this.allPatches),Q.a.resolve(o)}if(t.pluginCount=t.pluginCount||{},t.pluginCount[n]=(t.pluginCount[n]||0)+1,t.pluginCount[n]>100)return Q.a.resolve({spec:t.state,errors:t.errors.concat(new Error("We've reached a hard limit of ".concat(100," plugin runs")))});if(n!==this.currentPlugin&&this.promisedPatches.length){var i=this.promisedPatches.map(function(e){return e.value});return Q.a.all(i.map(function(e){return e.then(Function,Function)})).then(function(){return e.dispatch()})}return function(){t.currentPlugin=n;var e=t.getCurrentMutations(),r=t.mutations.length-1;try{if(n.isGenerator){var o=!0,i=!1,s=void 0;try{for(var u,c=te()(n(e,t.getLib()));!(o=(u=c.next()).done);o=!0){a(u.value)}}catch(e){i=!0,s=e}finally{try{o||null==c.return||c.return()}finally{if(i)throw s}}}else{a(n(e,t.getLib()))}}catch(e){console.error(e),a([ie()(re()(e),{plugin:n})])}finally{t.updatePluginHistory(n,{mutationIndex:r})}return t.dispatch()}();function a(e){e&&(e=_e.fullyNormalizeArray(e),t.updatePatches(e,n))}}}]),e}();var St={refs:ot,allOf:vt,parameters:gt,properties:yt},Ct=n(29),kt=n.n(Ct),Ot=function(e){return String.prototype.toLowerCase.call(e)},At=function(e){return e.replace(/[^\w]/gi,"_")};function Tt(e){var t=e.openapi;return!!t&&w()(t,"3")}function jt(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"",r=(arguments.length>3&&void 0!==arguments[3]?arguments[3]:{}).v2OperationIdCompatibilityMode;return e&&"object"===P()(e)?(e.operationId||"").replace(/\s/g,"").length?At(e.operationId):function(e,t){if((arguments.length>2&&void 0!==arguments[2]?arguments[2]:{}).v2OperationIdCompatibilityMode){var n="".concat(t.toLowerCase(),"_").concat(e).replace(/[\s!@#$%^&*()_+=[{\]};:<>|.\/?,\\'""-]/g,"_");return(n=n||"".concat(e.substring(1),"_").concat(t)).replace(/((_){2,})/g,"_").replace(/^(_)*/g,"").replace(/([_])*$/g,"")}return"".concat(Ot(t)).concat(At(e))}(t,n,{v2OperationIdCompatibilityMode:r}):null}function Pt(e,t){return"".concat(Ot(t),"-").concat(e)}function It(e,t){return e&&e.paths?function(e,t){return Mt(e,t,!0)||null}(e,function(e){var n=e.pathName,r=e.method,o=e.operation;if(!o||"object"!==P()(o))return!1;var i=o.operationId;return[jt(o,n,r),Pt(n,r),i].some(function(e){return e&&e===t})}):null}function Mt(e,t,n){if(!e||"object"!==P()(e)||!e.paths||"object"!==P()(e.paths))return null;var r=e.paths;for(var o in r)for(var i in r[o])if("PARAMETERS"!==i.toUpperCase()){var a=r[o][i];if(a&&"object"===P()(a)){var s={spec:e,pathName:o,method:i.toUpperCase(),operation:a},u=t(s);if(n&&u)return s}}}function Nt(e){var t=e.spec,n=t.paths,r={};if(!n||t.$$normalized)return e;for(var o in n){var i=n[o];if(kt()(i)){var a=i.parameters,s=function(e){var n=i[e];if(!kt()(n))return"continue";var s=jt(n,o,e);if(s){r[s]?r[s].push(n):r[s]=[n];var u=r[s];if(u.length>1)u.forEach(function(e,t){e.__originalOperationId=e.__originalOperationId||e.operationId,e.operationId="".concat(s).concat(t+1)});else if(void 0!==n.operationId){var c=u[0];c.__originalOperationId=c.__originalOperationId||n.operationId,c.operationId=s}}if("parameters"!==e){var l=[],p={};for(var f in t)"produces"!==f&&"consumes"!==f&&"security"!==f||(p[f]=t[f],l.push(p));if(a&&(p.parameters=a,l.push(p)),l.length)for(var h=0,d=l;h1&&void 0!==arguments[1]?arguments[1]:{},n=t.requestInterceptor,r=t.responseInterceptor,o=e.withCredentials?"include":"same-origin";return function(t){return e({url:t,loadSpec:!0,requestInterceptor:n,responseInterceptor:r,headers:{Accept:Ge},credentials:o}).then(function(e){return e.body})}}function Dt(e){var t=e.fetch,n=e.spec,r=e.url,o=e.mode,i=e.allowMetaPatches,a=void 0===i||i,s=e.pathDiscriminator,u=e.modelPropertyMacro,c=e.parameterMacro,l=e.requestInterceptor,p=e.responseInterceptor,f=e.skipNormalization,h=e.useCircularStructures,d=e.http,m=e.baseDoc;return m=m||r,d=t||d||V,n?v(n):Rt(d,{requestInterceptor:l,responseInterceptor:p})(m).then(v);function v(e){m&&(St.refs.docCache[m]=e),St.refs.fetchJSON=Rt(d,{requestInterceptor:l,responseInterceptor:p});var t,n=[St.refs];return"function"==typeof c&&n.push(St.parameters),"function"==typeof u&&n.push(St.properties),"strict"!==o&&n.push(St.allOf),(t={spec:e,context:{baseDoc:m},plugins:n,allowMetaPatches:a,pathDiscriminator:s,parameterMacro:c,modelPropertyMacro:u,useCircularStructures:h},new Et(t).dispatch()).then(f?function(){var e=R()(C.a.mark(function e(t){return C.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",t);case 1:case"end":return e.stop()}},e)}));return function(t){return e.apply(this,arguments)}}():Nt)}}var Lt=n(16),Ut=n.n(Lt);function qt(e,t){var n=m()(e);if(h.a){var r=h()(e);t&&(r=r.filter(function(t){return p()(e,t).enumerable})),n.push.apply(n,r)}return n}function Ft(e){for(var t=1;t2&&void 0!==m[2]?m[2]:{},o=r.returnEntireTree,i=r.baseDoc,a=r.requestInterceptor,s=r.responseInterceptor,u=r.parameterMacro,c=r.modelPropertyMacro,l=r.useCircularStructures,p={pathDiscriminator:n,baseDoc:i,requestInterceptor:a,responseInterceptor:s,parameterMacro:u,modelPropertyMacro:c,useCircularStructures:l},f=Nt({spec:t}),h=f.spec,e.next=6,Dt(Ft({},p,{spec:h,allowMetaPatches:!0,skipNormalization:!0}));case 6:return d=e.sent,!o&&M()(n)&&n.length&&(d.spec=Ut()(d.spec,n)||null),e.abrupt("return",d);case 9:case"end":return e.stop()}},e)}))).apply(this,arguments)}var zt=n(38),Vt=n.n(zt);function Ht(e,t){var n=m()(e);if(h.a){var r=h()(e);t&&(r=r.filter(function(t){return p()(e,t).enumerable})),n.push.apply(n,r)}return n}function Wt(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:{};return function(t){var n=t.pathName,r=t.method,o=t.operationId;return function(t){var i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return e.execute(Wt({spec:e.spec},Vt()(e,"requestInterceptor","responseInterceptor","userFetch"),{pathName:n,method:r,parameters:t,operationId:o},i))}}}};var $t=n(39),Gt=n.n($t),Zt=n(40),Xt=n.n(Zt),Qt=n(41),en=n.n(Qt),tn=n(19),nn=n.n(tn),rn=n(42),on=n.n(rn),an={body:function(e){var t=e.req,n=e.value;t.body=n},header:function(e){var t=e.req,n=e.parameter,r=e.value;t.headers=t.headers||{},void 0!==r&&(t.headers[n.name]=r)},query:function(e){var t=e.req,n=e.value,r=e.parameter;t.query=t.query||{},!1===n&&"boolean"===r.type&&(n="false");0===n&&["number","integer"].indexOf(r.type)>-1&&(n="0");if(n)t.query[r.name]={collectionFormat:r.collectionFormat,value:n};else if(r.allowEmptyValue&&void 0!==n){var o=r.name;t.query[o]=t.query[o]||{},t.query[o].allowEmptyValue=!0}},path:function(e){var t=e.req,n=e.value,r=e.parameter;t.url=t.url.split("{".concat(r.name,"}")).join(encodeURIComponent(n))},formData:function(e){var t=e.req,n=e.value,r=e.parameter;(n||r.allowEmptyValue)&&(t.form=t.form||{},t.form[r.name]={value:n,allowEmptyValue:r.allowEmptyValue,collectionFormat:r.collectionFormat})}};n(49);var sn=n(43),un=n.n(sn),cn=n(44),ln=function(e){return":/?#[]@!$&'()*+,;=".indexOf(e)>-1},pn=function(e){return/^[a-z0-9\-._~]+$/i.test(e)};function fn(e){var t=(arguments.length>1&&void 0!==arguments[1]?arguments[1]:{}).escape,n=arguments.length>2?arguments[2]:void 0;return"number"==typeof e&&(e=e.toString()),"string"==typeof e&&e.length&&t?n?JSON.parse(e):Object(cn.stringToCharArray)(e).map(function(e){return pn(e)?e:ln(e)&&"unsafe"===t?e:(un()(e)||[]).map(function(e){return"0".concat(e.toString(16).toUpperCase()).slice(-2)}).map(function(e){return"%".concat(e)}).join("")}).join(""):e}function hn(e){var t=e.value;return M()(t)?function(e){var t=e.key,n=e.value,r=e.style,o=e.explode,i=e.escape,a=function(e){return fn(e,{escape:i})};if("simple"===r)return n.map(function(e){return a(e)}).join(",");if("label"===r)return".".concat(n.map(function(e){return a(e)}).join("."));if("matrix"===r)return n.map(function(e){return a(e)}).reduce(function(e,n){return!e||o?"".concat(e||"",";").concat(t,"=").concat(n):"".concat(e,",").concat(n)},"");if("form"===r){var s=o?"&".concat(t,"="):",";return n.map(function(e){return a(e)}).join(s)}if("spaceDelimited"===r){var u=o?"".concat(t,"="):"";return n.map(function(e){return a(e)}).join(" ".concat(u))}if("pipeDelimited"===r){var c=o?"".concat(t,"="):"";return n.map(function(e){return a(e)}).join("|".concat(c))}}(e):"object"===P()(t)?function(e){var t=e.key,n=e.value,r=e.style,o=e.explode,i=e.escape,a=function(e){return fn(e,{escape:i})},s=m()(n);if("simple"===r)return s.reduce(function(e,t){var r=a(n[t]),i=o?"=":",",s=e?"".concat(e,","):"";return"".concat(s).concat(t).concat(i).concat(r)},"");if("label"===r)return s.reduce(function(e,t){var r=a(n[t]),i=o?"=":".",s=e?"".concat(e,"."):".";return"".concat(s).concat(t).concat(i).concat(r)},"");if("matrix"===r&&o)return s.reduce(function(e,t){var r=a(n[t]),o=e?"".concat(e,";"):";";return"".concat(o).concat(t,"=").concat(r)},"");if("matrix"===r)return s.reduce(function(e,r){var o=a(n[r]),i=e?"".concat(e,","):";".concat(t,"=");return"".concat(i).concat(r,",").concat(o)},"");if("form"===r)return s.reduce(function(e,t){var r=a(n[t]),i=e?"".concat(e).concat(o?"&":","):"",s=o?"=":",";return"".concat(i).concat(t).concat(s).concat(r)},"")}(e):function(e){var t=e.key,n=e.value,r=e.style,o=e.escape,i=function(e){return fn(e,{escape:o})};if("simple"===r)return i(n);if("label"===r)return".".concat(i(n));if("matrix"===r)return";".concat(t,"=").concat(i(n));if("form"===r)return i(n);if("deepObject"===r)return i(n)}(e)}function dn(e,t){return t.includes("application/json")?"string"==typeof e?e:T()(e):e.toString()}function mn(e){var t=e.req,n=e.value,r=e.parameter,o=r.name,i=r.style,a=r.explode,s=r.content;if(s){var u=m()(s)[0];t.url=t.url.split("{".concat(o,"}")).join(fn(dn(n,u),{escape:!0}))}else{var c=hn({key:r.name,value:n,style:i||"simple",explode:a||!1,escape:!0});t.url=t.url.split("{".concat(o,"}")).join(c)}}function vn(e){var t=e.req,n=e.value,r=e.parameter;if(t.query=t.query||{},r.content){var o=m()(r.content)[0];t.query[r.name]=dn(n,o)}else if(!1===n&&(n="false"),0===n&&(n="0"),n){var i=P()(n);if("deepObject"===r.style)m()(n).forEach(function(e){var o=n[e];t.query["".concat(r.name,"[").concat(e,"]")]={value:hn({key:e,value:o,style:"deepObject",escape:r.allowReserved?"unsafe":"reserved"}),skipEncoding:!0}});else if("object"!==i||M()(n)||"form"!==r.style&&r.style||!r.explode&&void 0!==r.explode){var a=encodeURIComponent(r.name);t.query[a]={value:hn({key:a,value:n,style:r.style||"form",explode:void 0===r.explode||r.explode,escape:r.allowReserved?"unsafe":"reserved"}),skipEncoding:!0}}else{m()(n).forEach(function(e){var o=n[e];t.query[e]={value:hn({key:e,value:o,style:r.style||"form",escape:r.allowReserved?"unsafe":"reserved"}),skipEncoding:!0}})}}else if(r.allowEmptyValue&&void 0!==n){var s=r.name;t.query[s]=t.query[s]||{},t.query[s].allowEmptyValue=!0}}var gn=["accept","authorization","content-type"];function yn(e){var t=e.req,n=e.parameter,r=e.value;if(t.headers=t.headers||{},!(gn.indexOf(n.name.toLowerCase())>-1))if(n.content){var o=m()(n.content)[0];t.headers[n.name]=dn(r,o)}else void 0!==r&&(t.headers[n.name]=hn({key:n.name,value:r,style:n.style||"simple",explode:void 0!==n.explode&&n.explode,escape:!1}))}function bn(e){var t=e.req,n=e.parameter,r=e.value;t.headers=t.headers||{};var o=P()(r);if(n.content){var i=m()(n.content)[0];t.headers.Cookie="".concat(n.name,"=").concat(dn(r,i))}else if("undefined"!==o){var a="object"===o&&!M()(r)&&n.explode?"":"".concat(n.name,"=");t.headers.Cookie=a+hn({key:n.name,value:r,escape:!1,style:n.style||"form",explode:void 0!==n.explode&&n.explode})}}var _n=n(30),wn=function(e,t){var n=e.operation,r=e.requestBody,o=e.securities,i=e.spec,a=e.attachContentTypeForEmptyPayload,s=e.requestContentType;t=function(e){var t=e.request,n=e.securities,r=void 0===n?{}:n,o=e.operation,i=void 0===o?{}:o,a=e.spec,s=b()({},t),u=r.authorized,c=void 0===u?{}:u,l=i.security||a.security||[],p=c&&!!m()(c).length,f=Ut()(a,["components","securitySchemes"])||{};if(s.headers=s.headers||{},s.query=s.query||{},!m()(r).length||!p||!l||M()(i.security)&&!i.security.length)return t;return l.forEach(function(e,t){for(var n in e){var r=c[n],o=f[n];if(r){var i=r.value||r,a=o.type;if(r)if("apiKey"===a)"query"===o.in&&(s.query[o.name]=i),"header"===o.in&&(s.headers[o.name]=i),"cookie"===o.in&&(s.cookies[o.name]=i);else if("http"===a){if("basic"===o.scheme){var u=i.username,l=i.password,p=nn()("".concat(u,":").concat(l));s.headers.Authorization="Basic ".concat(p)}"bearer"===o.scheme&&(s.headers.Authorization="Bearer ".concat(i))}else if("oauth2"===a){var h=r.token||{},d=h[o["x-tokenName"]||"access_token"],m=h.token_type;m&&"bearer"!==m.toLowerCase()||(m="Bearer"),s.headers.Authorization="".concat(m," ").concat(d)}}}}),s}({request:t,securities:o,operation:n,spec:i});var u=n.requestBody||{},c=m()(u.content||{}),l=s&&c.indexOf(s)>-1;if(r||a){if(s&&l)t.headers["Content-Type"]=s;else if(!s){var p=c[0];p&&(t.headers["Content-Type"]=p,s=p)}}else s&&l&&(t.headers["Content-Type"]=s);return r&&(s?c.indexOf(s)>-1&&("application/x-www-form-urlencoded"===s||0===s.indexOf("multipart/")?"object"===P()(r)?(t.form={},m()(r).forEach(function(e){var n,o,i=r[e];"undefined"!=typeof File&&(o=i instanceof File),"undefined"!=typeof Blob&&(o=o||i instanceof Blob),void 0!==_n.Buffer&&(o=o||_n.Buffer.isBuffer(i)),n="object"!==P()(i)||o?i:M()(i)?i.toString():T()(i),t.form[e]={value:n}})):t.form=r:t.body=r):t.body=r),t};var xn=function(e,t){var n=e.spec,r=e.operation,o=e.securities,i=e.requestContentType,a=e.attachContentTypeForEmptyPayload;if((t=function(e){var t=e.request,n=e.securities,r=void 0===n?{}:n,o=e.operation,i=void 0===o?{}:o,a=e.spec,s=b()({},t),u=r.authorized,c=void 0===u?{}:u,l=r.specSecurity,p=void 0===l?[]:l,f=i.security||p,h=c&&!!m()(c).length,d=a.securityDefinitions;if(s.headers=s.headers||{},s.query=s.query||{},!m()(r).length||!h||!f||M()(i.security)&&!i.security.length)return t;return f.forEach(function(e,t){for(var n in e){var r=c[n];if(r){var o=r.token,i=r.value||r,a=d[n],u=a.type,l=a["x-tokenName"]||"access_token",p=o&&o[l],f=o&&o.token_type;if(r)if("apiKey"===u){var h="query"===a.in?"query":"headers";s[h]=s[h]||{},s[h][a.name]=i}else"basic"===u?i.header?s.headers.authorization=i.header:(i.base64=nn()("".concat(i.username,":").concat(i.password)),s.headers.authorization="Basic ".concat(i.base64)):"oauth2"===u&&p&&(f=f&&"bearer"!==f.toLowerCase()?f:"Bearer",s.headers.authorization="".concat(f," ").concat(p))}}}),s}({request:t,securities:o,operation:r,spec:n})).body||t.form||a)i?t.headers["Content-Type"]=i:M()(r.consumes)?t.headers["Content-Type"]=r.consumes[0]:M()(n.consumes)?t.headers["Content-Type"]=n.consumes[0]:r.parameters&&r.parameters.filter(function(e){return"file"===e.type}).length?t.headers["Content-Type"]="multipart/form-data":r.parameters&&r.parameters.filter(function(e){return"formData"===e.in}).length&&(t.headers["Content-Type"]="application/x-www-form-urlencoded");else if(i){var s=r.parameters&&r.parameters.filter(function(e){return"body"===e.in}).length>0,u=r.parameters&&r.parameters.filter(function(e){return"formData"===e.in}).length>0;(s||u)&&(t.headers["Content-Type"]=i)}return t};function En(e,t){var n=m()(e);if(h.a){var r=h()(e);t&&(r=r.filter(function(t){return p()(e,t).enumerable})),n.push.apply(n,r)}return n}function Sn(e){for(var t=1;t-1&&(c=o,l=u[p.indexOf(o)])}return!c&&u&&u.length&&(c=u[0].url,l=u[0]),c.indexOf("{")>-1&&function(e){for(var t,n=[],r=/{([^}]+)}/g;t=r.exec(e);)n.push(t[1]);return n}(c).forEach(function(e){if(l.variables&&l.variables[e]){var t=l.variables[e],n=s[e]||t.default,r=new RegExp("{".concat(e,"}"),"g");c=c.replace(r,n)}}),function(){var e,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",r=E.a.parse(t),o=E.a.parse(n),i=Pn(r.protocol)||Pn(o.protocol)||"",a=r.host||o.host,s=r.pathname||"";return"/"===(e=i&&a?"".concat(i,"://").concat(a+s):s)[e.length-1]?e.slice(0,-1):e}(c,i)}(b):function(e){var t,n=e.spec,r=e.scheme,o=e.contextUrl,i=void 0===o?"":o,a=E.a.parse(i),s=M()(n.schemes)?n.schemes[0]:null,u=r||s||Pn(a.protocol)||"http",c=n.host||a.host||"",l=n.basePath||"";return"/"===(t=u&&c?"".concat(u,"://").concat(c+l):l)[t.length-1]?t.slice(0,-1):t}(b),!n)return delete g.cookies,g;g.url+=S,g.method="".concat(x).toUpperCase(),h=h||{};var C=t.paths[S]||{};o&&(g.headers.accept=o);var k=An([].concat(Cn(w.parameters)).concat(Cn(C.parameters)));k.forEach(function(e){var n,r=d[e.in];if("body"===e.in&&e.schema&&e.schema.properties&&(n=h),void 0===(n=e&&e.name&&h[e.name])?n=e&&e.name&&h["".concat(e.in,".").concat(e.name)]:On(e.name,k).length>1&&console.warn("Parameter '".concat(e.name,"' is ambiguous because the defined spec has more than one parameter with the name: '").concat(e.name,"' and the passed-in parameter values did not define an 'in' value.")),null!==n){if(void 0!==e.default&&void 0===n&&(n=e.default),void 0===n&&e.required&&!e.allowEmptyValue)throw new Error("Required parameter ".concat(e.name," is not provided"));if(v&&e.schema&&"object"===e.schema.type&&"string"==typeof n)try{n=JSON.parse(n)}catch(e){throw new Error("Could not parse object parameter value string as JSON")}r&&r({req:g,parameter:e,value:n,operation:w,spec:t})}});var O=Sn({},e,{operation:w});if((g=v?wn(O,g):xn(O,g)).cookies&&m()(g.cookies).length){var A=m()(g.cookies).reduce(function(e,t){var n=g.cookies[t];return e+(e?"&":"")+on.a.serialize(t,n)},"");g.headers.Cookie=A}return g.cookies&&delete g.cookies,Z(g),g}var Pn=function(e){return e?e.replace(/\W/g,""):null};function In(e,t){var n=m()(e);if(h.a){var r=h()(e);t&&(r=r.filter(function(t){return p()(e,t).enumerable})),n.push.apply(n,r)}return n}function Mn(e){var t=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if("string"==typeof e?n.url=e:n=e,!(this instanceof Mn))return new Mn(n);b()(this,n);var r=this.resolve().then(function(){return t.disableInterfaces||b()(t,Mn.makeApisTagOperation(t)),t});return r.client=this,r}Mn.http=V,Mn.makeHttp=function(e,t,n){return n=n||function(e){return e},t=t||function(e){return e},function(r){return"string"==typeof r&&(r={url:r}),z.mergeInQueryOrForm(r),r=t(r),n(e(r))}}.bind(null,Mn.http),Mn.resolve=Dt,Mn.resolveSubtree=function(e,t){return Bt.apply(this,arguments)},Mn.execute=function(e){var t=e.http,n=e.fetch,r=e.spec,o=e.operationId,i=e.pathName,a=e.method,s=e.parameters,u=e.securities,c=Gt()(e,["http","fetch","spec","operationId","pathName","method","parameters","securities"]),l=t||n||V;i&&a&&!o&&(o=Pt(i,a));var p=Tn.buildRequest(Sn({spec:r,operationId:o,parameters:s,securities:u,http:l},c));return p.body&&(Xt()(p.body)||en()(p.body))&&(p.body=T()(p.body)),l(p)},Mn.serializeRes=J,Mn.serializeHeaders=K,Mn.clearCache=function(){St.refs.clearCache()},Mn.makeApisTagOperation=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=Yt.makeExecute(e);return{apis:Yt.mapTagOperations({v2OperationIdCompatibilityMode:e.v2OperationIdCompatibilityMode,spec:e.spec,cb:t})}},Mn.buildRequest=jn,Mn.helpers={opId:jt},Mn.prototype={http:V,execute:function(e){return this.applyDefaults(),Mn.execute(function(e){for(var t=1;t - * @license MIT - */ -var r=n(569),o=n(570),i=n(355);function a(){return u.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function s(e,t){if(a()=a())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+a().toString(16)+" bytes");return 0|e}function d(e,t){if(u.isBuffer(e))return e.length;if("undefined"!=typeof ArrayBuffer&&"function"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(e)||e instanceof ArrayBuffer))return e.byteLength;"string"!=typeof e&&(e=""+e);var n=e.length;if(0===n)return 0;for(var r=!1;;)switch(t){case"ascii":case"latin1":case"binary":return n;case"utf8":case"utf-8":case void 0:return B(e).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*n;case"hex":return n>>>1;case"base64":return z(e).length;default:if(r)return B(e).length;t=(""+t).toLowerCase(),r=!0}}function m(e,t,n){var r=!1;if((void 0===t||t<0)&&(t=0),t>this.length)return"";if((void 0===n||n>this.length)&&(n=this.length),n<=0)return"";if((n>>>=0)<=(t>>>=0))return"";for(e||(e="utf8");;)switch(e){case"hex":return j(this,t,n);case"utf8":case"utf-8":return k(this,t,n);case"ascii":return A(this,t,n);case"latin1":case"binary":return T(this,t,n);case"base64":return C(this,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return P(this,t,n);default:if(r)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),r=!0}}function v(e,t,n){var r=e[t];e[t]=e[n],e[n]=r}function g(e,t,n,r,o){if(0===e.length)return-1;if("string"==typeof n?(r=n,n=0):n>2147483647?n=2147483647:n<-2147483648&&(n=-2147483648),n=+n,isNaN(n)&&(n=o?0:e.length-1),n<0&&(n=e.length+n),n>=e.length){if(o)return-1;n=e.length-1}else if(n<0){if(!o)return-1;n=0}if("string"==typeof t&&(t=u.from(t,r)),u.isBuffer(t))return 0===t.length?-1:y(e,t,n,r,o);if("number"==typeof t)return t&=255,u.TYPED_ARRAY_SUPPORT&&"function"==typeof Uint8Array.prototype.indexOf?o?Uint8Array.prototype.indexOf.call(e,t,n):Uint8Array.prototype.lastIndexOf.call(e,t,n):y(e,[t],n,r,o);throw new TypeError("val must be string, number or Buffer")}function y(e,t,n,r,o){var i,a=1,s=e.length,u=t.length;if(void 0!==r&&("ucs2"===(r=String(r).toLowerCase())||"ucs-2"===r||"utf16le"===r||"utf-16le"===r)){if(e.length<2||t.length<2)return-1;a=2,s/=2,u/=2,n/=2}function c(e,t){return 1===a?e[t]:e.readUInt16BE(t*a)}if(o){var l=-1;for(i=n;is&&(n=s-u),i=n;i>=0;i--){for(var p=!0,f=0;fo&&(r=o):r=o;var i=t.length;if(i%2!=0)throw new TypeError("Invalid hex string");r>i/2&&(r=i/2);for(var a=0;a>8,o=n%256,i.push(o),i.push(r);return i}(t,e.length-n),e,n,r)}function C(e,t,n){return 0===t&&n===e.length?r.fromByteArray(e):r.fromByteArray(e.slice(t,n))}function k(e,t,n){n=Math.min(e.length,n);for(var r=[],o=t;o239?4:c>223?3:c>191?2:1;if(o+p<=n)switch(p){case 1:c<128&&(l=c);break;case 2:128==(192&(i=e[o+1]))&&(u=(31&c)<<6|63&i)>127&&(l=u);break;case 3:i=e[o+1],a=e[o+2],128==(192&i)&&128==(192&a)&&(u=(15&c)<<12|(63&i)<<6|63&a)>2047&&(u<55296||u>57343)&&(l=u);break;case 4:i=e[o+1],a=e[o+2],s=e[o+3],128==(192&i)&&128==(192&a)&&128==(192&s)&&(u=(15&c)<<18|(63&i)<<12|(63&a)<<6|63&s)>65535&&u<1114112&&(l=u)}null===l?(l=65533,p=1):l>65535&&(l-=65536,r.push(l>>>10&1023|55296),l=56320|1023&l),r.push(l),o+=p}return function(e){var t=e.length;if(t<=O)return String.fromCharCode.apply(String,e);var n="",r=0;for(;r0&&(e=this.toString("hex",0,n).match(/.{2}/g).join(" "),this.length>n&&(e+=" ... ")),""},u.prototype.compare=function(e,t,n,r,o){if(!u.isBuffer(e))throw new TypeError("Argument must be a Buffer");if(void 0===t&&(t=0),void 0===n&&(n=e?e.length:0),void 0===r&&(r=0),void 0===o&&(o=this.length),t<0||n>e.length||r<0||o>this.length)throw new RangeError("out of range index");if(r>=o&&t>=n)return 0;if(r>=o)return-1;if(t>=n)return 1;if(this===e)return 0;for(var i=(o>>>=0)-(r>>>=0),a=(n>>>=0)-(t>>>=0),s=Math.min(i,a),c=this.slice(r,o),l=e.slice(t,n),p=0;po)&&(n=o),e.length>0&&(n<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");r||(r="utf8");for(var i=!1;;)switch(r){case"hex":return b(this,e,t,n);case"utf8":case"utf-8":return _(this,e,t,n);case"ascii":return w(this,e,t,n);case"latin1":case"binary":return x(this,e,t,n);case"base64":return E(this,e,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return S(this,e,t,n);default:if(i)throw new TypeError("Unknown encoding: "+r);r=(""+r).toLowerCase(),i=!0}},u.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var O=4096;function A(e,t,n){var r="";n=Math.min(e.length,n);for(var o=t;or)&&(n=r);for(var o="",i=t;in)throw new RangeError("Trying to access beyond buffer length")}function M(e,t,n,r,o,i){if(!u.isBuffer(e))throw new TypeError('"buffer" argument must be a Buffer instance');if(t>o||te.length)throw new RangeError("Index out of range")}function N(e,t,n,r){t<0&&(t=65535+t+1);for(var o=0,i=Math.min(e.length-n,2);o>>8*(r?o:1-o)}function R(e,t,n,r){t<0&&(t=4294967295+t+1);for(var o=0,i=Math.min(e.length-n,4);o>>8*(r?o:3-o)&255}function D(e,t,n,r,o,i){if(n+r>e.length)throw new RangeError("Index out of range");if(n<0)throw new RangeError("Index out of range")}function L(e,t,n,r,i){return i||D(e,0,n,4),o.write(e,t,n,r,23,4),n+4}function U(e,t,n,r,i){return i||D(e,0,n,8),o.write(e,t,n,r,52,8),n+8}u.prototype.slice=function(e,t){var n,r=this.length;if((e=~~e)<0?(e+=r)<0&&(e=0):e>r&&(e=r),(t=void 0===t?r:~~t)<0?(t+=r)<0&&(t=0):t>r&&(t=r),t0&&(o*=256);)r+=this[e+--t]*o;return r},u.prototype.readUInt8=function(e,t){return t||I(e,1,this.length),this[e]},u.prototype.readUInt16LE=function(e,t){return t||I(e,2,this.length),this[e]|this[e+1]<<8},u.prototype.readUInt16BE=function(e,t){return t||I(e,2,this.length),this[e]<<8|this[e+1]},u.prototype.readUInt32LE=function(e,t){return t||I(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},u.prototype.readUInt32BE=function(e,t){return t||I(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},u.prototype.readIntLE=function(e,t,n){e|=0,t|=0,n||I(e,t,this.length);for(var r=this[e],o=1,i=0;++i=(o*=128)&&(r-=Math.pow(2,8*t)),r},u.prototype.readIntBE=function(e,t,n){e|=0,t|=0,n||I(e,t,this.length);for(var r=t,o=1,i=this[e+--r];r>0&&(o*=256);)i+=this[e+--r]*o;return i>=(o*=128)&&(i-=Math.pow(2,8*t)),i},u.prototype.readInt8=function(e,t){return t||I(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},u.prototype.readInt16LE=function(e,t){t||I(e,2,this.length);var n=this[e]|this[e+1]<<8;return 32768&n?4294901760|n:n},u.prototype.readInt16BE=function(e,t){t||I(e,2,this.length);var n=this[e+1]|this[e]<<8;return 32768&n?4294901760|n:n},u.prototype.readInt32LE=function(e,t){return t||I(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},u.prototype.readInt32BE=function(e,t){return t||I(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},u.prototype.readFloatLE=function(e,t){return t||I(e,4,this.length),o.read(this,e,!0,23,4)},u.prototype.readFloatBE=function(e,t){return t||I(e,4,this.length),o.read(this,e,!1,23,4)},u.prototype.readDoubleLE=function(e,t){return t||I(e,8,this.length),o.read(this,e,!0,52,8)},u.prototype.readDoubleBE=function(e,t){return t||I(e,8,this.length),o.read(this,e,!1,52,8)},u.prototype.writeUIntLE=function(e,t,n,r){(e=+e,t|=0,n|=0,r)||M(this,e,t,n,Math.pow(2,8*n)-1,0);var o=1,i=0;for(this[t]=255&e;++i=0&&(i*=256);)this[t+o]=e/i&255;return t+n},u.prototype.writeUInt8=function(e,t,n){return e=+e,t|=0,n||M(this,e,t,1,255,0),u.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),this[t]=255&e,t+1},u.prototype.writeUInt16LE=function(e,t,n){return e=+e,t|=0,n||M(this,e,t,2,65535,0),u.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):N(this,e,t,!0),t+2},u.prototype.writeUInt16BE=function(e,t,n){return e=+e,t|=0,n||M(this,e,t,2,65535,0),u.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):N(this,e,t,!1),t+2},u.prototype.writeUInt32LE=function(e,t,n){return e=+e,t|=0,n||M(this,e,t,4,4294967295,0),u.TYPED_ARRAY_SUPPORT?(this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=255&e):R(this,e,t,!0),t+4},u.prototype.writeUInt32BE=function(e,t,n){return e=+e,t|=0,n||M(this,e,t,4,4294967295,0),u.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):R(this,e,t,!1),t+4},u.prototype.writeIntLE=function(e,t,n,r){if(e=+e,t|=0,!r){var o=Math.pow(2,8*n-1);M(this,e,t,n,o-1,-o)}var i=0,a=1,s=0;for(this[t]=255&e;++i>0)-s&255;return t+n},u.prototype.writeIntBE=function(e,t,n,r){if(e=+e,t|=0,!r){var o=Math.pow(2,8*n-1);M(this,e,t,n,o-1,-o)}var i=n-1,a=1,s=0;for(this[t+i]=255&e;--i>=0&&(a*=256);)e<0&&0===s&&0!==this[t+i+1]&&(s=1),this[t+i]=(e/a>>0)-s&255;return t+n},u.prototype.writeInt8=function(e,t,n){return e=+e,t|=0,n||M(this,e,t,1,127,-128),u.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),e<0&&(e=255+e+1),this[t]=255&e,t+1},u.prototype.writeInt16LE=function(e,t,n){return e=+e,t|=0,n||M(this,e,t,2,32767,-32768),u.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):N(this,e,t,!0),t+2},u.prototype.writeInt16BE=function(e,t,n){return e=+e,t|=0,n||M(this,e,t,2,32767,-32768),u.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):N(this,e,t,!1),t+2},u.prototype.writeInt32LE=function(e,t,n){return e=+e,t|=0,n||M(this,e,t,4,2147483647,-2147483648),u.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24):R(this,e,t,!0),t+4},u.prototype.writeInt32BE=function(e,t,n){return e=+e,t|=0,n||M(this,e,t,4,2147483647,-2147483648),e<0&&(e=4294967295+e+1),u.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):R(this,e,t,!1),t+4},u.prototype.writeFloatLE=function(e,t,n){return L(this,e,t,!0,n)},u.prototype.writeFloatBE=function(e,t,n){return L(this,e,t,!1,n)},u.prototype.writeDoubleLE=function(e,t,n){return U(this,e,t,!0,n)},u.prototype.writeDoubleBE=function(e,t,n){return U(this,e,t,!1,n)},u.prototype.copy=function(e,t,n,r){if(n||(n=0),r||0===r||(r=this.length),t>=e.length&&(t=e.length),t||(t=0),r>0&&r=this.length)throw new RangeError("sourceStart out of bounds");if(r<0)throw new RangeError("sourceEnd out of bounds");r>this.length&&(r=this.length),e.length-t=0;--o)e[o+t]=this[o+n];else if(i<1e3||!u.TYPED_ARRAY_SUPPORT)for(o=0;o>>=0,n=void 0===n?this.length:n>>>0,e||(e=0),"number"==typeof e)for(i=t;i55295&&n<57344){if(!o){if(n>56319){(t-=3)>-1&&i.push(239,191,189);continue}if(a+1===r){(t-=3)>-1&&i.push(239,191,189);continue}o=n;continue}if(n<56320){(t-=3)>-1&&i.push(239,191,189),o=n;continue}n=65536+(o-55296<<10|n-56320)}else o&&(t-=3)>-1&&i.push(239,191,189);if(o=null,n<128){if((t-=1)<0)break;i.push(n)}else if(n<2048){if((t-=2)<0)break;i.push(n>>6|192,63&n|128)}else if(n<65536){if((t-=3)<0)break;i.push(n>>12|224,n>>6&63|128,63&n|128)}else{if(!(n<1114112))throw new Error("Invalid code point");if((t-=4)<0)break;i.push(n>>18|240,n>>12&63|128,n>>6&63|128,63&n|128)}}return i}function z(e){return r.toByteArray(function(e){if((e=function(e){return e.trim?e.trim():e.replace(/^\s+|\s+$/g,"")}(e).replace(q,"")).length<2)return"";for(;e.length%4!=0;)e+="=";return e}(e))}function V(e,t,n,r){for(var o=0;o=t.length||o>=e.length);++o)t[o+n]=e[o];return o}}).call(this,n(36))},function(e,t,n){"use strict";e.exports={current:null}},function(e,t){e.exports=function(e){return null!=e&&"object"==typeof e}},function(e,t){var n,r,o=e.exports={};function i(){throw new Error("setTimeout has not been defined")}function a(){throw new Error("clearTimeout has not been defined")}function s(e){if(n===setTimeout)return setTimeout(e,0);if((n===i||!n)&&setTimeout)return n=setTimeout,setTimeout(e,0);try{return n(e,0)}catch(t){try{return n.call(null,e,0)}catch(t){return n.call(this,e,0)}}}!function(){try{n="function"==typeof setTimeout?setTimeout:i}catch(e){n=i}try{r="function"==typeof clearTimeout?clearTimeout:a}catch(e){r=a}}();var u,c=[],l=!1,p=-1;function f(){l&&u&&(l=!1,u.length?c=u.concat(c):p=-1,c.length&&h())}function h(){if(!l){var e=s(f);l=!0;for(var t=c.length;t;){for(u=c,c=[];++p1)for(var n=1;n0&&"/"!==t[0]});function oe(e,t,n){return t=t||[],te.apply(void 0,[e].concat(u()(t))).get("parameters",Object(p.List)()).reduce(function(e,t){var r=n&&"body"===t.get("in")?t.get("value_xml"):t.get("value");return e.set(Object(l.B)(t,{allowHashes:!1}),r)},Object(p.fromJS)({}))}function ie(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if(p.List.isList(e))return e.some(function(e){return p.Map.isMap(e)&&e.get("in")===t})}function ae(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if(p.List.isList(e))return e.some(function(e){return p.Map.isMap(e)&&e.get("type")===t})}function se(e,t){t=t||[];var n=x(e).getIn(["paths"].concat(u()(t)),Object(p.fromJS)({})),r=e.getIn(["meta","paths"].concat(u()(t)),Object(p.fromJS)({})),o=ue(e,t),i=n.get("parameters")||new p.List,a=r.get("consumes_value")?r.get("consumes_value"):ae(i,"file")?"multipart/form-data":ae(i,"formData")?"application/x-www-form-urlencoded":void 0;return Object(p.fromJS)({requestContentType:a,responseContentType:o})}function ue(e,t){t=t||[];var n=x(e).getIn(["paths"].concat(u()(t)),null);if(null!==n){var r=e.getIn(["meta","paths"].concat(u()(t),["produces_value"]),null),o=n.getIn(["produces",0],null);return r||o||"application/json"}}function ce(e,t){t=t||[];var n=x(e),r=n.getIn(["paths"].concat(u()(t)),null);if(null!==r){var o=t,i=a()(o,1)[0],s=r.get("produces",null),c=n.getIn(["paths",i,"produces"],null),l=n.getIn(["produces"],null);return s||c||l}}function le(e,t){t=t||[];var n=x(e),r=n.getIn(["paths"].concat(u()(t)),null);if(null!==r){var o=t,i=a()(o,1)[0],s=r.get("consumes",null),c=n.getIn(["paths",i,"consumes"],null),l=n.getIn(["consumes"],null);return s||c||l}}var pe=function(e,t,n){var r=e.get("url").match(/^([a-z][a-z0-9+\-.]*):/),i=o()(r)?r[1]:null;return e.getIn(["scheme",t,n])||e.getIn(["scheme","_defaultScheme"])||i||""},fe=function(e,t,n){return["http","https"].indexOf(pe(e,t,n))>-1},he=function(e,t){t=t||[];var n=e.getIn(["meta","paths"].concat(u()(t),["parameters"]),Object(p.fromJS)([])),r=!0;return n.forEach(function(e){var t=e.get("errors");t&&t.count()&&(r=!1)}),r};function de(e){return p.Map.isMap(e)?e:new p.Map}},function(e,t,n){"use strict";n.r(t),n.d(t,"SHOW_AUTH_POPUP",function(){return d}),n.d(t,"AUTHORIZE",function(){return m}),n.d(t,"LOGOUT",function(){return v}),n.d(t,"PRE_AUTHORIZE_OAUTH2",function(){return g}),n.d(t,"AUTHORIZE_OAUTH2",function(){return y}),n.d(t,"VALIDATE",function(){return b}),n.d(t,"CONFIGURE_AUTH",function(){return _}),n.d(t,"showDefinitions",function(){return w}),n.d(t,"authorize",function(){return x}),n.d(t,"logout",function(){return E}),n.d(t,"preAuthorizeImplicit",function(){return S}),n.d(t,"authorizeOauth2",function(){return C}),n.d(t,"authorizePassword",function(){return k}),n.d(t,"authorizeApplication",function(){return O}),n.d(t,"authorizeAccessCodeWithFormParams",function(){return A}),n.d(t,"authorizeAccessCodeWithBasicAuthentication",function(){return T}),n.d(t,"authorizeRequest",function(){return j}),n.d(t,"configureAuth",function(){return P});var r=n(26),o=n.n(r),i=n(16),a=n.n(i),s=n(28),u=n.n(s),c=n(95),l=n.n(c),p=n(18),f=n.n(p),h=n(3),d="show_popup",m="authorize",v="logout",g="pre_authorize_oauth2",y="authorize_oauth2",b="validate",_="configure_auth";function w(e){return{type:d,payload:e}}function x(e){return{type:m,payload:e}}function E(e){return{type:v,payload:e}}var S=function(e){return function(t){var n=t.authActions,r=t.errActions,o=e.auth,i=e.token,a=e.isValid,s=o.schema,c=o.name,l=s.get("flow");delete f.a.swaggerUIRedirectOauth2,"accessCode"===l||a||r.newAuthErr({authId:c,source:"auth",level:"warning",message:"Authorization may be unsafe, passed state was changed in server Passed state wasn't returned from auth server"}),i.error?r.newAuthErr({authId:c,source:"auth",level:"error",message:u()(i)}):n.authorizeOauth2({auth:o,token:i})}};function C(e){return{type:y,payload:e}}var k=function(e){return function(t){var n=t.authActions,r=e.schema,o=e.name,i=e.username,s=e.password,u=e.passwordType,c=e.clientId,l=e.clientSecret,p={grant_type:"password",scope:e.scopes.join(" "),username:i,password:s},f={};switch(u){case"request-body":!function(e,t,n){t&&a()(e,{client_id:t});n&&a()(e,{client_secret:n})}(p,c,l);break;case"basic":f.Authorization="Basic "+Object(h.a)(c+":"+l);break;default:console.warn("Warning: invalid passwordType ".concat(u," was passed, not including client id and secret"))}return n.authorizeRequest({body:Object(h.b)(p),url:r.get("tokenUrl"),name:o,headers:f,query:{},auth:e})}};var O=function(e){return function(t){var n=t.authActions,r=e.schema,o=e.scopes,i=e.name,a=e.clientId,s=e.clientSecret,u={Authorization:"Basic "+Object(h.a)(a+":"+s)},c={grant_type:"client_credentials",scope:o.join(" ")};return n.authorizeRequest({body:Object(h.b)(c),name:i,url:r.get("tokenUrl"),auth:e,headers:u})}},A=function(e){var t=e.auth,n=e.redirectUrl;return function(e){var r=e.authActions,o=t.schema,i=t.name,a=t.clientId,s=t.clientSecret,u=t.codeVerifier,c={grant_type:"authorization_code",code:t.code,client_id:a,client_secret:s,redirect_uri:n,code_verifier:u};return r.authorizeRequest({body:Object(h.b)(c),name:i,url:o.get("tokenUrl"),auth:t})}},T=function(e){var t=e.auth,n=e.redirectUrl;return function(e){var r=e.authActions,o=t.schema,i=t.name,a=t.clientId,s=t.clientSecret,u={Authorization:"Basic "+Object(h.a)(a+":"+s)},c={grant_type:"authorization_code",code:t.code,client_id:a,redirect_uri:n};return r.authorizeRequest({body:Object(h.b)(c),name:i,url:o.get("tokenUrl"),auth:t,headers:u})}},j=function(e){return function(t){var n,r=t.fn,i=t.getConfigs,s=t.authActions,c=t.errActions,p=t.oas3Selectors,f=t.specSelectors,h=t.authSelectors,d=e.body,m=e.query,v=void 0===m?{}:m,g=e.headers,y=void 0===g?{}:g,b=e.name,_=e.url,w=e.auth,x=(h.getConfigs()||{}).additionalQueryStringParams;n=f.isOAS3()?l()(_,p.selectedServer(),!0):l()(_,f.url(),!0),"object"===o()(x)&&(n.query=a()({},n.query,x));var E=n.toString(),S=a()({Accept:"application/json, text/plain, */*","Content-Type":"application/x-www-form-urlencoded","X-Requested-With":"XMLHttpRequest"},y);r.fetch({url:E,method:"post",headers:S,query:v,body:d,requestInterceptor:i().requestInterceptor,responseInterceptor:i().responseInterceptor}).then(function(e){var t=JSON.parse(e.data),n=t&&(t.error||""),r=t&&(t.parseError||"");e.ok?n||r?c.newAuthErr({authId:b,level:"error",source:"auth",message:u()(t)}):s.authorizeOauth2({auth:w,token:t}):c.newAuthErr({authId:b,level:"error",source:"auth",message:e.statusText})}).catch(function(e){var t=new Error(e).message;if(e.response&&e.response.data){var n=e.response.data;try{var r="string"==typeof n?JSON.parse(n):n;r.error&&(t+=", error: ".concat(r.error)),r.error_description&&(t+=", description: ".concat(r.error_description))}catch(e){}}c.newAuthErr({authId:b,level:"error",source:"auth",message:t})})}};function P(e){return{type:_,payload:e}}},function(e,t){var n=e.exports={version:"2.6.5"};"number"==typeof __e&&(__e=n)},function(e,t){e.exports=function(e){if(null==e)throw TypeError("Can't call method on "+e);return e}},function(e,t,n){var r=n(127),o=Math.min;e.exports=function(e){return e>0?o(r(e),9007199254740991):0}},function(e,t){var n={}.hasOwnProperty;e.exports=function(e,t){return n.call(e,t)}},function(e,t,n){var r=n(211),o=n(210);e.exports=function(e){return r(o(e))}},function(e,t,n){var r=n(49),o=n(133);e.exports=n(50)?function(e,t,n){return r.f(e,t,o(1,n))}:function(e,t,n){return e[t]=n,e}},function(e,t,n){"use strict";e.exports=function(e){if("function"!=typeof e)throw new TypeError(e+" is not a function");return e}},function(e,t,n){"use strict";n.r(t),n.d(t,"UPDATE_LAYOUT",function(){return o}),n.d(t,"UPDATE_FILTER",function(){return i}),n.d(t,"UPDATE_MODE",function(){return a}),n.d(t,"SHOW",function(){return s}),n.d(t,"updateLayout",function(){return u}),n.d(t,"updateFilter",function(){return c}),n.d(t,"show",function(){return l}),n.d(t,"changeMode",function(){return p});var r=n(3),o="layout_update_layout",i="layout_update_filter",a="layout_update_mode",s="layout_show";function u(e){return{type:o,payload:e}}function c(e){return{type:i,payload:e}}function l(e){var t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];return e=Object(r.w)(e),{type:s,payload:{thing:e,shown:t}}}function p(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return e=Object(r.w)(e),{type:a,payload:{thing:e,mode:t}}}},function(e,t,n){"use strict";(function(t){ -/*! - * @description Recursive object extending - * @author Viacheslav Lotsmanov - * @license MIT - * - * The MIT License (MIT) - * - * Copyright (c) 2013-2018 Viacheslav Lotsmanov - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -function n(e){return e instanceof t||e instanceof Date||e instanceof RegExp}function r(e){if(e instanceof t){var n=t.alloc?t.alloc(e.length):new t(e.length);return e.copy(n),n}if(e instanceof Date)return new Date(e.getTime());if(e instanceof RegExp)return new RegExp(e);throw new Error("Unexpected situation")}function o(e){var t=[];return e.forEach(function(e,i){"object"==typeof e&&null!==e?Array.isArray(e)?t[i]=o(e):n(e)?t[i]=r(e):t[i]=a({},e):t[i]=e}),t}function i(e,t){return"__proto__"===t?void 0:e[t]}var a=e.exports=function(){if(arguments.length<1||"object"!=typeof arguments[0])return!1;if(arguments.length<2)return arguments[0];var e,t,s=arguments[0],u=Array.prototype.slice.call(arguments,1);return u.forEach(function(u){"object"!=typeof u||null===u||Array.isArray(u)||Object.keys(u).forEach(function(c){return t=i(s,c),(e=i(u,c))===s?void 0:"object"!=typeof e||null===e?void(s[c]=e):Array.isArray(e)?void(s[c]=o(e)):n(e)?void(s[c]=r(e)):"object"!=typeof t||null===t||Array.isArray(t)?void(s[c]=a({},e)):void(s[c]=a(t,e))})}),s}}).call(this,n(64).Buffer)},function(e,t,n){var r=n(151),o=n(336);e.exports=n(126)?function(e,t,n){return r.f(e,t,o(1,n))}:function(e,t,n){return e[t]=n,e}},function(e,t){e.exports=function(e){try{return!!e()}catch(e){return!0}}},function(e,t,n){var r=n(106),o=n(603),i=n(604),a="[object Null]",s="[object Undefined]",u=r?r.toStringTag:void 0;e.exports=function(e){return null==e?void 0===e?s:a:u&&u in Object(e)?o(e):i(e)}},function(e,t,n){var r=n(621),o=n(624);e.exports=function(e,t){var n=o(e,t);return r(n)?n:void 0}},function(e,t,n){var r=n(380),o=n(661),i=n(107);e.exports=function(e){return i(e)?r(e):o(e)}},function(e,t,n){"use strict";var r=n(178),o=Object.keys||function(e){var t=[];for(var n in e)t.push(n);return t};e.exports=p;var i=n(137);i.inherits=n(47);var a=n(390),s=n(240);i.inherits(p,a);for(var u=o(s.prototype),c=0;c=t.length?{value:void 0,done:!0}:(e=r(t,n),this._i+=e.length,{value:e,done:!1})})},function(e,t){e.exports={}},function(e,t,n){n(561);for(var r=n(32),o=n(77),i=n(102),a=n(34)("toStringTag"),s="CSSRuleList,CSSStyleDeclaration,CSSValueList,ClientRectList,DOMRectList,DOMStringList,DOMTokenList,DataTransferItemList,FileList,HTMLAllCollection,HTMLCollection,HTMLFormElement,HTMLSelectElement,MediaList,MimeTypeArray,NamedNodeMap,NodeList,PaintRequestList,Plugin,PluginArray,SVGLengthList,SVGNumberList,SVGPathSegList,SVGPointList,SVGStringList,SVGTransformList,SourceBufferList,StyleSheetList,TextTrackCueList,TextTrackList,TouchList".split(","),u=0;u1){for(var d=Array(h),m=0;m1){for(var g=Array(v),y=0;y=this._finalSize&&(this._update(this._block),this._block.fill(0));var n=8*this._len;if(n<=4294967295)this._block.writeUInt32BE(n,this._blockSize-4);else{var r=(4294967295&n)>>>0,o=(n-r)/4294967296;this._block.writeUInt32BE(o,this._blockSize-8),this._block.writeUInt32BE(r,this._blockSize-4)}this._update(this._block);var i=this._hash();return e?i.toString(e):i},o.prototype._update=function(){throw new Error("_update must be implemented by subclass")},e.exports=o},function(e,t,n){var r=n(63),o=n(406),i=n(407),a=n(46),s=n(158),u=n(225),c={},l={};(t=e.exports=function(e,t,n,p,f){var h,d,m,v,g=f?function(){return e}:u(e),y=r(n,p,t?2:1),b=0;if("function"!=typeof g)throw TypeError(e+" is not iterable!");if(i(g)){for(h=s(e.length);h>b;b++)if((v=t?y(a(d=e[b])[0],d[1]):y(e[b]))===c||v===l)return v}else for(m=g.call(e);!(d=m.next()).done;)if((v=o(m,y,d.value,t))===c||v===l)return v}).BREAK=c,t.RETURN=l},function(e,t,n){"use strict";function r(e){return null==e}e.exports.isNothing=r,e.exports.isObject=function(e){return"object"==typeof e&&null!==e},e.exports.toArray=function(e){return Array.isArray(e)?e:r(e)?[]:[e]},e.exports.repeat=function(e,t){var n,r="";for(n=0;n1&&void 0!==arguments[1]?arguments[1]:{},r=Object(i.A)(t),a=r.type,s=r.example,u=r.properties,c=r.additionalProperties,l=r.items,p=n.includeReadOnly,f=n.includeWriteOnly;if(void 0!==s)return Object(i.e)(s,"$$ref",function(e){return"string"==typeof e&&e.indexOf("#")>-1});if(!a)if(u)a="object";else{if(!l)return;a="array"}if("object"===a){var d=Object(i.A)(u),m={};for(var v in d)d[v]&&d[v].deprecated||d[v]&&d[v].readOnly&&!p||d[v]&&d[v].writeOnly&&!f||(m[v]=e(d[v],n));if(!0===c)m.additionalProp1={};else if(c)for(var g=Object(i.A)(c),y=e(g,n),b=1;b<4;b++)m["additionalProp"+b]=y;return m}return"array"===a?o()(l.anyOf)?l.anyOf.map(function(t){return e(t,n)}):o()(l.oneOf)?l.oneOf.map(function(t){return e(t,n)}):[e(l,n)]:t.enum?t.default?t.default:Object(i.w)(t.enum)[0]:"file"!==a?h(t):void 0},m=function(e){return e.schema&&(e=e.schema),e.properties&&(e.type="object"),e},v=function e(t){var n,r,a=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},s=p()({},Object(i.A)(t)),u=s.type,c=s.properties,l=s.additionalProperties,f=s.items,d=s.example,m=a.includeReadOnly,v=a.includeWriteOnly,g=s.default,y={},b={},_=t.xml,w=_.name,x=_.prefix,E=_.namespace,S=s.enum;if(!u)if(c||l)u="object";else{if(!f)return;u="array"}if(n=(x?x+":":"")+(w=w||"notagname"),E){var C=x?"xmlns:"+x:"xmlns";b[C]=E}if("array"===u&&f){if(f.xml=f.xml||_||{},f.xml.name=f.xml.name||_.name,_.wrapped)return y[n]=[],o()(d)?d.forEach(function(t){f.example=t,y[n].push(e(f,a))}):o()(g)?g.forEach(function(t){f.default=t,y[n].push(e(f,a))}):y[n]=[e(f,a)],b&&y[n].push({_attr:b}),y;var k=[];return o()(d)?(d.forEach(function(t){f.example=t,k.push(e(f,a))}),k):o()(g)?(g.forEach(function(t){f.default=t,k.push(e(f,a))}),k):e(f,a)}if("object"===u){var O=Object(i.A)(c);for(var A in y[n]=[],d=d||{},O)if(O.hasOwnProperty(A)&&(!O[A].readOnly||m)&&(!O[A].writeOnly||v))if(O[A].xml=O[A].xml||{},O[A].xml.attribute){var T=o()(O[A].enum)&&O[A].enum[0],j=O[A].example,P=O[A].default;b[O[A].xml.name||A]=void 0!==j&&j||void 0!==d[A]&&d[A]||void 0!==P&&P||T||h(O[A])}else{O[A].xml.name=O[A].xml.name||A,void 0===O[A].example&&void 0!==d[A]&&(O[A].example=d[A]);var I=e(O[A]);o()(I)?y[n]=y[n].concat(I):y[n].push(I)}return!0===l?y[n].push({additionalProp:"Anything can be here"}):l&&y[n].push({additionalProp:h(l)}),b&&y[n].push({_attr:b}),y}return r=void 0!==d?d:void 0!==g?g:o()(S)?S[0]:h(t),y[n]=b?[{_attr:b},r]:r,y};function g(e,t){var n=v(e,t);if(n)return s()(n,{declaration:!0,indent:"\t"})}var y=c()(g),b=c()(d)},function(e,t,n){"use strict";n.r(t),n.d(t,"UPDATE_CONFIGS",function(){return i}),n.d(t,"TOGGLE_CONFIGS",function(){return a}),n.d(t,"update",function(){return s}),n.d(t,"toggle",function(){return u}),n.d(t,"loaded",function(){return c});var r=n(2),o=n.n(r),i="configs_update",a="configs_toggle";function s(e,t){return{type:i,payload:o()({},e,t)}}function u(e){return{type:a,payload:e}}var c=function(){return function(){}}},function(e,t,n){"use strict";n.d(t,"a",function(){return a});var r=n(1),o=n.n(r),i=o.a.Set.of("type","format","items","default","maximum","exclusiveMaximum","minimum","exclusiveMinimum","maxLength","minLength","pattern","maxItems","minItems","uniqueItems","enum","multipleOf");function a(e){var t=(arguments.length>1&&void 0!==arguments[1]?arguments[1]:{}).isOAS3;if(!o.a.Map.isMap(e))return{schema:o.a.Map(),parameterContentMediaType:null};if(!t)return"body"===e.get("in")?{schema:e.get("schema",o.a.Map()),parameterContentMediaType:null}:{schema:e.filter(function(e,t){return i.includes(t)}),parameterContentMediaType:null};if(e.get("content")){var n=e.get("content",o.a.Map({})).keySeq().first();return{schema:e.getIn(["content",n,"schema"],o.a.Map()),parameterContentMediaType:n}}return{schema:e.get("schema",o.a.Map()),parameterContentMediaType:null}}},function(e,t,n){e.exports=n(781)},function(e,t,n){"use strict";n.r(t);var r=n(469),o="object"==typeof self&&self&&self.Object===Object&&self,i=(r.a||o||Function("return this")()).Symbol,a=Object.prototype,s=a.hasOwnProperty,u=a.toString,c=i?i.toStringTag:void 0;var l=function(e){var t=s.call(e,c),n=e[c];try{e[c]=void 0;var r=!0}catch(e){}var o=u.call(e);return r&&(t?e[c]=n:delete e[c]),o},p=Object.prototype.toString;var f=function(e){return p.call(e)},h="[object Null]",d="[object Undefined]",m=i?i.toStringTag:void 0;var v=function(e){return null==e?void 0===e?d:h:m&&m in Object(e)?l(e):f(e)};var g=function(e,t){return function(n){return e(t(n))}}(Object.getPrototypeOf,Object);var y=function(e){return null!=e&&"object"==typeof e},b="[object Object]",_=Function.prototype,w=Object.prototype,x=_.toString,E=w.hasOwnProperty,S=x.call(Object);var C=function(e){if(!y(e)||v(e)!=b)return!1;var t=g(e);if(null===t)return!0;var n=E.call(t,"constructor")&&t.constructor;return"function"==typeof n&&n instanceof n&&x.call(n)==S},k=n(330),O={INIT:"@@redux/INIT"};function A(e,t,n){var r;if("function"==typeof t&&void 0===n&&(n=t,t=void 0),void 0!==n){if("function"!=typeof n)throw new Error("Expected the enhancer to be a function.");return n(A)(e,t)}if("function"!=typeof e)throw new Error("Expected the reducer to be a function.");var o=e,i=t,a=[],s=a,u=!1;function c(){s===a&&(s=a.slice())}function l(){return i}function p(e){if("function"!=typeof e)throw new Error("Expected listener to be a function.");var t=!0;return c(),s.push(e),function(){if(t){t=!1,c();var n=s.indexOf(e);s.splice(n,1)}}}function f(e){if(!C(e))throw new Error("Actions must be plain objects. Use custom middleware for async actions.");if(void 0===e.type)throw new Error('Actions may not have an undefined "type" property. Have you misspelled a constant?');if(u)throw new Error("Reducers may not dispatch actions.");try{u=!0,i=o(i,e)}finally{u=!1}for(var t=a=s,n=0;n0&&void 0!==arguments[0]?arguments[0]:{},t=arguments[1];if(a)throw a;for(var r=!1,o={},s=0;s0?r:n)(e)}},function(e,t){e.exports={}},function(e,t,n){var r=n(348),o=n(215);e.exports=Object.keys||function(e){return r(e,o)}},function(e,t){var n={}.toString;e.exports=function(e){return n.call(e).slice(8,-1)}},function(e,t){e.exports=!0},function(e,t){e.exports=function(e){if("function"!=typeof e)throw TypeError(e+" is not a function!");return e}},function(e,t){e.exports=function(e,t){return{enumerable:!(1&e),configurable:!(2&e),writable:!(4&e),value:t}}},function(e,t,n){var r=n(49).f,o=n(75),i=n(34)("toStringTag");e.exports=function(e,t,n){e&&!o(e=n?e:e.prototype,i)&&r(e,i,{configurable:!0,value:t})}},function(e,t,n){var r=n(159)("meta"),o=n(43),i=n(75),a=n(49).f,s=0,u=Object.isExtensible||function(){return!0},c=!n(82)(function(){return u(Object.preventExtensions({}))}),l=function(e){a(e,r,{value:{i:"O"+ ++s,w:{}}})},p=e.exports={KEY:r,NEED:!1,fastKey:function(e,t){if(!o(e))return"symbol"==typeof e?e:("string"==typeof e?"S":"P")+e;if(!i(e,r)){if(!u(e))return"F";if(!t)return"E";l(e)}return e[r].i},getWeak:function(e,t){if(!i(e,r)){if(!u(e))return!0;if(!t)return!1;l(e)}return e[r].w},onFreeze:function(e){return c&&p.NEED&&u(e)&&!i(e,r)&&l(e),e}}},function(e,t,n){"use strict";e.exports=function(e){for(var t=arguments.length-1,n="Minified React error #"+e+"; visit http://facebook.github.io/react/docs/error-decoder.html?invariant="+e,r=0;r1&&void 0!==arguments[1]?arguments[1]:[],n={arrayBehaviour:(arguments.length>2&&void 0!==arguments[2]?arguments[2]:{}).arrayBehaviour||"replace"},r=t.map(function(e){return e||{}}),i=e||{},c=0;c1?t-1:0),r=1;r")}),p=function(){var e=/(?:)/,t=e.exec;e.exec=function(){return t.apply(this,arguments)};var n="ab".split(e);return 2===n.length&&"a"===n[0]&&"b"===n[1]}();e.exports=function(e,t,n){var f=s(e),h=!i(function(){var t={};return t[f]=function(){return 7},7!=""[e](t)}),d=h?!i(function(){var t=!1,n=/a/;return n.exec=function(){return t=!0,null},"split"===e&&(n.constructor={},n.constructor[c]=function(){return n}),n[f](""),!t}):void 0;if(!h||!d||"replace"===e&&!l||"split"===e&&!p){var m=/./[f],v=n(a,f,""[e],function(e,t,n,r,o){return t.exec===u?h&&!o?{done:!0,value:m.call(t,n,r)}:{done:!0,value:e.call(n,t,r)}:{done:!1}}),g=v[0],y=v[1];r(String.prototype,e,g),o(RegExp.prototype,f,2==t?function(e,t){return y.call(e,this,t)}:function(e){return y.call(e,this)})}}},function(e,t,n){var r=n(212),o=Math.min;e.exports=function(e){return e>0?o(r(e),9007199254740991):0}},function(e,t){var n=0,r=Math.random();e.exports=function(e){return"Symbol(".concat(void 0===e?"":e,")_",(++n+r).toString(36))}},function(e,t,n){var r=n(46),o=n(350),i=n(215),a=n(213)("IE_PROTO"),s=function(){},u=function(){var e,t=n(217)("iframe"),r=i.length;for(t.style.display="none",n(351).appendChild(t),t.src="javascript:",(e=t.contentWindow.document).open(),e.write("
    \ No newline at end of file diff --git a/running-programs/index.xml b/running-programs/index.xml index 82feed80bb..cdc66a9f6f 100644 --- a/running-programs/index.xml +++ b/running-programs/index.xml @@ -1 +1 @@ -Community Health Toolkit – Running CHT implementationshttps://docs.communityhealthtoolkit.org/running-programs/Recent content in Running CHT implementations on Community Health ToolkitHugo -- gohugo.ioen \ No newline at end of file +Running CHT implementations on Community Health Toolkithttps://docs.communityhealthtoolkit.org/running-programs/Recent content in Running CHT implementations on Community Health ToolkitHugo -- gohugo.ioenCHT training resourceshttps://docs.communityhealthtoolkit.org/running-programs/training/Mon, 01 Jan 0001 00:00:00 +0000https://docs.communityhealthtoolkit.org/running-programs/training/CHT training process In the implementation of CHT supported community health programs, health care workers such as CHWs, CHW supervisors and facility based health care providers need training to help equip them with the required knowledge and skills to effectively carry out their roles and responsibilities. For most deployments, program leads and ministry of health officials may also need training for them to perform their roles, oversee and supervise the community health programs. \ No newline at end of file diff --git a/running-programs/training/index.html b/running-programs/training/index.html index 86b3214e7d..9ae8996ebe 100644 --- a/running-programs/training/index.html +++ b/running-programs/training/index.html @@ -1,5 +1,5 @@ -CHT training resources | Community Health Toolkit -

    CHT training resources

    CHT training process

    In the implementation of CHT supported community health programs, health care workers such as CHWs, CHW supervisors and facility based health care providers need training to help equip them with the required knowledge and skills to effectively carry out their roles and responsibilities. For most deployments, program leads and ministry of health officials may also need training for them to perform their roles, oversee and supervise the community health programs. End user training needs to be well planned and implemented using standardized training methods and approaches which take into account the capacity of end users.

    For most community health programs, CHW training is divided into two major sections consisting of basic modules and technical modules. For basic module training, CHWs are trained on basic skills such as leadership, communication and counselling, basic health promotion practices and basic lifesaving skills. For technical training, end users are trained in the following common areas to help improve their skills and delivery:

    • Thematic training - this training focuses on the health program areas of interest, this will vary from one program to another depending on the specific health areas users are expected to support. For example, for an MNCH program, users will be trained in ANC, PNC and immunization.
    • Hardware and software training - users are trained on how they can operate an android phone, reboot the hardware, install and reinstall applications and how they can log into the CHT app. This training is critical for end users who have never interacted with a smart phone before.
    • CHT tool training - this involves training users on how they can use different CHT tools (SMS, mobile and web applications) to guide them to deliver health care services.
    • Skill development - users are trained on interpersonal communication, listening skills, capacity building skills and how they can promote healthy behaviour change.
    • Behavioural - the users are taught how to build relations in the community and how to work within diverse communities.

    Proper training of health care workers leads to increased job satisfaction ensuring health care workers are able to provide high quality care.

    This guide highlights some of the considerations and steps for planning and conducting end user training. It is based on the learnings from many collaborative trainings that have been conducted over the years. Some of the training resources have been co-created with various implementing partners and ministries of health. This guide can help partners learn how they can plan and conduct training.

    The CHT training process comprises a series of steps highlighted below that need to be followed systematically to ensure an efficient training programme.

    • Training needs assessment - this is the first step prior to conducting the training to help understand the users needs, behaviour, roles and how they perform their responsibilities. At this stage, information on the literacy and numerical skills of end users, demographics (age, gender, language of instruction), potential program champions is collected, this information helps inform the training approach, duration and methods of training. To collect the needs assessment information, various methods can be used including observation, interviews,focused group discussions and surveys.
    • Define the training objectives - In this step, the training outcomes are defined, these are things end users should be able to do as a result of the training.
    • Develop a training plan or checklist - The checklist lists the tasks, activities and resources needed to train the end users
    • Create the training materials and resources
    • Conduct the training - Different training approaches can be adopted. Training can be done in person or virtually. It includes direct training of end users, training of trainers (ToT) model which involves training program champions to cascade the training, use of CHT training cards in which users are able to access training resources in-app, remote online training and self learning through a learning management system.
    • Evaluate the training - In this stage, the training outcomes are assessed to determine if the training has achieved the expected outcomes. The learning objectives will help to determine the evaluation process that would be most appropriate.

    Pre training preparation

    Before training is done, a training preparation checklist is prepared to ensure all items listed below are catered to.

    Tech preparation

    • Set up application instance (training & production instances)
    • Create user accounts for all personas depending on the specific project
    • Set up users’ phones to ensure applications like Playstore and other related tools are available

    Logistics

    • Identify the total number of participants. It is recommended that the ratio of facilitators to participants should be 1:10 and each training class should have a maximum of 30 participants.
    • Confirm and organize training venue. The venue should be well ventilated, have adequate lighting, good sanitation and security and be at a central location for easy access. It is recommended that the training of the CHWs takes place in a community setting such as schools, churches or mosques while training of CHAs and program teams can happen in a facility or hotel.
    • Phone preparation
      • Procure phones for the training with minimum specs requirements
      • Ensure phones are fully charged prior to training
      • Guide users on how to obtain and register SIM cards
      • Load airtime into phones
    • Pack phones, chargers and any other applicable hardware to take to the training venue
    • Prepare the inventory with end-users names, SIM numbers and phone IMEI numbers
    • Prepare the mobile and SIM handover form with a documented mobile and SIM Management guideline handout
    • Confirm availability of electricity, mobile network and internet at the training venue
    • Invite the partner or ministry of health to lead the training
    • Organize a facilitators pre-planning meeting to assign each facilitator sessions they will be covering.
    • Facilitators should visit the training site prior to the training to make sure the site is ready for the planned training.
    • Organize and process payment forms, if applicable, for:
      • Trainer and co-trainer allowance
      • Participation allowance
      • Travel allowance for participants
      • Venue costs and other expenses

    Training materials

    • Prepare training materials
    • Prepare training agenda with daily training schedule. This should contain the trainer responsibilities and activities - here is an example of a training schedule
    • Prepare screenshots or posters of training materials as backup
    • Finalize user guides - here is an example of a user guide
    • Finalize on the trainer and co-trainer resources
    • Translate training materials and arrange for a translator, if required
    • Organize supplies and stationery (markers, pens, notebooks, meta cards, tape, newsprint, index cards, handouts, take-home guides, projector and other required training equipment)
    • Print training materials such as the ones listed below:

    Training

    Conducting a training

    Training should be approached in a cohesive and integrated manner, starting with the initial training of the end users, followed by regular refresher training, with continuous support, supervision and mentorship. Training is usually carried out collaboratively by the implementing partners, ministry of health officials and project implementers.

    Different training approaches can be used to train end users. Several factors determine the training approach that is to be used, this includes the number of trainees and facilitators, user demographics, learning objectives, training environment and the available resources. The training approaches include: the training of trainers (ToT) model, direct end-user training and remote training. The ToT model is used to train a large pool of end users. For this model, a cadre of end users are trained as ToTs and after the training they are expected to cascade, reinforce and support the training of other end users. Direct training involves a trainer conducting an in-person training of end users while for the remote training, end users are trained online or they can access the training resources on their devices.

    Training delivery methods

    There are a number of training methods that can be used to train end users. Blended training, participatory and interactive techniques can be used to make the training effective. The table below has a list of some of the delivery methods.

    MethodDescription
    Role playLearners act out the different roles they are assigned. Following the skit, there are discussions and conclusions related to the topic.
    PresentationsA facilitator delivers content through oral, video and visual aids.
    DemonstrationsA skill is demonstrated with a goal of facilitating understanding, eliciting comments and explanations.
    Pair workParticipants are able to work in pairs so that participants with low confidence are able to learn from other participants.
    Case studiesA facilitator presents facts about a theoretical case or scenario and learners are asked to intervene and make suggestions, followed by a conclusion about the case.
    Interactive simulationsThis method provides an opportunity for learners to practise a certain task for example how to register a new households on CHT.
    TeachbacksTrainees pick a topic or a skill they have learned in one of the sessions and teach it to their peers.
    Question and answerThe learners are provided with a set of questions which they are expected to answer so as to establish their prior level of understanding of a certain topic.
    Hands-on practiceLearners get to experience and practise what they have learned, for example a learner will get the experience of navigating a digital app.
    Individual work and assessmentsParticipants work individually to answer written or oral questions about a topic that has already been covered in the training.
    Flip chartsThese are wall and board hangings that can serve as visual aids to reinforce learning.
    Group assessmentsLearners work together to answer questions or complete assigned tasks aimed at assessing knowledge of topics already covered in the training.

    Post training activities

    After every day of training, facilitators should conduct training evaluation to discuss what went well and the areas to improve on the subsequent training days. After the completion of the training, ministries of health, implementing partners and CHT users agree on a date when the project can go live. Below are the critical activities that take place before go-live.

    • Participants are informed when the project will go live. In most cases, the project goes live a few weeks after the training
    • Participants are trained on how to install the production application
    • Paricipants are reminded to uninstall the training application
    • User accounts are created on the production instance and credentials shared
    • Users are informed on technical support structure to support with post-training issues
    • Users are sensitized on privacy and security best practices

    Evaluation

    Evaluation assesses whether the training objectives have been achieved. Evaluation of the training involves assessing the effect of the training observed against a set standard considered as an indication of learning. A common model used to evaluate the training consists of the following four levels:

    • Level 1 - this is the participants reaction towards the learning usually performed through a feedback survey, participants can evaluate the training content, facilitators, training approaches and the training venue. Trainers use this feedback to improve the training content and approach
    • Level 2 - This measures what learners have learnt and what they are able to perform as a result of the training. This can be done during the training through pre and post tests, simulations, practical tests and tasks
    • Level 3 - After the training event, the learners are evaluated if they are able to apply what they have learned when working. This can be done through observations, learning and interviews
    • Level 4 - This is concerned with the extent to which changes in behaviour after the training contribute to improved results and increased impact. This can be done after a series of training and refresher trainings and it requires before and after the training comparison
    -

    \ No newline at end of file + Create project issue
    \ No newline at end of file diff --git a/scss/main.min.33c917960080e36ed777f6a54b379d40f4fa45af154cbe006f5e0fba09a3904c.css b/scss/main.min.33c917960080e36ed777f6a54b379d40f4fa45af154cbe006f5e0fba09a3904c.css new file mode 100644 index 0000000000..d283529c45 --- /dev/null +++ b/scss/main.min.33c917960080e36ed777f6a54b379d40f4fa45af154cbe006f5e0fba09a3904c.css @@ -0,0 +1,9 @@ +@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,700,700i&display=swap";:root,[data-bs-theme=light]{--td-pre-bg:var(--bs-tertiary-bg)}h4[id]:before,[id].h4:before,h5[id]:before,[id].h5:before{margin-top:0!important;height:0!important}.right{float:right}.left{float:left}.partner-logos img{width:15%;min-width:120px;padding:.2em}h2,.h2,h3,.h3,.td-footer__links-item{clear:both}.one-liner{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.meta-links a{display:block}.alert{clear:both}#td-sidebar-menu .td-search-input{max-width:none}#td-sidebar-menu .td-sidebar__search{padding-bottom:0}.td-sidebar{padding-bottom:0!important}#td-section-nav .td-sidebar-nav__section label{padding-left:.7rem}#td-section-nav .td-sidebar-nav__section .without-child,#td-section-nav .td-sidebar-nav__section .with-child{padding-left:.5rem}#td-section-nav .td-sidebar-link__page{color:#222;font-weight:400}#td-section-nav .td-sidebar-link__page.active{color:#f58301;font-weight:700}#td-section-nav .td-sidebar-link__section.active{color:#5f7681}#td-section-nav .td-sidebar-link__section.selected{color:#f58301}.workflow{font-size:90%}.workflow .fas,.workflow .td-offline-search-results__close-button:after{font-size:2em;color:#007ac0}.workflow>.row:nth-child(n+2){border-top:1px solid #eee;padding:.5rem 0}.workflow>.row>div.text{padding-bottom:.5rem}.schedule+table th{width:3em}.schedule+table td:not(:first-child):not(:empty){vertical-align:middle;background-image:url(/circle.svg);background-repeat:no-repeat;background-position:50%;text-align:center;color:#78b159;font-size:1pt}.fa-rotate-45{transform:rotate(45deg)}.highlight table td:first-child code span{-webkit-user-select:none;-moz-user-select:none;user-select:none}.mermaid{padding-bottom:30px}.td-navbar .td-navbar-nav-scroll .navbar-nav{padding-bottom:0!important}.td-navbar .td-navbar-nav-scroll{height:0!important}:root,[data-bs-theme=light]{--td-pre-bg:var(--bs-tertiary-bg)}/*!* Bootstrap v5.3.3 (https://getbootstrap.com/) +* Copyright 2011-2024 The Bootstrap Authors +* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)*/:root,[data-bs-theme=light]{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-black:#000;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#5f7681;--bs-secondary:#F58301;--bs-success:#3772ff;--bs-info:#007AC0;--bs-warning:#E33030;--bs-danger:#ed6a5a;--bs-light:#d3f3ee;--bs-dark:#333F47;--bs-primary-rgb:95, 118, 129;--bs-secondary-rgb:245, 131, 1;--bs-success-rgb:55, 114, 255;--bs-info-rgb:0, 122, 192;--bs-warning-rgb:227, 48, 48;--bs-danger-rgb:237, 106, 90;--bs-light-rgb:211, 243, 238;--bs-dark-rgb:51, 63, 71;--bs-primary-text-emphasis:#262f34;--bs-secondary-text-emphasis:#623400;--bs-success-text-emphasis:#162e66;--bs-info-text-emphasis:#00314d;--bs-warning-text-emphasis:#5b1313;--bs-danger-text-emphasis:#5f2a24;--bs-light-text-emphasis:#495057;--bs-dark-text-emphasis:#495057;--bs-primary-bg-subtle:#dfe4e6;--bs-secondary-bg-subtle:#fde6cc;--bs-success-bg-subtle:#d7e3ff;--bs-info-bg-subtle:#cce4f2;--bs-warning-bg-subtle:#f9d6d6;--bs-danger-bg-subtle:#fbe1de;--bs-light-bg-subtle:#fcfcfd;--bs-dark-bg-subtle:#ced4da;--bs-primary-border-subtle:#bfc8cd;--bs-secondary-border-subtle:#fbcd99;--bs-success-border-subtle:#afc7ff;--bs-info-border-subtle:#99cae6;--bs-warning-border-subtle:#f4acac;--bs-danger-border-subtle:#f8c3bd;--bs-light-border-subtle:#e9ecef;--bs-dark-border-subtle:#adb5bd;--bs-white-rgb:255, 255, 255;--bs-black-rgb:0, 0, 0;--bs-font-sans-serif:"Open Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";--bs-font-monospace:SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family:"Open Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-color-rgb:33, 37, 41;--bs-body-bg:#fff;--bs-body-bg-rgb:255, 255, 255;--bs-emphasis-color:#000;--bs-emphasis-color-rgb:0, 0, 0;--bs-secondary-color:rgba(33, 37, 41, 0.75);--bs-secondary-color-rgb:33, 37, 41;--bs-secondary-bg:#e9ecef;--bs-secondary-bg-rgb:233, 236, 239;--bs-tertiary-color:rgba(33, 37, 41, 0.5);--bs-tertiary-color-rgb:33, 37, 41;--bs-tertiary-bg:#f8f9fa;--bs-tertiary-bg-rgb:248, 249, 250;--bs-heading-color:inherit;--bs-link-color:#0d6efd;--bs-link-color-rgb:13, 110, 253;--bs-link-decoration:underline;--bs-link-hover-color:#094db1;--bs-link-hover-color-rgb:9, 77, 177;--bs-code-color:#99641d;--bs-highlight-color:#212529;--bs-highlight-bg:#fff3cd;--bs-border-width:1px;--bs-border-style:solid;--bs-border-color:#dee2e6;--bs-border-color-translucent:rgba(0, 0, 0, 0.175);--bs-border-radius:0.375rem;--bs-border-radius-sm:0.25rem;--bs-border-radius-lg:0.5rem;--bs-border-radius-xl:1rem;--bs-border-radius-xxl:2rem;--bs-border-radius-2xl:var(--bs-border-radius-xxl);--bs-border-radius-pill:50rem;--bs-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-box-shadow-sm:0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-box-shadow-lg:0 1rem 3rem rgba(0, 0, 0, 0.175);--bs-box-shadow-inset:inset 0 1px 2px rgba(0, 0, 0, 0.075);--bs-focus-ring-width:0.25rem;--bs-focus-ring-opacity:0.25;--bs-focus-ring-color:rgba(95, 118, 129, 0.25);--bs-form-valid-color:#3772ff;--bs-form-valid-border-color:#3772ff;--bs-form-invalid-color:#ed6a5a;--bs-form-invalid-border-color:#ed6a5a}[data-bs-theme=dark]{color-scheme:dark;--bs-body-color:#dee2e6;--bs-body-color-rgb:222, 226, 230;--bs-body-bg:#212529;--bs-body-bg-rgb:33, 37, 41;--bs-emphasis-color:#fff;--bs-emphasis-color-rgb:255, 255, 255;--bs-secondary-color:rgba(222, 226, 230, 0.75);--bs-secondary-color-rgb:222, 226, 230;--bs-secondary-bg:#343a40;--bs-secondary-bg-rgb:52, 58, 64;--bs-tertiary-color:rgba(222, 226, 230, 0.5);--bs-tertiary-color-rgb:222, 226, 230;--bs-tertiary-bg:#2b3035;--bs-tertiary-bg-rgb:43, 48, 53;--bs-primary-text-emphasis:#9fadb3;--bs-secondary-text-emphasis:#f9b567;--bs-success-text-emphasis:#87aaff;--bs-info-text-emphasis:#66afd9;--bs-warning-text-emphasis:#ee8383;--bs-danger-text-emphasis:#f4a69c;--bs-light-text-emphasis:#f8f9fa;--bs-dark-text-emphasis:#dee2e6;--bs-primary-bg-subtle:#13181a;--bs-secondary-bg-subtle:#311a00;--bs-success-bg-subtle:#0b1733;--bs-info-bg-subtle:#001826;--bs-warning-bg-subtle:#2d0a0a;--bs-danger-bg-subtle:#2f1512;--bs-light-bg-subtle:#343a40;--bs-dark-bg-subtle:#1a1d20;--bs-primary-border-subtle:#39474d;--bs-secondary-border-subtle:#934f01;--bs-success-border-subtle:#214499;--bs-info-border-subtle:#004973;--bs-warning-border-subtle:#881d1d;--bs-danger-border-subtle:#8e4036;--bs-light-border-subtle:#495057;--bs-dark-border-subtle:#343a40;--bs-heading-color:inherit;--bs-link-color:#9fadb3;--bs-link-hover-color:#bcc6ca;--bs-link-color-rgb:159, 173, 179;--bs-link-hover-color-rgb:188, 198, 202;--bs-code-color:#c2a277;--bs-highlight-color:#dee2e6;--bs-highlight-bg:#664d03;--bs-border-color:#495057;--bs-border-color-translucent:rgba(255, 255, 255, 0.15);--bs-form-valid-color:#75b798;--bs-form-valid-border-color:#75b798;--bs-form-invalid-color:#ea868f;--bs-form-invalid-border-color:#ea868f}*,*::before,*::after{box-sizing:border-box}@media(prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;border:0;border-top:var(--bs-border-width)solid;opacity:.25}h6,.h6,h5,.h5,h4,.h4,h3,.h3,.td-footer__links-item,h2,.h2,h1,.h1{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2;color:var(--bs-heading-color)}h1,.h1{font-size:calc(1.375rem + 1.5vw)}@media(min-width:1200px){h1,.h1{font-size:2.5rem}}h2,.h2{font-size:calc(1.325rem + .9vw)}@media(min-width:1200px){h2,.h2{font-size:2rem}}h3,.h3,.td-footer__links-item{font-size:calc(1.275rem + .3vw)}@media(min-width:1200px){h3,.h3,.td-footer__links-item{font-size:1.5rem}}h4,.h4{font-size:calc(1.26rem + .12vw)}@media(min-width:1200px){h4,.h4{font-size:1.35rem}}h5,.h5{font-size:1.15rem}h6,.h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}ol,ul,dl{margin-top:0;margin-bottom:1rem}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small,.small,.td-footer__center,.td-cover-block>.byline{font-size:.875em}mark,.mark{padding:.1875em;color:var(--bs-highlight-color);background-color:var(--bs-highlight-bg)}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,1));text-decoration:underline}a:hover{--bs-link-color-rgb:var(--bs-link-hover-color-rgb)}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}pre,code,kbd,samp{font-family:var(--bs-font-monospace);font-size:1em}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:var(--bs-code-color);word-wrap:break-word}a>code{color:inherit}kbd{padding:.1875rem .375rem;font-size:.875em;color:var(--bs-body-bg);background-color:var(--bs-body-color);border-radius:.25rem}kbd kbd{padding:0;font-size:1em}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-secondary-color);text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}thead,tbody,tfoot,tr,td,th{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}input,button,select,optgroup,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator{display:none!important}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}button:not(:disabled),[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media(min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-text,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:calc(1.625rem + 4.5vw);font-weight:300;line-height:1.2}@media(min-width:1200px){.display-1{font-size:5rem}}.display-2{font-size:calc(1.575rem + 3.9vw);font-weight:300;line-height:1.2}@media(min-width:1200px){.display-2{font-size:4.5rem}}.display-3{font-size:calc(1.525rem + 3.3vw);font-weight:300;line-height:1.2}@media(min-width:1200px){.display-3{font-size:4rem}}.display-4{font-size:calc(1.475rem + 2.7vw);font-weight:300;line-height:1.2}@media(min-width:1200px){.display-4{font-size:3.5rem}}.display-5{font-size:calc(1.425rem + 2.1vw);font-weight:300;line-height:1.2}@media(min-width:1200px){.display-5{font-size:3rem}}.display-6{font-size:calc(1.375rem + 1.5vw);font-weight:300;line-height:1.2}@media(min-width:1200px){.display-6{font-size:2.5rem}}.list-unstyled,.td-blog-posts-list{padding-left:0;list-style:none}.list-inline,.td-footer__links-list{padding-left:0;list-style:none}.list-inline-item,.td-footer__links-item{display:inline-block}.list-inline-item:not(:last-child),.td-footer__links-item:not(:last-child){margin-right:1rem}.initialism{font-size:.875em;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:.875em;color:#6c757d}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid,.td-content img{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:var(--bs-body-bg);border:var(--bs-border-width)solid var(--bs-border-color);border-radius:var(--bs-border-radius);box-shadow:var(--bs-box-shadow-sm);max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:.875em;color:var(--bs-secondary-color)}.container,.container-fluid,.container-xxl,.container-xl,.container-lg,.container-md,.container-sm{--bs-gutter-x:1.5rem;--bs-gutter-y:0;width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-right:auto;margin-left:auto}@media(min-width:576px){.container-sm,.container{max-width:540px}}@media(min-width:768px){.container-md,.container-sm,.container{max-width:720px}}@media(min-width:992px){.container-lg,.container-md,.container-sm,.container{max-width:960px}}@media(min-width:1200px){.container-xl,.container-lg,.container-md,.container-sm,.container{max-width:1140px}}@media(min-width:1400px){.container-xxl,.container-xl,.container-lg,.container-md,.container-sm,.container{max-width:1320px}}:root{--bs-breakpoint-xs:0;--bs-breakpoint-sm:576px;--bs-breakpoint-md:768px;--bs-breakpoint-lg:992px;--bs-breakpoint-xl:1200px;--bs-breakpoint-xxl:1400px}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(-1 * var(--bs-gutter-y));margin-right:calc(-.5 * var(--bs-gutter-x));margin-left:calc(-.5 * var(--bs-gutter-x))}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-top:var(--bs-gutter-y)}.col{flex:1 0}.row-cols-auto>*{flex:none;width:auto}.row-cols-1>*{flex:none;width:100%}.row-cols-2>*{flex:none;width:50%}.row-cols-3>*{flex:none;width:33.33333333%}.row-cols-4>*{flex:none;width:25%}.row-cols-5>*{flex:none;width:20%}.row-cols-6>*{flex:none;width:16.66666667%}.col-auto{flex:none;width:auto}.col-1{flex:none;width:8.33333333%}.col-2{flex:none;width:16.66666667%}.col-3{flex:none;width:25%}.col-4{flex:none;width:33.33333333%}.col-5{flex:none;width:41.66666667%}.col-6{flex:none;width:50%}.col-7{flex:none;width:58.33333333%}.col-8{flex:none;width:66.66666667%}.col-9{flex:none;width:75%}.col-10{flex:none;width:83.33333333%}.col-11{flex:none;width:91.66666667%}.col-12{flex:none;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.25rem}.g-1,.gy-1{--bs-gutter-y:0.25rem}.g-2,.gx-2{--bs-gutter-x:0.5rem}.g-2,.gy-2{--bs-gutter-y:0.5rem}.g-3,.gx-3{--bs-gutter-x:1rem}.g-3,.gy-3{--bs-gutter-y:1rem}.g-4,.gx-4{--bs-gutter-x:1.5rem}.g-4,.gy-4{--bs-gutter-y:1.5rem}.g-5,.gx-5{--bs-gutter-x:3rem}.g-5,.gy-5{--bs-gutter-y:3rem}@media(min-width:576px){.col-sm{flex:1 0}.row-cols-sm-auto>*{flex:none;width:auto}.row-cols-sm-1>*{flex:none;width:100%}.row-cols-sm-2>*{flex:none;width:50%}.row-cols-sm-3>*{flex:none;width:33.33333333%}.row-cols-sm-4>*{flex:none;width:25%}.row-cols-sm-5>*{flex:none;width:20%}.row-cols-sm-6>*{flex:none;width:16.66666667%}.col-sm-auto{flex:none;width:auto}.col-sm-1{flex:none;width:8.33333333%}.col-sm-2{flex:none;width:16.66666667%}.col-sm-3{flex:none;width:25%}.col-sm-4{flex:none;width:33.33333333%}.col-sm-5{flex:none;width:41.66666667%}.col-sm-6{flex:none;width:50%}.col-sm-7{flex:none;width:58.33333333%}.col-sm-8{flex:none;width:66.66666667%}.col-sm-9{flex:none;width:75%}.col-sm-10{flex:none;width:83.33333333%}.col-sm-11{flex:none;width:91.66666667%}.col-sm-12{flex:none;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.25rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.25rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:0.5rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:0.5rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem}}@media(min-width:768px){.col-md{flex:1 0}.row-cols-md-auto>*{flex:none;width:auto}.row-cols-md-1>*{flex:none;width:100%}.row-cols-md-2>*{flex:none;width:50%}.row-cols-md-3>*{flex:none;width:33.33333333%}.row-cols-md-4>*{flex:none;width:25%}.row-cols-md-5>*{flex:none;width:20%}.row-cols-md-6>*{flex:none;width:16.66666667%}.col-md-auto{flex:none;width:auto}.col-md-1{flex:none;width:8.33333333%}.col-md-2{flex:none;width:16.66666667%}.col-md-3{flex:none;width:25%}.col-md-4{flex:none;width:33.33333333%}.col-md-5{flex:none;width:41.66666667%}.col-md-6{flex:none;width:50%}.col-md-7{flex:none;width:58.33333333%}.col-md-8{flex:none;width:66.66666667%}.col-md-9{flex:none;width:75%}.col-md-10{flex:none;width:83.33333333%}.col-md-11{flex:none;width:91.66666667%}.col-md-12{flex:none;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.25rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.25rem}.g-md-2,.gx-md-2{--bs-gutter-x:0.5rem}.g-md-2,.gy-md-2{--bs-gutter-y:0.5rem}.g-md-3,.gx-md-3{--bs-gutter-x:1rem}.g-md-3,.gy-md-3{--bs-gutter-y:1rem}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem}.g-md-5,.gx-md-5{--bs-gutter-x:3rem}.g-md-5,.gy-md-5{--bs-gutter-y:3rem}}@media(min-width:992px){.col-lg{flex:1 0}.row-cols-lg-auto>*{flex:none;width:auto}.row-cols-lg-1>*{flex:none;width:100%}.row-cols-lg-2>*{flex:none;width:50%}.row-cols-lg-3>*{flex:none;width:33.33333333%}.row-cols-lg-4>*{flex:none;width:25%}.row-cols-lg-5>*{flex:none;width:20%}.row-cols-lg-6>*{flex:none;width:16.66666667%}.col-lg-auto{flex:none;width:auto}.col-lg-1{flex:none;width:8.33333333%}.col-lg-2{flex:none;width:16.66666667%}.col-lg-3{flex:none;width:25%}.col-lg-4{flex:none;width:33.33333333%}.col-lg-5{flex:none;width:41.66666667%}.col-lg-6{flex:none;width:50%}.col-lg-7{flex:none;width:58.33333333%}.col-lg-8{flex:none;width:66.66666667%}.col-lg-9{flex:none;width:75%}.col-lg-10{flex:none;width:83.33333333%}.col-lg-11{flex:none;width:91.66666667%}.col-lg-12{flex:none;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.25rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.25rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:0.5rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:0.5rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem}}@media(min-width:1200px){.col-xl{flex:1 0}.row-cols-xl-auto>*{flex:none;width:auto}.row-cols-xl-1>*{flex:none;width:100%}.row-cols-xl-2>*{flex:none;width:50%}.row-cols-xl-3>*{flex:none;width:33.33333333%}.row-cols-xl-4>*{flex:none;width:25%}.row-cols-xl-5>*{flex:none;width:20%}.row-cols-xl-6>*{flex:none;width:16.66666667%}.col-xl-auto{flex:none;width:auto}.col-xl-1{flex:none;width:8.33333333%}.col-xl-2{flex:none;width:16.66666667%}.col-xl-3{flex:none;width:25%}.col-xl-4{flex:none;width:33.33333333%}.col-xl-5{flex:none;width:41.66666667%}.col-xl-6{flex:none;width:50%}.col-xl-7{flex:none;width:58.33333333%}.col-xl-8{flex:none;width:66.66666667%}.col-xl-9{flex:none;width:75%}.col-xl-10{flex:none;width:83.33333333%}.col-xl-11{flex:none;width:91.66666667%}.col-xl-12{flex:none;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.25rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.25rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:0.5rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:0.5rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem}}@media(min-width:1400px){.col-xxl{flex:1 0}.row-cols-xxl-auto>*{flex:none;width:auto}.row-cols-xxl-1>*{flex:none;width:100%}.row-cols-xxl-2>*{flex:none;width:50%}.row-cols-xxl-3>*{flex:none;width:33.33333333%}.row-cols-xxl-4>*{flex:none;width:25%}.row-cols-xxl-5>*{flex:none;width:20%}.row-cols-xxl-6>*{flex:none;width:16.66666667%}.col-xxl-auto{flex:none;width:auto}.col-xxl-1{flex:none;width:8.33333333%}.col-xxl-2{flex:none;width:16.66666667%}.col-xxl-3{flex:none;width:25%}.col-xxl-4{flex:none;width:33.33333333%}.col-xxl-5{flex:none;width:41.66666667%}.col-xxl-6{flex:none;width:50%}.col-xxl-7{flex:none;width:58.33333333%}.col-xxl-8{flex:none;width:66.66666667%}.col-xxl-9{flex:none;width:75%}.col-xxl-10{flex:none;width:83.33333333%}.col-xxl-11{flex:none;width:91.66666667%}.col-xxl-12{flex:none;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.25rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.25rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:0.5rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:0.5rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem}}.table,.td-table:not(.td-initial),.td-content table:not(.td-initial),.td-box table:not(.td-initial){--bs-table-color-type:initial;--bs-table-bg-type:initial;--bs-table-color-state:initial;--bs-table-bg-state:initial;--bs-table-color:var(--bs-emphasis-color);--bs-table-bg:var(--bs-body-bg);--bs-table-border-color:var(--bs-border-color);--bs-table-accent-bg:transparent;--bs-table-striped-color:var(--bs-emphasis-color);--bs-table-striped-bg:rgba(var(--bs-emphasis-color-rgb), 0.05);--bs-table-active-color:var(--bs-emphasis-color);--bs-table-active-bg:rgba(var(--bs-emphasis-color-rgb), 0.1);--bs-table-hover-color:var(--bs-emphasis-color);--bs-table-hover-bg:rgba(var(--bs-emphasis-color-rgb), 0.075);width:100%;margin-bottom:1rem;vertical-align:top;border-color:var(--bs-table-border-color)}.table>:not(caption)>*>*,.td-table:not(.td-initial)>:not(caption)>*>*,.td-content table:not(.td-initial)>:not(caption)>*>*,.td-box table:not(.td-initial)>:not(caption)>*>*{padding:.5rem;color:var(--bs-table-color-state,var(--bs-table-color-type,var(--bs-table-color)));background-color:var(--bs-table-bg);border-bottom-width:var(--bs-border-width);box-shadow:inset 0 0 0 9999px var(--bs-table-bg-state,var(--bs-table-bg-type,var(--bs-table-accent-bg)))}.table>tbody,.td-table:not(.td-initial)>tbody,.td-content table:not(.td-initial)>tbody,.td-box table:not(.td-initial)>tbody{vertical-align:inherit}.table>thead,.td-table:not(.td-initial)>thead,.td-content table:not(.td-initial)>thead,.td-box table:not(.td-initial)>thead{vertical-align:bottom}.table-group-divider{border-top:calc(var(--bs-border-width) * 2)solid}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem}.table-bordered>:not(caption)>*{border-width:var(--bs-border-width)0}.table-bordered>:not(caption)>*>*{border-width:0 var(--bs-border-width)}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-borderless>:not(:first-child){border-top-width:0}.table-striped>tbody>tr:nth-of-type(odd)>*,.td-table:not(.td-initial)>tbody>tr:nth-of-type(odd)>*,.td-content table:not(.td-initial)>tbody>tr:nth-of-type(odd)>*,.td-box table:not(.td-initial)>tbody>tr:nth-of-type(odd)>*{--bs-table-color-type:var(--bs-table-striped-color);--bs-table-bg-type:var(--bs-table-striped-bg)}.table-striped-columns>:not(caption)>tr>:nth-child(even){--bs-table-color-type:var(--bs-table-striped-color);--bs-table-bg-type:var(--bs-table-striped-bg)}.table-active{--bs-table-color-state:var(--bs-table-active-color);--bs-table-bg-state:var(--bs-table-active-bg)}.table-hover>tbody>tr:hover>*{--bs-table-color-state:var(--bs-table-hover-color);--bs-table-bg-state:var(--bs-table-hover-bg)}.table-primary{--bs-table-color:#000;--bs-table-bg:#dfe4e6;--bs-table-border-color:#b2b6b8;--bs-table-striped-bg:#d4d9db;--bs-table-striped-color:#000;--bs-table-active-bg:#c9cdcf;--bs-table-active-color:#000;--bs-table-hover-bg:#ced3d5;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-secondary{--bs-table-color:#000;--bs-table-bg:#fde6cc;--bs-table-border-color:#cab8a3;--bs-table-striped-bg:#f0dbc2;--bs-table-striped-color:#000;--bs-table-active-bg:#e4cfb8;--bs-table-active-color:#000;--bs-table-hover-bg:#ead5bd;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-success{--bs-table-color:#000;--bs-table-bg:#d7e3ff;--bs-table-border-color:#acb6cc;--bs-table-striped-bg:#ccd8f2;--bs-table-striped-color:#000;--bs-table-active-bg:#c2cce6;--bs-table-active-color:#000;--bs-table-hover-bg:#c7d2ec;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-info{--bs-table-color:#000;--bs-table-bg:#cce4f2;--bs-table-border-color:#a3b6c2;--bs-table-striped-bg:#c2d9e6;--bs-table-striped-color:#000;--bs-table-active-bg:#b8cdda;--bs-table-active-color:#000;--bs-table-hover-bg:#bdd3e0;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-warning{--bs-table-color:#000;--bs-table-bg:#f9d6d6;--bs-table-border-color:#c7abab;--bs-table-striped-bg:#edcbcb;--bs-table-striped-color:#000;--bs-table-active-bg:#e0c1c1;--bs-table-active-color:#000;--bs-table-hover-bg:#e6c6c6;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-danger{--bs-table-color:#000;--bs-table-bg:#fbe1de;--bs-table-border-color:#c9b4b2;--bs-table-striped-bg:#eed6d3;--bs-table-striped-color:#000;--bs-table-active-bg:#e2cbc8;--bs-table-active-color:#000;--bs-table-hover-bg:#e8d0cd;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-light{--bs-table-color:#000;--bs-table-bg:#d3f3ee;--bs-table-border-color:#a9c2be;--bs-table-striped-bg:#c8e7e2;--bs-table-striped-color:#000;--bs-table-active-bg:#bedbd6;--bs-table-active-color:#000;--bs-table-hover-bg:#c3e1dc;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-dark{--bs-table-color:#fff;--bs-table-bg:#333F47;--bs-table-border-color:#5c656c;--bs-table-striped-bg:#3d4950;--bs-table-striped-color:#fff;--bs-table-active-bg:#475259;--bs-table-active-color:#fff;--bs-table-hover-bg:#424d55;--bs-table-hover-color:#fff;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-responsive,.td-table:not(.td-initial),.td-content table:not(.td-initial),.td-box table:not(.td-initial){overflow-x:auto;-webkit-overflow-scrolling:touch}@media(max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label{margin-bottom:.5rem}.col-form-label{padding-top:calc(.375rem + var(--bs-border-width));padding-bottom:calc(.375rem + var(--bs-border-width));margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + var(--bs-border-width));padding-bottom:calc(.5rem + var(--bs-border-width));font-size:1.25rem}.col-form-label-sm{padding-top:calc(.25rem + var(--bs-border-width));padding-bottom:calc(.25rem + var(--bs-border-width));font-size:.875rem}.form-text{margin-top:.25rem;font-size:.875em;color:var(--bs-secondary-color)}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--bs-body-bg);background-clip:padding-box;border:var(--bs-border-width)solid var(--bs-border-color);border-radius:var(--bs-border-radius);box-shadow:var(--bs-box-shadow-inset);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:var(--bs-body-color);background-color:var(--bs-body-bg);border-color:#afbbc0;outline:0;box-shadow:var(--bs-box-shadow-inset),0 0 0 .25rem rgba(95,118,129,.25)}.form-control::-webkit-date-and-time-value{min-width:85px;height:1.5em;margin:0}.form-control::-webkit-datetime-edit{display:block;padding:0}.form-control::-moz-placeholder{color:var(--bs-secondary-color);opacity:1}.form-control::placeholder{color:var(--bs-secondary-color);opacity:1}.form-control:disabled{background-color:var(--bs-secondary-bg);opacity:1}.form-control::file-selector-button{padding:.375rem .75rem;margin:-.375rem -.75rem;margin-inline-end:.75rem;color:var(--bs-body-color);background-color:var(--bs-tertiary-bg);background-image:var(--bs-gradient);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:var(--bs-border-width);border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:var(--bs-secondary-bg)}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:var(--bs-body-color);background-color:transparent;border:solid transparent;border-width:var(--bs-border-width)0}.form-control-plaintext:focus{outline:0}.form-control-plaintext.form-control-sm,.form-control-plaintext.form-control-lg{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2));padding:.25rem .5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-.25rem -.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2));padding:.5rem 1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-.5rem -1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + .75rem + calc(var(--bs-border-width) * 2))}textarea.form-control-sm{min-height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2))}textarea.form-control-lg{min-height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2))}.form-control-color{width:3rem;height:calc(1.5em + .75rem + calc(var(--bs-border-width) * 2));padding:.375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{border:0!important;border-radius:var(--bs-border-radius)}.form-control-color::-webkit-color-swatch{border:0!important;border-radius:var(--bs-border-radius)}.form-control-color.form-control-sm{height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2))}.form-control-color.form-control-lg{height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2))}.form-select{--bs-form-select-bg-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--bs-body-bg);background-image:var(--bs-form-select-bg-img),var(--bs-form-select-bg-icon,none);background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:var(--bs-border-width)solid var(--bs-border-color);border-radius:var(--bs-border-radius);box-shadow:var(--bs-box-shadow-inset);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-select{transition:none}}.form-select:focus{border-color:#afbbc0;outline:0;box-shadow:var(--bs-box-shadow-inset),0 0 0 .25rem rgba(95,118,129,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none}.form-select:disabled{background-color:var(--bs-secondary-bg)}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 var(--bs-body-color)}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}[data-bs-theme=dark] .form-select{--bs-form-select-bg-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23dee2e6' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e")}.form-check{display:block;min-height:1.5rem;padding-left:1.5em;margin-bottom:.125rem}.form-check .form-check-input{float:left;margin-left:-1.5em}.form-check-reverse{padding-right:1.5em;padding-left:0;text-align:right}.form-check-reverse .form-check-input{float:right;margin-right:-1.5em;margin-left:0}.form-check-input{--bs-form-check-bg:var(--bs-body-bg);flex-shrink:0;width:1em;height:1em;margin-top:.25em;vertical-align:top;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--bs-form-check-bg);background-image:var(--bs-form-check-bg-image);background-repeat:no-repeat;background-position:50%;background-size:contain;border:var(--bs-border-width)solid var(--bs-border-color);-webkit-print-color-adjust:exact;print-color-adjust:exact}.form-check-input[type=checkbox]{border-radius:.25em}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:#afbbc0;outline:0;box-shadow:0 0 0 .25rem rgba(95,118,129,.25)}.form-check-input:checked{background-color:#5f7681;border-color:#5f7681}.form-check-input:checked[type=checkbox]{--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e"), var(--bs-gradient)}.form-check-input:checked[type=radio]{--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e"), var(--bs-gradient)}.form-check-input[type=checkbox]:indeterminate{background-color:#5f7681;border-color:#5f7681;--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e"), var(--bs-gradient)}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input[disabled]~.form-check-label,.form-check-input:disabled~.form-check-label{cursor:default;opacity:.5}.form-switch{padding-left:2.5em}.form-switch .form-check-input{--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");width:2em;margin-left:-2.5em;background-image:var(--bs-form-switch-bg);background-position:0;border-radius:2em;transition:background-position .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23afbbc0'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:100%;--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"), var(--bs-gradient)}.form-switch.form-check-reverse{padding-right:2.5em;padding-left:0}.form-switch.form-check-reverse .form-check-input{margin-right:-2.5em;margin-left:0}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.btn-check[disabled]+.btn,div.drawio .btn-check[disabled]+button,.td-blog .btn-check[disabled]+.td-rss-button,.btn-check:disabled+.btn,div.drawio .btn-check:disabled+button,.td-blog .btn-check:disabled+.td-rss-button{pointer-events:none;filter:none;opacity:.65}[data-bs-theme=dark] .form-switch .form-check-input:not(:checked):not(:focus){--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%28255, 255, 255, 0.25%29'/%3e%3c/svg%3e")}.form-range{width:100%;height:1.5rem;padding:0;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:transparent}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(95,118,129,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(95,118,129,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;-webkit-appearance:none;appearance:none;background-color:#5f7681;background-image:var(--bs-gradient);border:0;border-radius:1rem;box-shadow:0 .1rem .25rem rgba(0,0,0,.1);-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#cfd6d9;background-image:var(--bs-gradient)}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:var(--bs-secondary-bg);border-color:transparent;border-radius:1rem;box-shadow:var(--bs-box-shadow-inset)}.form-range::-moz-range-thumb{width:1rem;height:1rem;-moz-appearance:none;appearance:none;background-color:#5f7681;background-image:var(--bs-gradient);border:0;border-radius:1rem;box-shadow:0 .1rem .25rem rgba(0,0,0,.1);-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{-moz-transition:none;transition:none}}.form-range::-moz-range-thumb:active{background-color:#cfd6d9;background-image:var(--bs-gradient)}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:var(--bs-secondary-bg);border-color:transparent;border-radius:1rem;box-shadow:var(--bs-box-shadow-inset)}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:var(--bs-secondary-color)}.form-range:disabled::-moz-range-thumb{background-color:var(--bs-secondary-color)}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-control-plaintext,.form-floating>.form-select{height:calc(3.5rem + calc(var(--bs-border-width) * 2));min-height:calc(3.5rem + calc(var(--bs-border-width) * 2));line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;z-index:2;height:100%;padding:1rem .75rem;overflow:hidden;text-align:start;text-overflow:ellipsis;white-space:nowrap;pointer-events:none;border:var(--bs-border-width)solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media(prefers-reduced-motion:reduce){.form-floating>label{transition:none}}.form-floating>.form-control,.form-floating>.form-control-plaintext{padding:1rem .75rem}.form-floating>.form-control::-moz-placeholder,.form-floating>.form-control-plaintext::-moz-placeholder{color:transparent}.form-floating>.form-control::placeholder,.form-floating>.form-control-plaintext::placeholder{color:transparent}.form-floating>.form-control:not(:-moz-placeholder-shown),.form-floating>.form-control-plaintext:not(:-moz-placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown),.form-floating>.form-control-plaintext:focus,.form-floating>.form-control-plaintext:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:-webkit-autofill,.form-floating>.form-control-plaintext:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:not(:-moz-placeholder-shown)~label{color:rgba(var(--bs-body-color-rgb),.65);transform:scale(.85)translateY(-.5rem)translateX(.15rem)}.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-control-plaintext~label,.form-floating>.form-select~label{color:rgba(var(--bs-body-color-rgb),.65);transform:scale(.85)translateY(-.5rem)translateX(.15rem)}.form-floating>.form-control:not(:-moz-placeholder-shown)~label::after{position:absolute;inset:1rem .375rem;z-index:-1;height:1.5em;content:"";background-color:var(--bs-body-bg);border-radius:var(--bs-border-radius)}.form-floating>.form-control:focus~label::after,.form-floating>.form-control:not(:placeholder-shown)~label::after,.form-floating>.form-control-plaintext~label::after,.form-floating>.form-select~label::after{position:absolute;inset:1rem .375rem;z-index:-1;height:1.5em;content:"";background-color:var(--bs-body-bg);border-radius:var(--bs-border-radius)}.form-floating>.form-control:-webkit-autofill~label{color:rgba(var(--bs-body-color-rgb),.65);transform:scale(.85)translateY(-.5rem)translateX(.15rem)}.form-floating>.form-control-plaintext~label{border-width:var(--bs-border-width)0}.form-floating>:disabled~label,.form-floating>.form-control:disabled~label{color:#6c757d}.form-floating>:disabled~label::after,.form-floating>.form-control:disabled~label::after{background-color:var(--bs-secondary-bg)}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-select,.input-group>.form-floating{position:relative;flex:auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-select:focus,.input-group>.form-floating:focus-within{z-index:5}.input-group .btn,.input-group div.drawio button,div.drawio .input-group button,.input-group .td-blog .td-rss-button,.td-blog .input-group .td-rss-button{position:relative;z-index:2}.input-group .btn:focus,.input-group div.drawio button:focus,div.drawio .input-group button:focus,.input-group .td-blog .td-rss-button:focus,.td-blog .input-group .td-rss-button:focus{z-index:5}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);text-align:center;white-space:nowrap;background-color:var(--bs-tertiary-bg);border:var(--bs-border-width)solid var(--bs-border-color);border-radius:var(--bs-border-radius)}.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text,.input-group-lg>.btn,div.drawio .input-group-lg>button,.td-blog .input-group-lg>.td-rss-button{padding:.5rem 1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text,.input-group-sm>.btn,div.drawio .input-group-sm>button,.td-blog .input-group-sm>.td-rss-button{padding:.25rem .5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating),.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-control,.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-select{border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating),.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-control,.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-select{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:calc(var(--bs-border-width) * -1);border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.form-floating:not(:first-child)>.form-control,.input-group>.form-floating:not(:first-child)>.form-select{border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--bs-form-valid-color)}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:var(--bs-success);border-radius:var(--bs-border-radius)}.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip,.is-valid~.valid-feedback,.is-valid~.valid-tooltip{display:block}.was-validated .form-control:valid,.form-control.is-valid{border-color:var(--bs-form-valid-border-color);padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%233772ff' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem)center;background-size:calc(.75em + .375rem)calc(.75em + .375rem)}.was-validated .form-control:valid:focus,.form-control.is-valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:var(--bs-box-shadow-inset),0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem)right calc(.375em + .1875rem)}.was-validated .form-select:valid,.form-select.is-valid{border-color:var(--bs-form-valid-border-color)}.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"],.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"]{--bs-form-select-bg-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%233772ff' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");padding-right:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem)calc(.75em + .375rem)}.was-validated .form-select:valid:focus,.form-select.is-valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:var(--bs-box-shadow-inset),0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.was-validated .form-control-color:valid,.form-control-color.is-valid{width:calc(3rem + calc(1.5em + .75rem))}.was-validated .form-check-input:valid,.form-check-input.is-valid{border-color:var(--bs-form-valid-border-color)}.was-validated .form-check-input:valid:checked,.form-check-input.is-valid:checked{background-color:var(--bs-form-valid-color)}.was-validated .form-check-input:valid:focus,.form-check-input.is-valid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.was-validated .form-check-input:valid~.form-check-label,.form-check-input.is-valid~.form-check-label{color:var(--bs-form-valid-color)}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.was-validated .input-group>.form-control:not(:focus):valid,.input-group>.form-control:not(:focus).is-valid,.was-validated .input-group>.form-select:not(:focus):valid,.input-group>.form-select:not(:focus).is-valid,.was-validated .input-group>.form-floating:not(:focus-within):valid,.input-group>.form-floating:not(:focus-within).is-valid{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--bs-form-invalid-color)}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:var(--bs-danger);border-radius:var(--bs-border-radius)}.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip,.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip{display:block}.was-validated .form-control:invalid,.form-control.is-invalid{border-color:var(--bs-form-invalid-border-color);padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23ed6a5a'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23ed6a5a' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem)center;background-size:calc(.75em + .375rem)calc(.75em + .375rem)}.was-validated .form-control:invalid:focus,.form-control.is-invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:var(--bs-box-shadow-inset),0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem)right calc(.375em + .1875rem)}.was-validated .form-select:invalid,.form-select.is-invalid{border-color:var(--bs-form-invalid-border-color)}.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"],.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"]{--bs-form-select-bg-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23ed6a5a'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23ed6a5a' stroke='none'/%3e%3c/svg%3e");padding-right:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem)calc(.75em + .375rem)}.was-validated .form-select:invalid:focus,.form-select.is-invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:var(--bs-box-shadow-inset),0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.was-validated .form-control-color:invalid,.form-control-color.is-invalid{width:calc(3rem + calc(1.5em + .75rem))}.was-validated .form-check-input:invalid,.form-check-input.is-invalid{border-color:var(--bs-form-invalid-border-color)}.was-validated .form-check-input:invalid:checked,.form-check-input.is-invalid:checked{background-color:var(--bs-form-invalid-color)}.was-validated .form-check-input:invalid:focus,.form-check-input.is-invalid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.was-validated .form-check-input:invalid~.form-check-label,.form-check-input.is-invalid~.form-check-label{color:var(--bs-form-invalid-color)}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.was-validated .input-group>.form-control:not(:focus):invalid,.input-group>.form-control:not(:focus).is-invalid,.was-validated .input-group>.form-select:not(:focus):invalid,.input-group>.form-select:not(:focus).is-invalid,.was-validated .input-group>.form-floating:not(:focus-within):invalid,.input-group>.form-floating:not(:focus-within).is-invalid{z-index:4}.btn,div.drawio button,.td-blog .td-rss-button{--bs-btn-padding-x:0.75rem;--bs-btn-padding-y:0.375rem;--bs-btn-font-family: ;--bs-btn-font-size:1rem;--bs-btn-font-weight:400;--bs-btn-line-height:1.5;--bs-btn-color:var(--bs-body-color);--bs-btn-bg:transparent;--bs-btn-border-width:var(--bs-border-width);--bs-btn-border-color:transparent;--bs-btn-border-radius:var(--bs-border-radius);--bs-btn-hover-border-color:transparent;--bs-btn-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);--bs-btn-disabled-opacity:0.65;--bs-btn-focus-box-shadow:0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5);display:inline-block;padding:var(--bs-btn-padding-y)var(--bs-btn-padding-x);font-family:var(--bs-btn-font-family);font-size:var(--bs-btn-font-size);font-weight:var(--bs-btn-font-weight);line-height:var(--bs-btn-line-height);color:var(--bs-btn-color);text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;border:var(--bs-btn-border-width)solid var(--bs-btn-border-color);border-radius:var(--bs-btn-border-radius);background-color:var(--bs-btn-bg);background-image:var(--bs-gradient);box-shadow:var(--bs-btn-box-shadow);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.btn,div.drawio button,.td-blog .td-rss-button{transition:none}}.btn:hover,div.drawio button:hover,.td-blog .td-rss-button:hover{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color)}.btn-check+.btn:hover,div.drawio .btn-check+button:hover,.td-blog .btn-check+.td-rss-button:hover{color:var(--bs-btn-color);background-color:var(--bs-btn-bg);border-color:var(--bs-btn-border-color)}.btn:focus-visible,div.drawio button:focus-visible,.td-blog .td-rss-button:focus-visible{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);background-image:var(--bs-gradient);border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-box-shadow),var(--bs-btn-focus-box-shadow)}.btn-check:focus-visible+.btn,div.drawio .btn-check:focus-visible+button,.td-blog .btn-check:focus-visible+.td-rss-button{border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-box-shadow),var(--bs-btn-focus-box-shadow)}.btn-check:checked+.btn,div.drawio .btn-check:checked+button,.td-blog .btn-check:checked+.td-rss-button,:not(.btn-check)+.btn:active,div.drawio :not(.btn-check)+button:active,.td-blog :not(.btn-check)+.td-rss-button:active,.btn:first-child:active,div.drawio button:first-child:active,.td-blog .td-rss-button:first-child:active,.btn.active,div.drawio button.active,.td-blog .active.td-rss-button,.btn.show,div.drawio button.show,.td-blog .show.td-rss-button{color:var(--bs-btn-active-color);background-color:var(--bs-btn-active-bg);background-image:none;border-color:var(--bs-btn-active-border-color);box-shadow:var(--bs-btn-active-shadow)}.btn-check:checked+.btn:focus-visible,div.drawio .btn-check:checked+button:focus-visible,.td-blog .btn-check:checked+.td-rss-button:focus-visible,:not(.btn-check)+.btn:active:focus-visible,div.drawio :not(.btn-check)+button:active:focus-visible,.td-blog :not(.btn-check)+.td-rss-button:active:focus-visible,.btn:first-child:active:focus-visible,div.drawio button:first-child:active:focus-visible,.td-blog .td-rss-button:first-child:active:focus-visible,.btn.active:focus-visible,div.drawio button.active:focus-visible,.td-blog .active.td-rss-button:focus-visible,.btn.show:focus-visible,div.drawio button.show:focus-visible,.td-blog .show.td-rss-button:focus-visible{box-shadow:var(--bs-btn-active-shadow),var(--bs-btn-focus-box-shadow)}.btn-check:checked:focus-visible+.btn,div.drawio .btn-check:checked:focus-visible+button,.td-blog .btn-check:checked:focus-visible+.td-rss-button{box-shadow:var(--bs-btn-active-shadow),var(--bs-btn-focus-box-shadow)}.btn:disabled,div.drawio button:disabled,.td-blog .td-rss-button:disabled,.btn.disabled,div.drawio button.disabled,.td-blog .disabled.td-rss-button,fieldset:disabled .btn,fieldset:disabled div.drawio button,div.drawio fieldset:disabled button,fieldset:disabled .td-blog .td-rss-button,.td-blog fieldset:disabled .td-rss-button{color:var(--bs-btn-disabled-color);pointer-events:none;background-color:var(--bs-btn-disabled-bg);background-image:none;border-color:var(--bs-btn-disabled-border-color);opacity:var(--bs-btn-disabled-opacity);box-shadow:none}.btn-primary{--bs-btn-color:#fff;--bs-btn-bg:#5f7681;--bs-btn-border-color:#5f7681;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#51646e;--bs-btn-hover-border-color:#4c5e67;--bs-btn-focus-shadow-rgb:119, 139, 148;--bs-btn-active-color:#fff;--bs-btn-active-bg:#4c5e67;--bs-btn-active-border-color:#475961;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#5f7681;--bs-btn-disabled-border-color:#5f7681}.btn-secondary{--bs-btn-color:#000;--bs-btn-bg:#F58301;--bs-btn-border-color:#F58301;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#f79627;--bs-btn-hover-border-color:#f68f1a;--bs-btn-focus-shadow-rgb:208, 111, 1;--bs-btn-active-color:#000;--bs-btn-active-bg:#f79c34;--bs-btn-active-border-color:#f68f1a;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#F58301;--bs-btn-disabled-border-color:#F58301}.btn-success{--bs-btn-color:#000;--bs-btn-bg:#3772ff;--bs-btn-border-color:#3772ff;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#5587ff;--bs-btn-hover-border-color:#4b80ff;--bs-btn-focus-shadow-rgb:47, 97, 217;--bs-btn-active-color:#000;--bs-btn-active-bg:#5f8eff;--bs-btn-active-border-color:#4b80ff;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#3772ff;--bs-btn-disabled-border-color:#3772ff}.btn-info,.td-blog .td-rss-button{--bs-btn-color:#fff;--bs-btn-bg:#007AC0;--bs-btn-border-color:#007AC0;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#0068a3;--bs-btn-hover-border-color:#00629a;--bs-btn-focus-shadow-rgb:38, 142, 201;--bs-btn-active-color:#fff;--bs-btn-active-bg:#00629a;--bs-btn-active-border-color:#005c90;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#007AC0;--bs-btn-disabled-border-color:#007AC0}.btn-warning{--bs-btn-color:#000;--bs-btn-bg:#E33030;--bs-btn-border-color:#E33030;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#e74f4f;--bs-btn-hover-border-color:#e64545;--bs-btn-focus-shadow-rgb:193, 41, 41;--bs-btn-active-color:#000;--bs-btn-active-bg:#e95959;--bs-btn-active-border-color:#e64545;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#E33030;--bs-btn-disabled-border-color:#E33030}.btn-danger{--bs-btn-color:#000;--bs-btn-bg:#ed6a5a;--bs-btn-border-color:#ed6a5a;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#f08073;--bs-btn-hover-border-color:#ef796b;--bs-btn-focus-shadow-rgb:201, 90, 77;--bs-btn-active-color:#000;--bs-btn-active-bg:#f1887b;--bs-btn-active-border-color:#ef796b;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#ed6a5a;--bs-btn-disabled-border-color:#ed6a5a}.btn-light{--bs-btn-color:#000;--bs-btn-bg:#d3f3ee;--bs-btn-border-color:#d3f3ee;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#b3cfca;--bs-btn-hover-border-color:#a9c2be;--bs-btn-focus-shadow-rgb:179, 207, 202;--bs-btn-active-color:#000;--bs-btn-active-bg:#a9c2be;--bs-btn-active-border-color:#9eb6b3;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#d3f3ee;--bs-btn-disabled-border-color:#d3f3ee}.btn-dark{--bs-btn-color:#fff;--bs-btn-bg:#333F47;--bs-btn-border-color:#333F47;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#525c63;--bs-btn-hover-border-color:#475259;--bs-btn-focus-shadow-rgb:82, 92, 99;--bs-btn-active-color:#fff;--bs-btn-active-bg:#5c656c;--bs-btn-active-border-color:#475259;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#333F47;--bs-btn-disabled-border-color:#333F47}.btn-outline-primary,div.drawio button{--bs-btn-color:#5f7681;--bs-btn-border-color:#5f7681;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#5f7681;--bs-btn-hover-border-color:#5f7681;--bs-btn-focus-shadow-rgb:95, 118, 129;--bs-btn-active-color:#fff;--bs-btn-active-bg:#5f7681;--bs-btn-active-border-color:#5f7681;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#5f7681;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#5f7681;--bs-gradient:none}.btn-outline-secondary{--bs-btn-color:#F58301;--bs-btn-border-color:#F58301;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#F58301;--bs-btn-hover-border-color:#F58301;--bs-btn-focus-shadow-rgb:245, 131, 1;--bs-btn-active-color:#000;--bs-btn-active-bg:#F58301;--bs-btn-active-border-color:#F58301;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#F58301;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#F58301;--bs-gradient:none}.btn-outline-success{--bs-btn-color:#3772ff;--bs-btn-border-color:#3772ff;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#3772ff;--bs-btn-hover-border-color:#3772ff;--bs-btn-focus-shadow-rgb:55, 114, 255;--bs-btn-active-color:#000;--bs-btn-active-bg:#3772ff;--bs-btn-active-border-color:#3772ff;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#3772ff;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#3772ff;--bs-gradient:none}.btn-outline-info{--bs-btn-color:#007AC0;--bs-btn-border-color:#007AC0;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#007AC0;--bs-btn-hover-border-color:#007AC0;--bs-btn-focus-shadow-rgb:0, 122, 192;--bs-btn-active-color:#fff;--bs-btn-active-bg:#007AC0;--bs-btn-active-border-color:#007AC0;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#007AC0;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#007AC0;--bs-gradient:none}.btn-outline-warning{--bs-btn-color:#E33030;--bs-btn-border-color:#E33030;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#E33030;--bs-btn-hover-border-color:#E33030;--bs-btn-focus-shadow-rgb:227, 48, 48;--bs-btn-active-color:#000;--bs-btn-active-bg:#E33030;--bs-btn-active-border-color:#E33030;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#E33030;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#E33030;--bs-gradient:none}.btn-outline-danger{--bs-btn-color:#ed6a5a;--bs-btn-border-color:#ed6a5a;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#ed6a5a;--bs-btn-hover-border-color:#ed6a5a;--bs-btn-focus-shadow-rgb:237, 106, 90;--bs-btn-active-color:#000;--bs-btn-active-bg:#ed6a5a;--bs-btn-active-border-color:#ed6a5a;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#ed6a5a;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#ed6a5a;--bs-gradient:none}.btn-outline-light{--bs-btn-color:#d3f3ee;--bs-btn-border-color:#d3f3ee;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#d3f3ee;--bs-btn-hover-border-color:#d3f3ee;--bs-btn-focus-shadow-rgb:211, 243, 238;--bs-btn-active-color:#000;--bs-btn-active-bg:#d3f3ee;--bs-btn-active-border-color:#d3f3ee;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#d3f3ee;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#d3f3ee;--bs-gradient:none}.btn-outline-dark{--bs-btn-color:#333F47;--bs-btn-border-color:#333F47;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#333F47;--bs-btn-hover-border-color:#333F47;--bs-btn-focus-shadow-rgb:51, 63, 71;--bs-btn-active-color:#fff;--bs-btn-active-bg:#333F47;--bs-btn-active-border-color:#333F47;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#333F47;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#333F47;--bs-gradient:none}.btn-link{--bs-btn-font-weight:400;--bs-btn-color:var(--bs-link-color);--bs-btn-bg:transparent;--bs-btn-border-color:transparent;--bs-btn-hover-color:var(--bs-link-hover-color);--bs-btn-hover-border-color:transparent;--bs-btn-active-color:var(--bs-link-hover-color);--bs-btn-active-border-color:transparent;--bs-btn-disabled-color:#6c757d;--bs-btn-disabled-border-color:transparent;--bs-btn-box-shadow:0 0 0 #000;--bs-btn-focus-shadow-rgb:49, 132, 253;text-decoration:underline;background-image:none}.btn-link:focus-visible{color:var(--bs-btn-color)}.btn-link:hover{color:var(--bs-btn-hover-color)}.btn-lg,.td-blog .td-rss-button,.btn-group-lg>.btn,div.drawio .btn-group-lg>button{--bs-btn-padding-y:0.5rem;--bs-btn-padding-x:1rem;--bs-btn-font-size:1.25rem;--bs-btn-border-radius:var(--bs-border-radius-lg)}.btn-sm,.btn-group-sm>.btn,div.drawio .btn-group-sm>button,.td-blog .btn-group-sm>.td-rss-button{--bs-btn-padding-y:0.25rem;--bs-btn-padding-x:0.5rem;--bs-btn-font-size:0.875rem;--bs-btn-border-radius:var(--bs-border-radius-sm)}.fade{transition:opacity .15s linear}@media(prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media(prefers-reduced-motion:reduce){.collapsing{transition:none}}.collapsing.collapse-horizontal{width:0;height:auto;transition:width .35s ease}@media(prefers-reduced-motion:reduce){.collapsing.collapse-horizontal{transition:none}}.dropup,.dropend,.dropdown,.dropstart,.dropup-center,.dropdown-center{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{--bs-dropdown-zindex:1000;--bs-dropdown-min-width:10rem;--bs-dropdown-padding-x:0;--bs-dropdown-padding-y:0.5rem;--bs-dropdown-spacer:0.125rem;--bs-dropdown-font-size:1rem;--bs-dropdown-color:var(--bs-body-color);--bs-dropdown-bg:var(--bs-body-bg);--bs-dropdown-border-color:var(--bs-border-color-translucent);--bs-dropdown-border-radius:var(--bs-border-radius);--bs-dropdown-border-width:var(--bs-border-width);--bs-dropdown-inner-border-radius:calc(var(--bs-border-radius) - var(--bs-border-width));--bs-dropdown-divider-bg:var(--bs-border-color-translucent);--bs-dropdown-divider-margin-y:0.5rem;--bs-dropdown-box-shadow:var(--bs-box-shadow);--bs-dropdown-link-color:var(--bs-body-color);--bs-dropdown-link-hover-color:var(--bs-body-color);--bs-dropdown-link-hover-bg:var(--bs-tertiary-bg);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#5f7681;--bs-dropdown-link-disabled-color:var(--bs-tertiary-color);--bs-dropdown-item-padding-x:1rem;--bs-dropdown-item-padding-y:0.25rem;--bs-dropdown-header-color:#6c757d;--bs-dropdown-header-padding-x:1rem;--bs-dropdown-header-padding-y:0.5rem;position:absolute;z-index:var(--bs-dropdown-zindex);display:none;min-width:var(--bs-dropdown-min-width);padding:var(--bs-dropdown-padding-y)var(--bs-dropdown-padding-x);margin:0;font-size:var(--bs-dropdown-font-size);color:var(--bs-dropdown-color);text-align:left;list-style:none;background-color:var(--bs-dropdown-bg);background-clip:padding-box;border:var(--bs-dropdown-border-width)solid var(--bs-dropdown-border-color);border-radius:var(--bs-dropdown-border-radius);box-shadow:var(--bs-dropdown-box-shadow)}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:var(--bs-dropdown-spacer)}.dropdown-menu-start{--bs-position:start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position:end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media(min-width:576px){.dropdown-menu-sm-start{--bs-position:start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position:end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media(min-width:768px){.dropdown-menu-md-start{--bs-position:start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position:end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media(min-width:992px){.dropdown-menu-lg-start{--bs-position:start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position:end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media(min-width:1200px){.dropdown-menu-xl-start{--bs-position:start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position:end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media(min-width:1400px){.dropdown-menu-xxl-start{--bs-position:start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position:end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:var(--bs-dropdown-spacer)}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:var(--bs-dropdown-spacer)}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropend .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-toggle::after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:var(--bs-dropdown-spacer)}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropstart .dropdown-toggle::after{display:none}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:var(--bs-dropdown-divider-margin-y)0;overflow:hidden;border-top:1px solid var(--bs-dropdown-divider-bg);opacity:1}.dropdown-item{display:block;width:100%;padding:var(--bs-dropdown-item-padding-y)var(--bs-dropdown-item-padding-x);clear:both;font-weight:400;color:var(--bs-dropdown-link-color);text-align:inherit;text-decoration:none;white-space:nowrap;background-color:transparent;border:0;border-radius:var(--bs-dropdown-item-border-radius,0)}.dropdown-item:hover,.dropdown-item:focus{color:var(--bs-dropdown-link-hover-color);background-color:var(--bs-dropdown-link-hover-bg);background-image:var(--bs-gradient)}.dropdown-item.active,.dropdown-item:active{color:var(--bs-dropdown-link-active-color);text-decoration:none;background-color:var(--bs-dropdown-link-active-bg);background-image:var(--bs-gradient)}.dropdown-item.disabled,.dropdown-item:disabled{color:var(--bs-dropdown-link-disabled-color);pointer-events:none;background-color:transparent;background-image:none}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:var(--bs-dropdown-header-padding-y)var(--bs-dropdown-header-padding-x);margin-bottom:0;font-size:.875rem;color:var(--bs-dropdown-header-color);white-space:nowrap}.dropdown-item-text{display:block;padding:var(--bs-dropdown-item-padding-y)var(--bs-dropdown-item-padding-x);color:var(--bs-dropdown-link-color)}.dropdown-menu-dark{--bs-dropdown-color:#dee2e6;--bs-dropdown-bg:#343a40;--bs-dropdown-border-color:var(--bs-border-color-translucent);--bs-dropdown-box-shadow: ;--bs-dropdown-link-color:#dee2e6;--bs-dropdown-link-hover-color:#fff;--bs-dropdown-divider-bg:var(--bs-border-color-translucent);--bs-dropdown-link-hover-bg:rgba(255, 255, 255, 0.15);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#5f7681;--bs-dropdown-link-disabled-color:#adb5bd;--bs-dropdown-header-color:#adb5bd}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group>.btn,div.drawio .btn-group>button,.td-blog .btn-group>.td-rss-button,.btn-group-vertical>.btn,div.drawio .btn-group-vertical>button,.td-blog .btn-group-vertical>.td-rss-button{position:relative;flex:auto}.btn-group>.btn-check:checked+.btn,div.drawio .btn-group>.btn-check:checked+button,.td-blog .btn-group>.btn-check:checked+.td-rss-button,.btn-group>.btn-check:focus+.btn,div.drawio .btn-group>.btn-check:focus+button,.td-blog .btn-group>.btn-check:focus+.td-rss-button,.btn-group>.btn:hover,div.drawio .btn-group>button:hover,.td-blog .btn-group>.td-rss-button:hover,.btn-group>.btn:focus,div.drawio .btn-group>button:focus,.td-blog .btn-group>.td-rss-button:focus,.btn-group>.btn:active,div.drawio .btn-group>button:active,.td-blog .btn-group>.td-rss-button:active,.btn-group>.btn.active,div.drawio .btn-group>button.active,.td-blog .btn-group>.active.td-rss-button,.btn-group-vertical>.btn-check:checked+.btn,div.drawio .btn-group-vertical>.btn-check:checked+button,.td-blog .btn-group-vertical>.btn-check:checked+.td-rss-button,.btn-group-vertical>.btn-check:focus+.btn,div.drawio .btn-group-vertical>.btn-check:focus+button,.td-blog .btn-group-vertical>.btn-check:focus+.td-rss-button,.btn-group-vertical>.btn:hover,div.drawio .btn-group-vertical>button:hover,.td-blog .btn-group-vertical>.td-rss-button:hover,.btn-group-vertical>.btn:focus,div.drawio .btn-group-vertical>button:focus,.td-blog .btn-group-vertical>.td-rss-button:focus,.btn-group-vertical>.btn:active,div.drawio .btn-group-vertical>button:active,.td-blog .btn-group-vertical>.td-rss-button:active,.btn-group-vertical>.btn.active,div.drawio .btn-group-vertical>button.active,.td-blog .btn-group-vertical>.active.td-rss-button{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group{border-radius:var(--bs-border-radius)}.btn-group>:not(.btn-check:first-child)+.btn,div.drawio .btn-group>:not(.btn-check:first-child)+button,.td-blog .btn-group>:not(.btn-check:first-child)+.td-rss-button,.btn-group>.btn-group:not(:first-child){margin-left:calc(var(--bs-border-width) * -1)}.btn-group>.btn:not(:last-child):not(.dropdown-toggle),div.drawio .btn-group>button:not(:last-child):not(.dropdown-toggle),.td-blog .btn-group>.td-rss-button:not(:last-child):not(.dropdown-toggle),.btn-group>.btn.dropdown-toggle-split:first-child,div.drawio .btn-group>button.dropdown-toggle-split:first-child,.td-blog .btn-group>.dropdown-toggle-split.td-rss-button:first-child,.btn-group>.btn-group:not(:last-child)>.btn,div.drawio .btn-group>.btn-group:not(:last-child)>button,.td-blog .btn-group>.btn-group:not(:last-child)>.td-rss-button{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:nth-child(n+3),div.drawio .btn-group>button:nth-child(n+3),.td-blog .btn-group>.td-rss-button:nth-child(n+3),.btn-group>:not(.btn-check)+.btn,div.drawio .btn-group>:not(.btn-check)+button,.td-blog .btn-group>:not(.btn-check)+.td-rss-button,.btn-group>.btn-group:not(:first-child)>.btn,div.drawio .btn-group>.btn-group:not(:first-child)>button,.td-blog .btn-group>.btn-group:not(:first-child)>.td-rss-button{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after{margin-left:0}.dropstart .dropdown-toggle-split::before{margin-right:0}.btn-sm+.dropdown-toggle-split,.btn-group-sm>.btn+.dropdown-toggle-split,div.drawio .btn-group-sm>button+.dropdown-toggle-split,.td-blog .btn-group-sm>.td-rss-button+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-lg+.dropdown-toggle-split,.td-blog .td-rss-button+.dropdown-toggle-split,.btn-group-lg>.btn+.dropdown-toggle-split,div.drawio .btn-group-lg>button+.dropdown-toggle-split,.td-blog .btn-group-lg>.td-rss-button+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group.show .dropdown-toggle{box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.show .dropdown-toggle.btn-link{box-shadow:none}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,div.drawio .btn-group-vertical>button,.td-blog .btn-group-vertical>.td-rss-button,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn:not(:first-child),div.drawio .btn-group-vertical>button:not(:first-child),.td-blog .btn-group-vertical>.td-rss-button:not(:first-child),.btn-group-vertical>.btn-group:not(:first-child){margin-top:calc(var(--bs-border-width) * -1)}.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle),div.drawio .btn-group-vertical>button:not(:last-child):not(.dropdown-toggle),.td-blog .btn-group-vertical>.td-rss-button:not(:last-child):not(.dropdown-toggle),.btn-group-vertical>.btn-group:not(:last-child)>.btn,div.drawio .btn-group-vertical>.btn-group:not(:last-child)>button,.td-blog .btn-group-vertical>.btn-group:not(:last-child)>.td-rss-button{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn~.btn,div.drawio .btn-group-vertical>button~.btn,div.drawio .btn-group-vertical>.btn~button,div.drawio .btn-group-vertical>button~button,.td-blog .btn-group-vertical>.td-rss-button~.btn,.td-blog div.drawio .btn-group-vertical>.td-rss-button~button,div.drawio .td-blog .btn-group-vertical>.td-rss-button~button,.td-blog .btn-group-vertical>.btn~.td-rss-button,.td-blog div.drawio .btn-group-vertical>button~.td-rss-button,div.drawio .td-blog .btn-group-vertical>button~.td-rss-button,.td-blog .btn-group-vertical>.td-rss-button~.td-rss-button,.btn-group-vertical>.btn-group:not(:first-child)>.btn,div.drawio .btn-group-vertical>.btn-group:not(:first-child)>button,.td-blog .btn-group-vertical>.btn-group:not(:first-child)>.td-rss-button{border-top-left-radius:0;border-top-right-radius:0}.nav{--bs-nav-link-padding-x:1rem;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-link-color);--bs-nav-link-hover-color:var(--bs-link-hover-color);--bs-nav-link-disabled-color:var(--bs-secondary-color);display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:var(--bs-nav-link-padding-y)var(--bs-nav-link-padding-x);font-size:var(--bs-nav-link-font-size);font-weight:var(--bs-nav-link-font-weight);color:var(--bs-nav-link-color);text-decoration:none;background:0 0;border:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}@media(prefers-reduced-motion:reduce){.nav-link{transition:none}}.nav-link:hover,.nav-link:focus{color:var(--bs-nav-link-hover-color)}.nav-link:focus-visible{outline:0;box-shadow:0 0 0 .25rem rgba(95,118,129,.25)}.nav-link.disabled,.nav-link:disabled{color:var(--bs-nav-link-disabled-color);pointer-events:none;cursor:default}.nav-tabs{--bs-nav-tabs-border-width:var(--bs-border-width);--bs-nav-tabs-border-color:var(--bs-border-color);--bs-nav-tabs-border-radius:var(--bs-border-radius);--bs-nav-tabs-link-hover-border-color:var(--bs-secondary-bg) var(--bs-secondary-bg) var(--bs-border-color);--bs-nav-tabs-link-active-color:var(--bs-emphasis-color);--bs-nav-tabs-link-active-bg:var(--bs-body-bg);--bs-nav-tabs-link-active-border-color:var(--bs-border-color) var(--bs-border-color) var(--bs-body-bg);border-bottom:var(--bs-nav-tabs-border-width)solid var(--bs-nav-tabs-border-color)}.nav-tabs .nav-link{margin-bottom:calc(-1 * var(--bs-nav-tabs-border-width));border:var(--bs-nav-tabs-border-width)solid transparent;border-top-left-radius:var(--bs-nav-tabs-border-radius);border-top-right-radius:var(--bs-nav-tabs-border-radius)}.nav-tabs .nav-link:hover,.nav-tabs .nav-link:focus{isolation:isolate;border-color:var(--bs-nav-tabs-link-hover-border-color)}.nav-tabs .nav-link.active,.nav-tabs .nav-item.show .nav-link{color:var(--bs-nav-tabs-link-active-color);background-color:var(--bs-nav-tabs-link-active-bg);border-color:var(--bs-nav-tabs-link-active-border-color)}.nav-tabs .dropdown-menu{margin-top:calc(-1 * var(--bs-nav-tabs-border-width));border-top-left-radius:0;border-top-right-radius:0}.nav-pills{--bs-nav-pills-border-radius:var(--bs-border-radius);--bs-nav-pills-link-active-color:#fff;--bs-nav-pills-link-active-bg:#5f7681}.nav-pills .nav-link{border-radius:var(--bs-nav-pills-border-radius)}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:var(--bs-nav-pills-link-active-color);background-color:var(--bs-nav-pills-link-active-bg);background-image:var(--bs-gradient)}.nav-underline{--bs-nav-underline-gap:1rem;--bs-nav-underline-border-width:0.125rem;--bs-nav-underline-link-active-color:var(--bs-emphasis-color);gap:var(--bs-nav-underline-gap)}.nav-underline .nav-link{padding-right:0;padding-left:0;border-bottom:var(--bs-nav-underline-border-width)solid transparent}.nav-underline .nav-link:hover,.nav-underline .nav-link:focus{border-bottom-color:initial}.nav-underline .nav-link.active,.nav-underline .show>.nav-link{font-weight:700;color:var(--bs-nav-underline-link-active-color);border-bottom-color:initial}.nav-fill>.nav-link,.nav-fill .nav-item{flex:auto;text-align:center}.nav-justified>.nav-link,.nav-justified .nav-item{flex-basis:0;flex-grow:1;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar,.td-navbar{--bs-navbar-padding-x:0;--bs-navbar-padding-y:0.5rem;--bs-navbar-color:rgba(var(--bs-emphasis-color-rgb), 0.65);--bs-navbar-hover-color:rgba(var(--bs-emphasis-color-rgb), 0.8);--bs-navbar-disabled-color:rgba(var(--bs-emphasis-color-rgb), 0.3);--bs-navbar-active-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-brand-padding-y:0.3125rem;--bs-navbar-brand-margin-end:1rem;--bs-navbar-brand-font-size:1.25rem;--bs-navbar-brand-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-brand-hover-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-nav-link-padding-x:0.5rem;--bs-navbar-toggler-padding-y:0.25rem;--bs-navbar-toggler-padding-x:0.75rem;--bs-navbar-toggler-font-size:1.25rem;--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2833, 37, 41, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");--bs-navbar-toggler-border-color:rgba(var(--bs-emphasis-color-rgb), 0.15);--bs-navbar-toggler-border-radius:var(--bs-border-radius);--bs-navbar-toggler-focus-width:0.25rem;--bs-navbar-toggler-transition:box-shadow 0.15s ease-in-out;position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding:var(--bs-navbar-padding-y)var(--bs-navbar-padding-x);background-image:var(--bs-gradient)}.navbar>.container,.td-navbar>.container,.navbar>.container-fluid,.td-navbar>.container-fluid,.navbar>.container-sm,.td-navbar>.container-sm,.navbar>.container-md,.td-navbar>.container-md,.navbar>.container-lg,.td-navbar>.container-lg,.navbar>.container-xl,.td-navbar>.container-xl,.navbar>.container-xxl,.td-navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:var(--bs-navbar-brand-padding-y);padding-bottom:var(--bs-navbar-brand-padding-y);margin-right:var(--bs-navbar-brand-margin-end);font-size:var(--bs-navbar-brand-font-size);color:var(--bs-navbar-brand-color);text-decoration:none;white-space:nowrap}.navbar-brand:hover,.navbar-brand:focus{color:var(--bs-navbar-brand-hover-color)}.navbar-nav{--bs-nav-link-padding-x:0;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-navbar-color);--bs-nav-link-hover-color:var(--bs-navbar-hover-color);--bs-nav-link-disabled-color:var(--bs-navbar-disabled-color);display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link.active,.navbar-nav .nav-link.show{color:var(--bs-navbar-active-color)}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-navbar-color)}.navbar-text a,.navbar-text a:hover,.navbar-text a:focus{color:var(--bs-navbar-active-color)}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:var(--bs-navbar-toggler-padding-y)var(--bs-navbar-toggler-padding-x);font-size:var(--bs-navbar-toggler-font-size);line-height:1;color:var(--bs-navbar-color);background-color:transparent;border:var(--bs-border-width)solid var(--bs-navbar-toggler-border-color);border-radius:var(--bs-navbar-toggler-border-radius);transition:var(--bs-navbar-toggler-transition)}@media(prefers-reduced-motion:reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 var(--bs-navbar-toggler-focus-width)}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-image:var(--bs-navbar-toggler-icon-bg);background-repeat:no-repeat;background-position:50%;background-size:100%}.navbar-nav-scroll{max-height:var(--bs-scroll-height,75vh);overflow-y:auto}@media(min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}.navbar-expand-sm .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;box-shadow:none;transition:none}.navbar-expand-sm .offcanvas .offcanvas-header{display:none}.navbar-expand-sm .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}.navbar-expand-md .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;box-shadow:none;transition:none}.navbar-expand-md .offcanvas .offcanvas-header{display:none}.navbar-expand-md .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}.navbar-expand-lg .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;box-shadow:none;transition:none}.navbar-expand-lg .offcanvas .offcanvas-header{display:none}.navbar-expand-lg .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}.navbar-expand-xl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;box-shadow:none;transition:none}.navbar-expand-xl .offcanvas .offcanvas-header{display:none}.navbar-expand-xl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}.navbar-expand-xxl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;box-shadow:none;transition:none}.navbar-expand-xxl .offcanvas .offcanvas-header{display:none}.navbar-expand-xxl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}.navbar-expand,.td-navbar{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav,.td-navbar .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu,.td-navbar .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link,.td-navbar .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand .navbar-nav-scroll,.td-navbar .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse,.td-navbar .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler,.td-navbar .navbar-toggler{display:none}.navbar-expand .offcanvas,.td-navbar .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;box-shadow:none;transition:none}.navbar-expand .offcanvas .offcanvas-header,.td-navbar .offcanvas .offcanvas-header{display:none}.navbar-expand .offcanvas .offcanvas-body,.td-navbar .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}.navbar-dark,.navbar[data-bs-theme=dark],[data-bs-theme=dark].td-navbar{--bs-navbar-color:rgba(255, 255, 255, 0.55);--bs-navbar-hover-color:rgba(255, 255, 255, 0.75);--bs-navbar-disabled-color:rgba(255, 255, 255, 0.25);--bs-navbar-active-color:#fff;--bs-navbar-brand-color:#fff;--bs-navbar-brand-hover-color:#fff;--bs-navbar-toggler-border-color:rgba(255, 255, 255, 0.1);--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}[data-bs-theme=dark] .navbar-toggler-icon{--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.card{--bs-card-spacer-y:1rem;--bs-card-spacer-x:1rem;--bs-card-title-spacer-y:0.5rem;--bs-card-title-color: ;--bs-card-subtitle-color: ;--bs-card-border-width:var(--bs-border-width);--bs-card-border-color:var(--bs-border-color-translucent);--bs-card-border-radius:var(--bs-border-radius);--bs-card-box-shadow: ;--bs-card-inner-border-radius:calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-card-cap-padding-y:0.5rem;--bs-card-cap-padding-x:1rem;--bs-card-cap-bg:rgba(var(--bs-body-color-rgb), 0.03);--bs-card-cap-color: ;--bs-card-height: ;--bs-card-color: ;--bs-card-bg:var(--bs-body-bg);--bs-card-img-overlay-padding:1rem;--bs-card-group-margin:0.75rem;position:relative;display:flex;flex-direction:column;min-width:0;height:var(--bs-card-height);color:var(--bs-body-color);word-wrap:break-word;background-color:var(--bs-card-bg);background-clip:border-box;border:var(--bs-card-border-width)solid var(--bs-card-border-color);border-radius:var(--bs-card-border-radius);box-shadow:var(--bs-card-box-shadow)}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:auto;padding:var(--bs-card-spacer-y)var(--bs-card-spacer-x);color:var(--bs-card-color)}.card-title{margin-bottom:var(--bs-card-title-spacer-y);color:var(--bs-card-title-color)}.card-subtitle{margin-top:calc(-.5 * var(--bs-card-title-spacer-y));margin-bottom:0;color:var(--bs-card-subtitle-color)}.card-text:last-child{margin-bottom:0}.card-link+.card-link{margin-left:var(--bs-card-spacer-x)}.card-header{padding:var(--bs-card-cap-padding-y)var(--bs-card-cap-padding-x);margin-bottom:0;color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-bottom:var(--bs-card-border-width)solid var(--bs-card-border-color)}.card-header:first-child{border-radius:var(--bs-card-inner-border-radius)var(--bs-card-inner-border-radius)0 0}.card-footer{padding:var(--bs-card-cap-padding-y)var(--bs-card-cap-padding-x);color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-top:var(--bs-card-border-width)solid var(--bs-card-border-color)}.card-footer:last-child{border-radius:0 0 var(--bs-card-inner-border-radius)var(--bs-card-inner-border-radius)}.card-header-tabs{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-bottom:calc(-1 * var(--bs-card-cap-padding-y));margin-left:calc(-.5 * var(--bs-card-cap-padding-x));border-bottom:0}.card-header-tabs .nav-link.active{background-color:var(--bs-card-bg);border-bottom-color:var(--bs-card-bg)}.card-header-pills{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-left:calc(-.5 * var(--bs-card-cap-padding-x))}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:var(--bs-card-img-overlay-padding);border-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-top,.card-img-bottom{width:100%}.card-img,.card-img-top{border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom{border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card-group>.card{margin-bottom:var(--bs-card-group-margin)}@media(min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-img-top,.card-group>.card:not(:last-child) .card-header{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-img-bottom,.card-group>.card:not(:last-child) .card-footer{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-img-top,.card-group>.card:not(:first-child) .card-header{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-img-bottom,.card-group>.card:not(:first-child) .card-footer{border-bottom-left-radius:0}}.accordion{--bs-accordion-color:var(--bs-body-color);--bs-accordion-bg:var(--bs-body-bg);--bs-accordion-transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, border-radius 0.15s ease;--bs-accordion-border-color:var(--bs-border-color);--bs-accordion-border-width:var(--bs-border-width);--bs-accordion-border-radius:var(--bs-border-radius);--bs-accordion-inner-border-radius:calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-accordion-btn-padding-x:1.25rem;--bs-accordion-btn-padding-y:1rem;--bs-accordion-btn-color:var(--bs-body-color);--bs-accordion-btn-bg:var(--bs-accordion-bg);--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23212529' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e");--bs-accordion-btn-icon-width:1.25rem;--bs-accordion-btn-icon-transform:rotate(-180deg);--bs-accordion-btn-icon-transition:transform 0.2s ease-in-out;--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23262f34' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e");--bs-accordion-btn-focus-box-shadow:0 0 0 0.25rem rgba(95, 118, 129, 0.25);--bs-accordion-body-padding-x:1.25rem;--bs-accordion-body-padding-y:1rem;--bs-accordion-active-color:var(--bs-primary-text-emphasis);--bs-accordion-active-bg:var(--bs-primary-bg-subtle)}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:var(--bs-accordion-btn-padding-y)var(--bs-accordion-btn-padding-x);font-size:1rem;color:var(--bs-accordion-btn-color);text-align:left;background-color:var(--bs-accordion-btn-bg);border:0;border-radius:0;overflow-anchor:none;transition:var(--bs-accordion-transition)}@media(prefers-reduced-motion:reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:var(--bs-accordion-active-color);background-color:var(--bs-accordion-active-bg);box-shadow:inset 0 calc(-1 * var(--bs-accordion-border-width))0 var(--bs-accordion-border-color)}.accordion-button:not(.collapsed)::after{background-image:var(--bs-accordion-btn-active-icon);transform:var(--bs-accordion-btn-icon-transform)}.accordion-button::after{flex-shrink:0;width:var(--bs-accordion-btn-icon-width);height:var(--bs-accordion-btn-icon-width);margin-left:auto;content:"";background-image:var(--bs-accordion-btn-icon);background-repeat:no-repeat;background-size:var(--bs-accordion-btn-icon-width);transition:var(--bs-accordion-btn-icon-transition)}@media(prefers-reduced-motion:reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;outline:0;box-shadow:var(--bs-accordion-btn-focus-box-shadow)}.accordion-header{margin-bottom:0}.accordion-item{color:var(--bs-accordion-color);background-color:var(--bs-accordion-bg);border:var(--bs-accordion-border-width)solid var(--bs-accordion-border-color)}.accordion-item:first-of-type{border-top-left-radius:var(--bs-accordion-border-radius);border-top-right-radius:var(--bs-accordion-border-radius)}.accordion-item:first-of-type>.accordion-header .accordion-button{border-top-left-radius:var(--bs-accordion-inner-border-radius);border-top-right-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-item:last-of-type>.accordion-header .accordion-button.collapsed{border-bottom-right-radius:var(--bs-accordion-inner-border-radius);border-bottom-left-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:last-of-type>.accordion-collapse{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-body{padding:var(--bs-accordion-body-padding-y)var(--bs-accordion-body-padding-x)}.accordion-flush>.accordion-item{border-right:0;border-left:0;border-radius:0}.accordion-flush>.accordion-item:first-child{border-top:0}.accordion-flush>.accordion-item:last-child{border-bottom:0}.accordion-flush>.accordion-item>.accordion-header .accordion-button,.accordion-flush>.accordion-item>.accordion-header .accordion-button.collapsed{border-radius:0}.accordion-flush>.accordion-item>.accordion-collapse{border-radius:0}[data-bs-theme=dark] .accordion-button::after{--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%239fadb3'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%239fadb3'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.breadcrumb{--bs-breadcrumb-padding-x:0;--bs-breadcrumb-padding-y:0;--bs-breadcrumb-margin-bottom:1rem;--bs-breadcrumb-bg: ;--bs-breadcrumb-border-radius: ;--bs-breadcrumb-divider-color:var(--bs-secondary-color);--bs-breadcrumb-item-padding-x:0.5rem;--bs-breadcrumb-item-active-color:var(--bs-secondary-color);display:flex;flex-wrap:wrap;padding:var(--bs-breadcrumb-padding-y)var(--bs-breadcrumb-padding-x);margin-bottom:var(--bs-breadcrumb-margin-bottom);font-size:var(--bs-breadcrumb-font-size);list-style:none;background-color:var(--bs-breadcrumb-bg);border-radius:var(--bs-breadcrumb-border-radius)}.breadcrumb-item+.breadcrumb-item{padding-left:var(--bs-breadcrumb-item-padding-x)}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:var(--bs-breadcrumb-item-padding-x);color:var(--bs-breadcrumb-divider-color);content:var(--bs-breadcrumb-divider,"/")}.breadcrumb-item.active{color:var(--bs-breadcrumb-item-active-color)}.pagination{--bs-pagination-padding-x:0.75rem;--bs-pagination-padding-y:0.375rem;--bs-pagination-font-size:1rem;--bs-pagination-color:#6c757d;--bs-pagination-bg:var(--bs-body-bg);--bs-pagination-border-width:var(--bs-border-width);--bs-pagination-border-color:var(--bs-border-color);--bs-pagination-border-radius:var(--bs-border-radius);--bs-pagination-hover-color:var(--bs-link-hover-color);--bs-pagination-hover-bg:var(--bs-tertiary-bg);--bs-pagination-hover-border-color:var(--bs-border-color);--bs-pagination-focus-color:var(--bs-link-hover-color);--bs-pagination-focus-bg:var(--bs-secondary-bg);--bs-pagination-focus-box-shadow:0 0 0 0.25rem rgba(95, 118, 129, 0.25);--bs-pagination-active-color:#fff;--bs-pagination-active-bg:#5f7681;--bs-pagination-active-border-color:#5f7681;--bs-pagination-disabled-color:#dee2e6;--bs-pagination-disabled-bg:var(--bs-secondary-bg);--bs-pagination-disabled-border-color:var(--bs-border-color);display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;padding:var(--bs-pagination-padding-y)var(--bs-pagination-padding-x);font-size:var(--bs-pagination-font-size);color:var(--bs-pagination-color);text-decoration:none;background-color:var(--bs-pagination-bg);border:var(--bs-pagination-border-width)solid var(--bs-pagination-border-color);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:var(--bs-pagination-hover-color);background-color:var(--bs-pagination-hover-bg);border-color:var(--bs-pagination-hover-border-color)}.page-link:focus{z-index:3;color:var(--bs-pagination-focus-color);background-color:var(--bs-pagination-focus-bg);outline:0;box-shadow:var(--bs-pagination-focus-box-shadow)}.page-link.active,.active>.page-link{z-index:3;color:var(--bs-pagination-active-color);background-color:var(--bs-pagination-active-bg);background-image:var(--bs-gradient);border-color:var(--bs-pagination-active-border-color)}.page-link.disabled,.disabled>.page-link{color:var(--bs-pagination-disabled-color);pointer-events:none;background-color:var(--bs-pagination-disabled-bg);border-color:var(--bs-pagination-disabled-border-color)}.page-item:not(:first-child) .page-link{margin-left:calc(var(--bs-border-width) * -1)}.page-item:first-child .page-link{border-top-left-radius:var(--bs-pagination-border-radius);border-bottom-left-radius:var(--bs-pagination-border-radius)}.page-item:last-child .page-link{border-top-right-radius:var(--bs-pagination-border-radius);border-bottom-right-radius:var(--bs-pagination-border-radius)}.pagination-lg{--bs-pagination-padding-x:1.5rem;--bs-pagination-padding-y:0.75rem;--bs-pagination-font-size:1.25rem;--bs-pagination-border-radius:var(--bs-border-radius-lg)}.pagination-sm{--bs-pagination-padding-x:0.5rem;--bs-pagination-padding-y:0.25rem;--bs-pagination-font-size:0.875rem;--bs-pagination-border-radius:var(--bs-border-radius-sm)}.badge{--bs-badge-padding-x:0.65em;--bs-badge-padding-y:0.35em;--bs-badge-font-size:0.75em;--bs-badge-font-weight:700;--bs-badge-color:#fff;--bs-badge-border-radius:var(--bs-border-radius);display:inline-block;padding:var(--bs-badge-padding-y)var(--bs-badge-padding-x);font-size:var(--bs-badge-font-size);font-weight:var(--bs-badge-font-weight);line-height:1;color:var(--bs-badge-color);text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:var(--bs-badge-border-radius);background-image:var(--bs-gradient)}.badge:empty{display:none}.btn .badge,div.drawio button .badge,.td-blog .td-rss-button .badge{position:relative;top:-1px}.alert{--bs-alert-bg:transparent;--bs-alert-padding-x:1rem;--bs-alert-padding-y:1rem;--bs-alert-margin-bottom:1rem;--bs-alert-color:inherit;--bs-alert-border-color:transparent;--bs-alert-border:var(--bs-border-width) solid var(--bs-alert-border-color);--bs-alert-border-radius:var(--bs-border-radius);--bs-alert-link-color:inherit;position:relative;padding:var(--bs-alert-padding-y)var(--bs-alert-padding-x);margin-bottom:var(--bs-alert-margin-bottom);color:var(--bs-alert-color);background-color:var(--bs-alert-bg);border:var(--bs-alert-border);border-radius:var(--bs-alert-border-radius)}.alert-heading{color:inherit}.alert-link{font-weight:700;color:var(--bs-alert-link-color)}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem}.alert-primary{--bs-alert-color:var(--bs-primary-text-emphasis);--bs-alert-bg:var(--bs-primary-bg-subtle);--bs-alert-border-color:var(--bs-primary-border-subtle);--bs-alert-link-color:var(--bs-primary-text-emphasis)}.alert-secondary{--bs-alert-color:var(--bs-secondary-text-emphasis);--bs-alert-bg:var(--bs-secondary-bg-subtle);--bs-alert-border-color:var(--bs-secondary-border-subtle);--bs-alert-link-color:var(--bs-secondary-text-emphasis)}.alert-success{--bs-alert-color:var(--bs-success-text-emphasis);--bs-alert-bg:var(--bs-success-bg-subtle);--bs-alert-border-color:var(--bs-success-border-subtle);--bs-alert-link-color:var(--bs-success-text-emphasis)}.alert-info{--bs-alert-color:var(--bs-info-text-emphasis);--bs-alert-bg:var(--bs-info-bg-subtle);--bs-alert-border-color:var(--bs-info-border-subtle);--bs-alert-link-color:var(--bs-info-text-emphasis)}.alert-warning{--bs-alert-color:var(--bs-warning-text-emphasis);--bs-alert-bg:var(--bs-warning-bg-subtle);--bs-alert-border-color:var(--bs-warning-border-subtle);--bs-alert-link-color:var(--bs-warning-text-emphasis)}.alert-danger{--bs-alert-color:var(--bs-danger-text-emphasis);--bs-alert-bg:var(--bs-danger-bg-subtle);--bs-alert-border-color:var(--bs-danger-border-subtle);--bs-alert-link-color:var(--bs-danger-text-emphasis)}.alert-light{--bs-alert-color:var(--bs-light-text-emphasis);--bs-alert-bg:var(--bs-light-bg-subtle);--bs-alert-border-color:var(--bs-light-border-subtle);--bs-alert-link-color:var(--bs-light-text-emphasis)}.alert-dark{--bs-alert-color:var(--bs-dark-text-emphasis);--bs-alert-bg:var(--bs-dark-bg-subtle);--bs-alert-border-color:var(--bs-dark-border-subtle);--bs-alert-link-color:var(--bs-dark-text-emphasis)}@keyframes progress-bar-stripes{0%{background-position-x:1rem}}.progress,.progress-stacked{--bs-progress-height:1rem;--bs-progress-font-size:0.75rem;--bs-progress-bg:var(--bs-secondary-bg);--bs-progress-border-radius:var(--bs-border-radius);--bs-progress-box-shadow:var(--bs-box-shadow-inset);--bs-progress-bar-color:#fff;--bs-progress-bar-bg:#5f7681;--bs-progress-bar-transition:width 0.6s ease;display:flex;height:var(--bs-progress-height);overflow:hidden;font-size:var(--bs-progress-font-size);background-color:var(--bs-progress-bg);border-radius:var(--bs-progress-border-radius);box-shadow:var(--bs-progress-box-shadow)}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:var(--bs-progress-bar-color);text-align:center;white-space:nowrap;background-color:var(--bs-progress-bar-bg);transition:var(--bs-progress-bar-transition)}@media(prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:var(--bs-progress-height)var(--bs-progress-height)}.progress-stacked>.progress{overflow:visible}.progress-stacked>.progress>.progress-bar{width:100%}.progress-bar-animated{animation:1s linear infinite progress-bar-stripes}@media(prefers-reduced-motion:reduce){.progress-bar-animated{animation:none}}.list-group{--bs-list-group-color:var(--bs-body-color);--bs-list-group-bg:var(--bs-body-bg);--bs-list-group-border-color:var(--bs-border-color);--bs-list-group-border-width:var(--bs-border-width);--bs-list-group-border-radius:var(--bs-border-radius);--bs-list-group-item-padding-x:1rem;--bs-list-group-item-padding-y:0.5rem;--bs-list-group-action-color:var(--bs-secondary-color);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-tertiary-bg);--bs-list-group-action-active-color:var(--bs-body-color);--bs-list-group-action-active-bg:var(--bs-secondary-bg);--bs-list-group-disabled-color:var(--bs-secondary-color);--bs-list-group-disabled-bg:var(--bs-body-bg);--bs-list-group-active-color:#fff;--bs-list-group-active-bg:#5f7681;--bs-list-group-active-border-color:#5f7681;display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:var(--bs-list-group-border-radius)}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>.list-group-item::before{content:counters(section,".")". ";counter-increment:section}.list-group-item-action{width:100%;color:var(--bs-list-group-action-color);text-align:inherit}.list-group-item-action:hover,.list-group-item-action:focus{z-index:1;color:var(--bs-list-group-action-hover-color);text-decoration:none;background-color:var(--bs-list-group-action-hover-bg)}.list-group-item-action:active{color:var(--bs-list-group-action-active-color);background-color:var(--bs-list-group-action-active-bg)}.list-group-item{position:relative;display:block;padding:var(--bs-list-group-item-padding-y)var(--bs-list-group-item-padding-x);color:var(--bs-list-group-color);text-decoration:none;background-color:var(--bs-list-group-bg);border:var(--bs-list-group-border-width)solid var(--bs-list-group-border-color)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:var(--bs-list-group-disabled-color);pointer-events:none;background-color:var(--bs-list-group-disabled-bg)}.list-group-item.active{z-index:2;color:var(--bs-list-group-active-color);background-color:var(--bs-list-group-active-bg);border-color:var(--bs-list-group-active-border-color)}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:calc(-1 * var(--bs-list-group-border-width));border-top-width:var(--bs-list-group-border-width)}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}@media(min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 var(--bs-list-group-border-width)}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{--bs-list-group-color:var(--bs-primary-text-emphasis);--bs-list-group-bg:var(--bs-primary-bg-subtle);--bs-list-group-border-color:var(--bs-primary-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-primary-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-primary-border-subtle);--bs-list-group-active-color:var(--bs-primary-bg-subtle);--bs-list-group-active-bg:var(--bs-primary-text-emphasis);--bs-list-group-active-border-color:var(--bs-primary-text-emphasis)}.list-group-item-secondary{--bs-list-group-color:var(--bs-secondary-text-emphasis);--bs-list-group-bg:var(--bs-secondary-bg-subtle);--bs-list-group-border-color:var(--bs-secondary-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-secondary-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-secondary-border-subtle);--bs-list-group-active-color:var(--bs-secondary-bg-subtle);--bs-list-group-active-bg:var(--bs-secondary-text-emphasis);--bs-list-group-active-border-color:var(--bs-secondary-text-emphasis)}.list-group-item-success{--bs-list-group-color:var(--bs-success-text-emphasis);--bs-list-group-bg:var(--bs-success-bg-subtle);--bs-list-group-border-color:var(--bs-success-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-success-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-success-border-subtle);--bs-list-group-active-color:var(--bs-success-bg-subtle);--bs-list-group-active-bg:var(--bs-success-text-emphasis);--bs-list-group-active-border-color:var(--bs-success-text-emphasis)}.list-group-item-info{--bs-list-group-color:var(--bs-info-text-emphasis);--bs-list-group-bg:var(--bs-info-bg-subtle);--bs-list-group-border-color:var(--bs-info-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-info-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-info-border-subtle);--bs-list-group-active-color:var(--bs-info-bg-subtle);--bs-list-group-active-bg:var(--bs-info-text-emphasis);--bs-list-group-active-border-color:var(--bs-info-text-emphasis)}.list-group-item-warning{--bs-list-group-color:var(--bs-warning-text-emphasis);--bs-list-group-bg:var(--bs-warning-bg-subtle);--bs-list-group-border-color:var(--bs-warning-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-warning-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-warning-border-subtle);--bs-list-group-active-color:var(--bs-warning-bg-subtle);--bs-list-group-active-bg:var(--bs-warning-text-emphasis);--bs-list-group-active-border-color:var(--bs-warning-text-emphasis)}.list-group-item-danger{--bs-list-group-color:var(--bs-danger-text-emphasis);--bs-list-group-bg:var(--bs-danger-bg-subtle);--bs-list-group-border-color:var(--bs-danger-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-danger-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-danger-border-subtle);--bs-list-group-active-color:var(--bs-danger-bg-subtle);--bs-list-group-active-bg:var(--bs-danger-text-emphasis);--bs-list-group-active-border-color:var(--bs-danger-text-emphasis)}.list-group-item-light{--bs-list-group-color:var(--bs-light-text-emphasis);--bs-list-group-bg:var(--bs-light-bg-subtle);--bs-list-group-border-color:var(--bs-light-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-light-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-light-border-subtle);--bs-list-group-active-color:var(--bs-light-bg-subtle);--bs-list-group-active-bg:var(--bs-light-text-emphasis);--bs-list-group-active-border-color:var(--bs-light-text-emphasis)}.list-group-item-dark{--bs-list-group-color:var(--bs-dark-text-emphasis);--bs-list-group-bg:var(--bs-dark-bg-subtle);--bs-list-group-border-color:var(--bs-dark-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-dark-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-dark-border-subtle);--bs-list-group-active-color:var(--bs-dark-bg-subtle);--bs-list-group-active-bg:var(--bs-dark-text-emphasis);--bs-list-group-active-border-color:var(--bs-dark-text-emphasis)}.btn-close{--bs-btn-close-color:#000;--bs-btn-close-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e");--bs-btn-close-opacity:0.5;--bs-btn-close-hover-opacity:0.75;--bs-btn-close-focus-shadow:0 0 0 0.25rem rgba(95, 118, 129, 0.25);--bs-btn-close-focus-opacity:1;--bs-btn-close-disabled-opacity:0.25;--bs-btn-close-white-filter:invert(1) grayscale(100%) brightness(200%);box-sizing:content-box;width:1em;height:1em;padding:.25em;color:var(--bs-btn-close-color);background:var(--bs-btn-close-bg)50%/1em no-repeat;border:0;border-radius:.375rem;opacity:var(--bs-btn-close-opacity)}.btn-close:hover{color:var(--bs-btn-close-color);text-decoration:none;opacity:var(--bs-btn-close-hover-opacity)}.btn-close:focus{outline:0;box-shadow:var(--bs-btn-close-focus-shadow);opacity:var(--bs-btn-close-focus-opacity)}.btn-close:disabled,.btn-close.disabled{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;opacity:var(--bs-btn-close-disabled-opacity)}.btn-close-white{filter:var(--bs-btn-close-white-filter)}[data-bs-theme=dark] .btn-close{filter:var(--bs-btn-close-white-filter)}.toast{--bs-toast-zindex:1090;--bs-toast-padding-x:0.75rem;--bs-toast-padding-y:0.5rem;--bs-toast-spacing:1.5rem;--bs-toast-max-width:350px;--bs-toast-font-size:0.875rem;--bs-toast-color: ;--bs-toast-bg:rgba(var(--bs-body-bg-rgb), 0.85);--bs-toast-border-width:var(--bs-border-width);--bs-toast-border-color:var(--bs-border-color-translucent);--bs-toast-border-radius:var(--bs-border-radius);--bs-toast-box-shadow:var(--bs-box-shadow);--bs-toast-header-color:var(--bs-secondary-color);--bs-toast-header-bg:rgba(var(--bs-body-bg-rgb), 0.85);--bs-toast-header-border-color:var(--bs-border-color-translucent);width:var(--bs-toast-max-width);max-width:100%;font-size:var(--bs-toast-font-size);color:var(--bs-toast-color);pointer-events:auto;background-color:var(--bs-toast-bg);background-clip:padding-box;border:var(--bs-toast-border-width)solid var(--bs-toast-border-color);box-shadow:var(--bs-toast-box-shadow);border-radius:var(--bs-toast-border-radius)}.toast.showing{opacity:0}.toast:not(.show){display:none}.toast-container{--bs-toast-zindex:1090;position:absolute;z-index:var(--bs-toast-zindex);width:-moz-max-content;width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:var(--bs-toast-spacing)}.toast-header{display:flex;align-items:center;padding:var(--bs-toast-padding-y)var(--bs-toast-padding-x);color:var(--bs-toast-header-color);background-color:var(--bs-toast-header-bg);background-clip:padding-box;border-bottom:var(--bs-toast-border-width)solid var(--bs-toast-header-border-color);border-top-left-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width));border-top-right-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width))}.toast-header .btn-close{margin-right:calc(-.5 * var(--bs-toast-padding-x));margin-left:var(--bs-toast-padding-x)}.toast-body{padding:var(--bs-toast-padding-x);word-wrap:break-word}.modal{--bs-modal-zindex:1055;--bs-modal-width:500px;--bs-modal-padding:1rem;--bs-modal-margin:0.5rem;--bs-modal-color: ;--bs-modal-bg:var(--bs-body-bg);--bs-modal-border-color:var(--bs-border-color-translucent);--bs-modal-border-width:var(--bs-border-width);--bs-modal-border-radius:var(--bs-border-radius-lg);--bs-modal-box-shadow:var(--bs-box-shadow-sm);--bs-modal-inner-border-radius:calc(var(--bs-border-radius-lg) - (var(--bs-border-width)));--bs-modal-header-padding-x:1rem;--bs-modal-header-padding-y:1rem;--bs-modal-header-padding:1rem 1rem;--bs-modal-header-border-color:var(--bs-border-color);--bs-modal-header-border-width:var(--bs-border-width);--bs-modal-title-line-height:1.5;--bs-modal-footer-gap:0.5rem;--bs-modal-footer-bg: ;--bs-modal-footer-border-color:var(--bs-border-color);--bs-modal-footer-border-width:var(--bs-border-width);position:fixed;top:0;left:0;z-index:var(--bs-modal-zindex);display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:var(--bs-modal-margin);pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-50px)}@media(prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - var(--bs-modal-margin) * 2)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - var(--bs-modal-margin) * 2)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;color:var(--bs-modal-color);pointer-events:auto;background-color:var(--bs-modal-bg);background-clip:padding-box;border:var(--bs-modal-border-width)solid var(--bs-modal-border-color);border-radius:var(--bs-modal-border-radius);box-shadow:var(--bs-modal-box-shadow);outline:0}.modal-backdrop{--bs-backdrop-zindex:1050;--bs-backdrop-bg:#000;--bs-backdrop-opacity:0.5;position:fixed;top:0;left:0;z-index:var(--bs-backdrop-zindex);width:100vw;height:100vh;background-color:var(--bs-backdrop-bg)}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:var(--bs-backdrop-opacity)}.modal-header{display:flex;flex-shrink:0;align-items:center;padding:var(--bs-modal-header-padding);border-bottom:var(--bs-modal-header-border-width)solid var(--bs-modal-header-border-color);border-top-left-radius:var(--bs-modal-inner-border-radius);border-top-right-radius:var(--bs-modal-inner-border-radius)}.modal-header .btn-close{padding:calc(var(--bs-modal-header-padding-y) * .5);margin:calc(-.5 * var(--bs-modal-header-padding-y))calc(-.5 * var(--bs-modal-header-padding-x))calc(-.5 * var(--bs-modal-header-padding-y))auto}.modal-title{margin-bottom:0;line-height:var(--bs-modal-title-line-height)}.modal-body{position:relative;flex:auto;padding:var(--bs-modal-padding)}.modal-footer{display:flex;flex-shrink:0;flex-wrap:wrap;align-items:center;justify-content:flex-end;padding:calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap) * .5);background-color:var(--bs-modal-footer-bg);border-top:var(--bs-modal-footer-border-width)solid var(--bs-modal-footer-border-color);border-bottom-right-radius:var(--bs-modal-inner-border-radius);border-bottom-left-radius:var(--bs-modal-inner-border-radius)}.modal-footer>*{margin:calc(var(--bs-modal-footer-gap) * .5)}@media(min-width:576px){.modal{--bs-modal-margin:1.75rem;--bs-modal-box-shadow:var(--bs-box-shadow)}.modal-dialog{max-width:var(--bs-modal-width);margin-right:auto;margin-left:auto}.modal-sm{--bs-modal-width:300px}}@media(min-width:992px){.modal-lg,.modal-xl{--bs-modal-width:800px}}@media(min-width:1200px){.modal-xl{--bs-modal-width:1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen .modal-header,.modal-fullscreen .modal-footer{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}@media(max-width:575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-sm-down .modal-header,.modal-fullscreen-sm-down .modal-footer{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}}@media(max-width:767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-md-down .modal-header,.modal-fullscreen-md-down .modal-footer{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}}@media(max-width:991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-lg-down .modal-header,.modal-fullscreen-lg-down .modal-footer{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}}@media(max-width:1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xl-down .modal-header,.modal-fullscreen-xl-down .modal-footer{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}}@media(max-width:1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xxl-down .modal-header,.modal-fullscreen-xxl-down .modal-footer{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}}.tooltip{--bs-tooltip-zindex:1080;--bs-tooltip-max-width:200px;--bs-tooltip-padding-x:0.5rem;--bs-tooltip-padding-y:0.25rem;--bs-tooltip-margin: ;--bs-tooltip-font-size:0.875rem;--bs-tooltip-color:var(--bs-body-bg);--bs-tooltip-bg:var(--bs-emphasis-color);--bs-tooltip-border-radius:var(--bs-border-radius);--bs-tooltip-opacity:0.9;--bs-tooltip-arrow-width:0.8rem;--bs-tooltip-arrow-height:0.4rem;z-index:var(--bs-tooltip-zindex);display:block;margin:var(--bs-tooltip-margin);font-family:open sans,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-tooltip-font-size);word-wrap:break-word;opacity:0}.tooltip.show{opacity:var(--bs-tooltip-opacity)}.tooltip .tooltip-arrow{display:block;width:var(--bs-tooltip-arrow-width);height:var(--bs-tooltip-arrow-height)}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-top .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow{bottom:calc(-1 * var(--bs-tooltip-arrow-height))}.bs-tooltip-top .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before{top:-1px;border-width:var(--bs-tooltip-arrow-height)calc(var(--bs-tooltip-arrow-width) * .5)0;border-top-color:var(--bs-tooltip-bg)}.bs-tooltip-end .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow{left:calc(-1 * var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-end .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before{right:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5)var(--bs-tooltip-arrow-height)calc(var(--bs-tooltip-arrow-width) * .5)0;border-right-color:var(--bs-tooltip-bg)}.bs-tooltip-bottom .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow{top:calc(-1 * var(--bs-tooltip-arrow-height))}.bs-tooltip-bottom .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before{bottom:-1px;border-width:0 calc(var(--bs-tooltip-arrow-width) * .5)var(--bs-tooltip-arrow-height);border-bottom-color:var(--bs-tooltip-bg)}.bs-tooltip-start .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow{right:calc(-1 * var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-start .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before{left:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5)0 calc(var(--bs-tooltip-arrow-width) * .5)var(--bs-tooltip-arrow-height);border-left-color:var(--bs-tooltip-bg)}.tooltip-inner{max-width:var(--bs-tooltip-max-width);padding:var(--bs-tooltip-padding-y)var(--bs-tooltip-padding-x);color:var(--bs-tooltip-color);text-align:center;background-color:var(--bs-tooltip-bg);border-radius:var(--bs-tooltip-border-radius)}.popover{--bs-popover-zindex:1070;--bs-popover-max-width:276px;--bs-popover-font-size:0.875rem;--bs-popover-bg:var(--bs-body-bg);--bs-popover-border-width:var(--bs-border-width);--bs-popover-border-color:var(--bs-border-color-translucent);--bs-popover-border-radius:var(--bs-border-radius-lg);--bs-popover-inner-border-radius:calc(var(--bs-border-radius-lg) - var(--bs-border-width));--bs-popover-box-shadow:var(--bs-box-shadow);--bs-popover-header-padding-x:1rem;--bs-popover-header-padding-y:0.5rem;--bs-popover-header-font-size:1rem;--bs-popover-header-color:inherit;--bs-popover-header-bg:var(--bs-secondary-bg);--bs-popover-body-padding-x:1rem;--bs-popover-body-padding-y:1rem;--bs-popover-body-color:var(--bs-body-color);--bs-popover-arrow-width:1rem;--bs-popover-arrow-height:0.5rem;--bs-popover-arrow-border:var(--bs-popover-border-color);z-index:var(--bs-popover-zindex);display:block;max-width:var(--bs-popover-max-width);font-family:open sans,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-popover-font-size);word-wrap:break-word;background-color:var(--bs-popover-bg);background-clip:padding-box;border:var(--bs-popover-border-width)solid var(--bs-popover-border-color);border-radius:var(--bs-popover-border-radius);box-shadow:var(--bs-popover-box-shadow)}.popover .popover-arrow{display:block;width:var(--bs-popover-arrow-width);height:var(--bs-popover-arrow-height)}.popover .popover-arrow::before,.popover .popover-arrow::after{position:absolute;display:block;content:"";border-color:transparent;border-style:solid;border-width:0}.bs-popover-top>.popover-arrow,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow{bottom:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-top>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after{border-width:var(--bs-popover-arrow-height)calc(var(--bs-popover-arrow-width) * .5)0}.bs-popover-top>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before{bottom:0;border-top-color:var(--bs-popover-arrow-border)}.bs-popover-top>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after{bottom:var(--bs-popover-border-width);border-top-color:var(--bs-popover-bg)}.bs-popover-end>.popover-arrow,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow{left:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-end>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after{border-width:calc(var(--bs-popover-arrow-width) * .5)var(--bs-popover-arrow-height)calc(var(--bs-popover-arrow-width) * .5)0}.bs-popover-end>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before{left:0;border-right-color:var(--bs-popover-arrow-border)}.bs-popover-end>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after{left:var(--bs-popover-border-width);border-right-color:var(--bs-popover-bg)}.bs-popover-bottom>.popover-arrow,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow{top:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-bottom>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after{border-width:0 calc(var(--bs-popover-arrow-width) * .5)var(--bs-popover-arrow-height)}.bs-popover-bottom>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before{top:0;border-bottom-color:var(--bs-popover-arrow-border)}.bs-popover-bottom>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after{top:var(--bs-popover-border-width);border-bottom-color:var(--bs-popover-bg)}.bs-popover-bottom .popover-header::before,.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before{position:absolute;top:0;left:50%;display:block;width:var(--bs-popover-arrow-width);margin-left:calc(-.5 * var(--bs-popover-arrow-width));content:"";border-bottom:var(--bs-popover-border-width)solid var(--bs-popover-header-bg)}.bs-popover-start>.popover-arrow,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow{right:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-start>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after{border-width:calc(var(--bs-popover-arrow-width) * .5)0 calc(var(--bs-popover-arrow-width) * .5)var(--bs-popover-arrow-height)}.bs-popover-start>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before{right:0;border-left-color:var(--bs-popover-arrow-border)}.bs-popover-start>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after{right:var(--bs-popover-border-width);border-left-color:var(--bs-popover-bg)}.popover-header{padding:var(--bs-popover-header-padding-y)var(--bs-popover-header-padding-x);margin-bottom:0;font-size:var(--bs-popover-header-font-size);color:var(--bs-popover-header-color);background-color:var(--bs-popover-header-bg);border-bottom:var(--bs-popover-border-width)solid var(--bs-popover-border-color);border-top-left-radius:var(--bs-popover-inner-border-radius);border-top-right-radius:var(--bs-popover-inner-border-radius)}.popover-header:empty{display:none}.popover-body{padding:var(--bs-popover-body-padding-y)var(--bs-popover-body-padding-x);color:var(--bs-popover-body-color)}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;backface-visibility:hidden;transition:transform .6s ease-in-out}@media(prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item.active,.carousel-item-next,.carousel-item-prev{display:block}.carousel-item-next:not(.carousel-item-start),.active.carousel-item-end{transform:translateX(100%)}.carousel-item-prev:not(.carousel-item-end),.active.carousel-item-start{transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item.active,.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end{z-index:1;opacity:1}.carousel-fade .active.carousel-item-start,.carousel-fade .active.carousel-item-end{z-index:0;opacity:0;transition:opacity 0s .6s}@media(prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-start,.carousel-fade .active.carousel-item-end{transition:none}}.carousel-control-prev,.carousel-control-next{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:0 0;border:0;opacity:.5;transition:opacity .15s ease}@media(prefers-reduced-motion:reduce){.carousel-control-prev,.carousel-control-next{transition:none}}.carousel-control-prev:hover,.carousel-control-prev:focus,.carousel-control-next:hover,.carousel-control-next:focus{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0;background-image:linear-gradient(90deg,rgba(0,0,0,.25),rgba(0,0,0,.1%))}.carousel-control-next{right:0;background-image:linear-gradient(270deg,rgba(0,0,0,.25),rgba(0,0,0,.1%))}.carousel-control-prev-icon,.carousel-control-next-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:initial;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media(prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:#fff;text-align:center}.carousel-dark .carousel-control-prev-icon,.carousel-dark .carousel-control-next-icon{filter:invert(1)grayscale(100)}.carousel-dark .carousel-indicators [data-bs-target]{background-color:#000}.carousel-dark .carousel-caption{color:#000}[data-bs-theme=dark] .carousel .carousel-control-prev-icon,[data-bs-theme=dark] .carousel .carousel-control-next-icon,[data-bs-theme=dark].carousel .carousel-control-prev-icon,[data-bs-theme=dark].carousel .carousel-control-next-icon{filter:invert(1)grayscale(100)}[data-bs-theme=dark] .carousel .carousel-indicators [data-bs-target],[data-bs-theme=dark].carousel .carousel-indicators [data-bs-target]{background-color:#000}[data-bs-theme=dark] .carousel .carousel-caption,[data-bs-theme=dark].carousel .carousel-caption{color:#000}.spinner-grow,.spinner-border{display:inline-block;width:var(--bs-spinner-width);height:var(--bs-spinner-height);vertical-align:var(--bs-spinner-vertical-align);border-radius:50%;animation:var(--bs-spinner-animation-speed)linear infinite var(--bs-spinner-animation-name)}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-border-width:0.25em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-border;border:var(--bs-spinner-border-width)solid;border-right-color:transparent}.spinner-border-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem;--bs-spinner-border-width:0.2em}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-grow;background-color:currentcolor;opacity:0}.spinner-grow-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem}@media(prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{--bs-spinner-animation-speed:1.5s}}.offcanvas,.offcanvas-xxl,.offcanvas-xl,.offcanvas-lg,.offcanvas-md,.offcanvas-sm{--bs-offcanvas-zindex:1045;--bs-offcanvas-width:400px;--bs-offcanvas-height:30vh;--bs-offcanvas-padding-x:1rem;--bs-offcanvas-padding-y:1rem;--bs-offcanvas-color:var(--bs-body-color);--bs-offcanvas-bg:var(--bs-body-bg);--bs-offcanvas-border-width:var(--bs-border-width);--bs-offcanvas-border-color:var(--bs-border-color-translucent);--bs-offcanvas-box-shadow:var(--bs-box-shadow-sm);--bs-offcanvas-transition:transform 0.3s ease-in-out;--bs-offcanvas-title-line-height:1.5}@media(max-width:575.98px){.offcanvas-sm{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;box-shadow:var(--bs-offcanvas-box-shadow);transition:var(--bs-offcanvas-transition)}}@media(max-width:575.98px) and (prefers-reduced-motion:reduce){.offcanvas-sm{transition:none}}@media(max-width:575.98px){.offcanvas-sm.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-sm.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-sm.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-sm.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-sm.showing,.offcanvas-sm.show:not(.hiding){transform:none}.offcanvas-sm.showing,.offcanvas-sm.hiding,.offcanvas-sm.show{visibility:visible}}@media(min-width:576px){.offcanvas-sm{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-sm .offcanvas-header{display:none}.offcanvas-sm .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media(max-width:767.98px){.offcanvas-md{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;box-shadow:var(--bs-offcanvas-box-shadow);transition:var(--bs-offcanvas-transition)}}@media(max-width:767.98px) and (prefers-reduced-motion:reduce){.offcanvas-md{transition:none}}@media(max-width:767.98px){.offcanvas-md.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-md.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-md.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-md.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-md.showing,.offcanvas-md.show:not(.hiding){transform:none}.offcanvas-md.showing,.offcanvas-md.hiding,.offcanvas-md.show{visibility:visible}}@media(min-width:768px){.offcanvas-md{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-md .offcanvas-header{display:none}.offcanvas-md .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media(max-width:991.98px){.offcanvas-lg{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;box-shadow:var(--bs-offcanvas-box-shadow);transition:var(--bs-offcanvas-transition)}}@media(max-width:991.98px) and (prefers-reduced-motion:reduce){.offcanvas-lg{transition:none}}@media(max-width:991.98px){.offcanvas-lg.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-lg.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-lg.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-lg.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-lg.showing,.offcanvas-lg.show:not(.hiding){transform:none}.offcanvas-lg.showing,.offcanvas-lg.hiding,.offcanvas-lg.show{visibility:visible}}@media(min-width:992px){.offcanvas-lg{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-lg .offcanvas-header{display:none}.offcanvas-lg .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media(max-width:1199.98px){.offcanvas-xl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;box-shadow:var(--bs-offcanvas-box-shadow);transition:var(--bs-offcanvas-transition)}}@media(max-width:1199.98px) and (prefers-reduced-motion:reduce){.offcanvas-xl{transition:none}}@media(max-width:1199.98px){.offcanvas-xl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xl.showing,.offcanvas-xl.show:not(.hiding){transform:none}.offcanvas-xl.showing,.offcanvas-xl.hiding,.offcanvas-xl.show{visibility:visible}}@media(min-width:1200px){.offcanvas-xl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-xl .offcanvas-header{display:none}.offcanvas-xl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media(max-width:1399.98px){.offcanvas-xxl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;box-shadow:var(--bs-offcanvas-box-shadow);transition:var(--bs-offcanvas-transition)}}@media(max-width:1399.98px) and (prefers-reduced-motion:reduce){.offcanvas-xxl{transition:none}}@media(max-width:1399.98px){.offcanvas-xxl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xxl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xxl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xxl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xxl.showing,.offcanvas-xxl.show:not(.hiding){transform:none}.offcanvas-xxl.showing,.offcanvas-xxl.hiding,.offcanvas-xxl.show{visibility:visible}}@media(min-width:1400px){.offcanvas-xxl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-xxl .offcanvas-header{display:none}.offcanvas-xxl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}.offcanvas{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;box-shadow:var(--bs-offcanvas-box-shadow);transition:var(--bs-offcanvas-transition)}@media(prefers-reduced-motion:reduce){.offcanvas{transition:none}}.offcanvas.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas.showing,.offcanvas.show:not(.hiding){transform:none}.offcanvas.showing,.offcanvas.hiding,.offcanvas.show{visibility:visible}.offcanvas-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.offcanvas-backdrop.fade{opacity:0}.offcanvas-backdrop.show{opacity:.5}.offcanvas-header{display:flex;align-items:center;padding:var(--bs-offcanvas-padding-y)var(--bs-offcanvas-padding-x)}.offcanvas-header .btn-close{padding:calc(var(--bs-offcanvas-padding-y) * .5);margin:calc(-.5 * var(--bs-offcanvas-padding-y))calc(-.5 * var(--bs-offcanvas-padding-x))calc(-.5 * var(--bs-offcanvas-padding-y))auto}.offcanvas-title{margin-bottom:0;line-height:var(--bs-offcanvas-title-line-height)}.offcanvas-body{flex-grow:1;padding:var(--bs-offcanvas-padding-y)var(--bs-offcanvas-padding-x);overflow-y:auto}.placeholder{display:inline-block;min-height:1em;vertical-align:middle;cursor:wait;background-color:currentcolor;opacity:.5}.placeholder.btn::before,div.drawio button.placeholder::before,.td-blog .placeholder.td-rss-button::before{display:inline-block;content:""}.placeholder-xs{min-height:.6em}.placeholder-sm{min-height:.8em}.placeholder-lg{min-height:1.2em}.placeholder-glow .placeholder{animation:placeholder-glow 2s ease-in-out infinite}@keyframes placeholder-glow{50%{opacity:.2}}.placeholder-wave{-webkit-mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,.8) 75%,#000 95%);mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,.8) 75%,#000 95%);-webkit-mask-size:200% 100%;mask-size:200% 100%;animation:placeholder-wave 2s linear infinite}@keyframes placeholder-wave{100%{-webkit-mask-position:-200% 0%;mask-position:-200% 0%}}.clearfix::after{display:block;clear:both;content:""}.text-bg-primary{color:#fff!important;background-color:RGBA(var(--bs-primary-rgb),var(--bs-bg-opacity,1))!important}.text-bg-secondary{color:#000!important;background-color:RGBA(var(--bs-secondary-rgb),var(--bs-bg-opacity,1))!important}.text-bg-success{color:#000!important;background-color:RGBA(var(--bs-success-rgb),var(--bs-bg-opacity,1))!important}.text-bg-info{color:#fff!important;background-color:RGBA(var(--bs-info-rgb),var(--bs-bg-opacity,1))!important}.text-bg-warning{color:#000!important;background-color:RGBA(var(--bs-warning-rgb),var(--bs-bg-opacity,1))!important}.text-bg-danger{color:#000!important;background-color:RGBA(var(--bs-danger-rgb),var(--bs-bg-opacity,1))!important}.text-bg-light{color:#000!important;background-color:RGBA(var(--bs-light-rgb),var(--bs-bg-opacity,1))!important}.text-bg-dark{color:#fff!important;background-color:RGBA(var(--bs-dark-rgb),var(--bs-bg-opacity,1))!important}.link-primary{color:RGBA(var(--bs-primary-rgb),var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(var(--bs-primary-rgb),var(--bs-link-underline-opacity,1))!important}.link-primary:hover,.link-primary:focus{color:RGBA(67,83,90,var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(67,83,90,var(--bs-link-underline-opacity,1))!important}.link-secondary{color:RGBA(var(--bs-secondary-rgb),var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(var(--bs-secondary-rgb),var(--bs-link-underline-opacity,1))!important}.link-secondary:hover,.link-secondary:focus{color:RGBA(248,168,77,var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(248,168,77,var(--bs-link-underline-opacity,1))!important}.link-success{color:RGBA(var(--bs-success-rgb),var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(var(--bs-success-rgb),var(--bs-link-underline-opacity,1))!important}.link-success:hover,.link-success:focus{color:RGBA(115,156,255,var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(115,156,255,var(--bs-link-underline-opacity,1))!important}.link-info{color:RGBA(var(--bs-info-rgb),var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(var(--bs-info-rgb),var(--bs-link-underline-opacity,1))!important}.link-info:hover,.link-info:focus{color:RGBA(0,85,134,var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(0,85,134,var(--bs-link-underline-opacity,1))!important}.link-warning{color:RGBA(var(--bs-warning-rgb),var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(var(--bs-warning-rgb),var(--bs-link-underline-opacity,1))!important}.link-warning:hover,.link-warning:focus{color:RGBA(235,110,110,var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(235,110,110,var(--bs-link-underline-opacity,1))!important}.link-danger{color:RGBA(var(--bs-danger-rgb),var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(var(--bs-danger-rgb),var(--bs-link-underline-opacity,1))!important}.link-danger:hover,.link-danger:focus{color:RGBA(242,151,140,var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(242,151,140,var(--bs-link-underline-opacity,1))!important}.link-light{color:RGBA(var(--bs-light-rgb),var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(var(--bs-light-rgb),var(--bs-link-underline-opacity,1))!important}.link-light:hover,.link-light:focus{color:RGBA(224,247,243,var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(224,247,243,var(--bs-link-underline-opacity,1))!important}.link-dark{color:RGBA(var(--bs-dark-rgb),var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(var(--bs-dark-rgb),var(--bs-link-underline-opacity,1))!important}.link-dark:hover,.link-dark:focus{color:RGBA(36,44,50,var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(36,44,50,var(--bs-link-underline-opacity,1))!important}.link-body-emphasis{color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,1))!important}.link-body-emphasis:hover,.link-body-emphasis:focus{color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-opacity,.75))!important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,.75))!important}.focus-ring:focus{outline:0;box-shadow:var(--bs-focus-ring-x,0)var(--bs-focus-ring-y,0)var(--bs-focus-ring-blur,0)var(--bs-focus-ring-width)var(--bs-focus-ring-color)}.icon-link{display:inline-flex;gap:.375rem;align-items:center;text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,.5));text-underline-offset:.25em;backface-visibility:hidden}.icon-link>.bi{flex-shrink:0;width:1em;height:1em;fill:currentcolor;transition:.2s ease-in-out transform}@media(prefers-reduced-motion:reduce){.icon-link>.bi{transition:none}}.icon-link-hover:hover>.bi,.icon-link-hover:focus-visible>.bi{transform:var(--bs-icon-link-transform,translate3d(.25em,0,0))}.ratio{position:relative;width:100%}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.ratio-1x1{--bs-aspect-ratio:100%}.ratio-4x3{--bs-aspect-ratio:calc(3 / 4 * 100%)}.ratio-16x9{--bs-aspect-ratio:calc(9 / 16 * 100%)}.ratio-21x9{--bs-aspect-ratio:calc(9 / 21 * 100%)}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:sticky;top:0;z-index:1020}.sticky-bottom{position:sticky;bottom:0;z-index:1020}@media(min-width:576px){.sticky-sm-top{position:sticky;top:0;z-index:1020}.sticky-sm-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width:768px){.sticky-md-top{position:sticky;top:0;z-index:1020}.sticky-md-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width:992px){.sticky-lg-top{position:sticky;top:0;z-index:1020}.sticky-lg-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width:1200px){.sticky-xl-top{position:sticky;top:0;z-index:1020}.sticky-xl-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width:1400px){.sticky-xxl-top{position:sticky;top:0;z-index:1020}.sticky-xxl-bottom{position:sticky;bottom:0;z-index:1020}}.hstack{display:flex;flex-direction:row;align-items:center;align-self:stretch}.vstack{display:flex;flex:auto;flex-direction:column;align-self:stretch}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.visually-hidden:not(caption),.visually-hidden-focusable:not(:focus):not(:focus-within):not(caption){position:absolute!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vr{display:inline-block;align-self:stretch;width:var(--bs-border-width);min-height:1em;background-color:currentcolor;opacity:.25}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.float-start{float:left!important}.float-end{float:right!important}.float-none{float:none!important}.object-fit-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-none{-o-object-fit:none!important;object-fit:none!important}.opacity-0{opacity:0!important}.opacity-25{opacity:.25!important}.opacity-50{opacity:.5!important}.opacity-75{opacity:.75!important}.opacity-100{opacity:1!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.overflow-x-auto{overflow-x:auto!important}.overflow-x-hidden{overflow-x:hidden!important}.overflow-x-visible{overflow-x:visible!important}.overflow-x-scroll{overflow-x:scroll!important}.overflow-y-auto{overflow-y:auto!important}.overflow-y-hidden{overflow-y:hidden!important}.overflow-y-visible{overflow-y:visible!important}.overflow-y-scroll{overflow-y:scroll!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-inline-grid{display:inline-grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.shadow{box-shadow:var(--bs-box-shadow)!important}.shadow-sm{box-shadow:var(--bs-box-shadow-sm)!important}.shadow-lg{box-shadow:var(--bs-box-shadow-lg)!important}.shadow-none{box-shadow:none!important}.focus-ring-primary{--bs-focus-ring-color:rgba(var(--bs-primary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-secondary{--bs-focus-ring-color:rgba(var(--bs-secondary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-success{--bs-focus-ring-color:rgba(var(--bs-success-rgb), var(--bs-focus-ring-opacity))}.focus-ring-info{--bs-focus-ring-color:rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity))}.focus-ring-warning{--bs-focus-ring-color:rgba(var(--bs-warning-rgb), var(--bs-focus-ring-opacity))}.focus-ring-danger{--bs-focus-ring-color:rgba(var(--bs-danger-rgb), var(--bs-focus-ring-opacity))}.focus-ring-light{--bs-focus-ring-color:rgba(var(--bs-light-rgb), var(--bs-focus-ring-opacity))}.focus-ring-dark{--bs-focus-ring-color:rgba(var(--bs-dark-rgb), var(--bs-focus-ring-opacity))}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:sticky!important}.top-0{top:0!important}.top-50{top:50%!important}.top-100{top:100%!important}.bottom-0{bottom:0!important}.bottom-50{bottom:50%!important}.bottom-100{bottom:100%!important}.start-0{left:0!important}.start-50{left:50%!important}.start-100{left:100%!important}.end-0{right:0!important}.end-50{right:50%!important}.end-100{right:100%!important}.translate-middle{transform:translate(-50%,-50%)!important}.translate-middle-x{transform:translateX(-50%)!important}.translate-middle-y{transform:translateY(-50%)!important}.border{border:var(--bs-border-width)var(--bs-border-style)var(--bs-border-color)!important}.border-0{border:0!important}.border-top,.td-page-meta__lastmod{border-top:var(--bs-border-width)var(--bs-border-style)var(--bs-border-color)!important}.border-top-0{border-top:0!important}.border-end{border-right:var(--bs-border-width)var(--bs-border-style)var(--bs-border-color)!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:var(--bs-border-width)var(--bs-border-style)var(--bs-border-color)!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:var(--bs-border-width)var(--bs-border-style)var(--bs-border-color)!important}.border-start-0{border-left:0!important}.border-primary{--bs-border-opacity:1;border-color:rgba(var(--bs-primary-rgb),var(--bs-border-opacity))!important}.border-secondary{--bs-border-opacity:1;border-color:rgba(var(--bs-secondary-rgb),var(--bs-border-opacity))!important}.border-success{--bs-border-opacity:1;border-color:rgba(var(--bs-success-rgb),var(--bs-border-opacity))!important}.border-info{--bs-border-opacity:1;border-color:rgba(var(--bs-info-rgb),var(--bs-border-opacity))!important}.border-warning{--bs-border-opacity:1;border-color:rgba(var(--bs-warning-rgb),var(--bs-border-opacity))!important}.border-danger{--bs-border-opacity:1;border-color:rgba(var(--bs-danger-rgb),var(--bs-border-opacity))!important}.border-light{--bs-border-opacity:1;border-color:rgba(var(--bs-light-rgb),var(--bs-border-opacity))!important}.border-dark{--bs-border-opacity:1;border-color:rgba(var(--bs-dark-rgb),var(--bs-border-opacity))!important}.border-black{--bs-border-opacity:1;border-color:rgba(var(--bs-black-rgb),var(--bs-border-opacity))!important}.border-white{--bs-border-opacity:1;border-color:rgba(var(--bs-white-rgb),var(--bs-border-opacity))!important}.border-primary-subtle{border-color:var(--bs-primary-border-subtle)!important}.border-secondary-subtle{border-color:var(--bs-secondary-border-subtle)!important}.border-success-subtle{border-color:var(--bs-success-border-subtle)!important}.border-info-subtle{border-color:var(--bs-info-border-subtle)!important}.border-warning-subtle{border-color:var(--bs-warning-border-subtle)!important}.border-danger-subtle{border-color:var(--bs-danger-border-subtle)!important}.border-light-subtle{border-color:var(--bs-light-border-subtle)!important}.border-dark-subtle{border-color:var(--bs-dark-border-subtle)!important}.border-1{border-width:1px!important}.border-2{border-width:2px!important}.border-3{border-width:3px!important}.border-4{border-width:4px!important}.border-5{border-width:5px!important}.border-opacity-10{--bs-border-opacity:0.1}.border-opacity-25{--bs-border-opacity:0.25}.border-opacity-50{--bs-border-opacity:0.5}.border-opacity-75{--bs-border-opacity:0.75}.border-opacity-100{--bs-border-opacity:1}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mh-100{max-height:100%!important}.vh-100{height:100vh!important}.min-vh-100{min-height:100vh!important}.flex-fill{flex:auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:3rem!important}.m-auto{margin:auto!important}.mx-0{margin-right:0!important;margin-left:0!important}.mx-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-3{margin-right:1rem!important;margin-left:1rem!important}.mx-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-5{margin-right:3rem!important;margin-left:3rem!important}.mx-auto{margin-right:auto!important;margin-left:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:3rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.25rem!important}.me-2{margin-right:.5rem!important}.me-3{margin-right:1rem!important}.me-4{margin-right:1.5rem!important}.me-5{margin-right:3rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:3rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.25rem!important}.ms-2{margin-left:.5rem!important}.ms-3{margin-left:1rem!important}.ms-4{margin-left:1.5rem!important}.ms-5{margin-left:3rem!important}.ms-auto{margin-left:auto!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:3rem!important}.px-0{padding-right:0!important;padding-left:0!important}.px-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-3{padding-right:1rem!important;padding-left:1rem!important}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-5{padding-right:3rem!important;padding-left:3rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:3rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.25rem!important}.pe-2{padding-right:.5rem!important}.pe-3{padding-right:1rem!important}.pe-4{padding-right:1.5rem!important}.pe-5{padding-right:3rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:3rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.25rem!important}.ps-2{padding-left:.5rem!important}.ps-3{padding-left:1rem!important}.ps-4{padding-left:1.5rem!important}.ps-5{padding-left:3rem!important}.gap-0{gap:0!important}.gap-1{gap:.25rem!important}.gap-2{gap:.5rem!important}.gap-3{gap:1rem!important}.gap-4{gap:1.5rem!important}.gap-5{gap:3rem!important}.row-gap-0{row-gap:0!important}.row-gap-1{row-gap:.25rem!important}.row-gap-2{row-gap:.5rem!important}.row-gap-3{row-gap:1rem!important}.row-gap-4{row-gap:1.5rem!important}.row-gap-5{row-gap:3rem!important}.column-gap-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-1{-moz-column-gap:.25rem!important;column-gap:.25rem!important}.column-gap-2{-moz-column-gap:.5rem!important;column-gap:.5rem!important}.column-gap-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.font-monospace{font-family:var(--bs-font-monospace)!important}.fs-1{font-size:calc(1.375rem + 1.5vw)!important}.fs-2{font-size:calc(1.325rem + .9vw)!important}.fs-3{font-size:calc(1.275rem + .3vw)!important}.fs-4{font-size:calc(1.26rem + .12vw)!important}.fs-5{font-size:1.15rem!important}.fs-6{font-size:1rem!important}.fst-italic{font-style:italic!important}.fst-normal{font-style:normal!important}.fw-lighter{font-weight:lighter!important}.fw-light{font-weight:300!important}.fw-normal{font-weight:400!important}.fw-medium{font-weight:500!important}.fw-semibold{font-weight:600!important}.fw-bold{font-weight:700!important}.fw-bolder{font-weight:bolder!important}.lh-1{line-height:1!important}.lh-sm{line-height:1.25!important}.lh-base{line-height:1.5!important}.lh-lg{line-height:2!important}.text-start{text-align:left!important}.text-end{text-align:right!important}.text-center{text-align:center!important}.text-decoration-none{text-decoration:none!important}.text-decoration-underline{text-decoration:underline!important}.text-decoration-line-through{text-decoration:line-through!important}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-break{word-wrap:break-word!important;word-break:break-word!important}.text-primary{--bs-text-opacity:1;color:rgba(var(--bs-primary-rgb),var(--bs-text-opacity))!important}.text-secondary{--bs-text-opacity:1;color:rgba(var(--bs-secondary-rgb),var(--bs-text-opacity))!important}.text-success{--bs-text-opacity:1;color:rgba(var(--bs-success-rgb),var(--bs-text-opacity))!important}.text-info{--bs-text-opacity:1;color:rgba(var(--bs-info-rgb),var(--bs-text-opacity))!important}.text-warning{--bs-text-opacity:1;color:rgba(var(--bs-warning-rgb),var(--bs-text-opacity))!important}.text-danger{--bs-text-opacity:1;color:rgba(var(--bs-danger-rgb),var(--bs-text-opacity))!important}.text-light{--bs-text-opacity:1;color:rgba(var(--bs-light-rgb),var(--bs-text-opacity))!important}.text-dark{--bs-text-opacity:1;color:rgba(var(--bs-dark-rgb),var(--bs-text-opacity))!important}.text-black{--bs-text-opacity:1;color:rgba(var(--bs-black-rgb),var(--bs-text-opacity))!important}.text-white{--bs-text-opacity:1;color:rgba(var(--bs-white-rgb),var(--bs-text-opacity))!important}.text-body{--bs-text-opacity:1;color:rgba(var(--bs-body-color-rgb),var(--bs-text-opacity))!important}.text-muted{--bs-text-opacity:1;color:var(--bs-secondary-color)!important}.text-black-50{--bs-text-opacity:1;color:rgba(0,0,0,.5)!important}.text-white-50{--bs-text-opacity:1;color:rgba(255,255,255,.5)!important}.text-body-secondary,.td-page-meta__lastmod{--bs-text-opacity:1;color:var(--bs-secondary-color)!important}.text-body-tertiary{--bs-text-opacity:1;color:var(--bs-tertiary-color)!important}.text-body-emphasis{--bs-text-opacity:1;color:var(--bs-emphasis-color)!important}.text-reset{--bs-text-opacity:1;color:inherit!important}.text-opacity-25{--bs-text-opacity:0.25}.text-opacity-50{--bs-text-opacity:0.5}.text-opacity-75{--bs-text-opacity:0.75}.text-opacity-100{--bs-text-opacity:1}.text-primary-emphasis{color:var(--bs-primary-text-emphasis)!important}.text-secondary-emphasis{color:var(--bs-secondary-text-emphasis)!important}.text-success-emphasis{color:var(--bs-success-text-emphasis)!important}.text-info-emphasis{color:var(--bs-info-text-emphasis)!important}.text-warning-emphasis{color:var(--bs-warning-text-emphasis)!important}.text-danger-emphasis{color:var(--bs-danger-text-emphasis)!important}.text-light-emphasis{color:var(--bs-light-text-emphasis)!important}.text-dark-emphasis{color:var(--bs-dark-text-emphasis)!important}.link-opacity-10{--bs-link-opacity:0.1}.link-opacity-10-hover:hover{--bs-link-opacity:0.1}.link-opacity-25{--bs-link-opacity:0.25}.link-opacity-25-hover:hover{--bs-link-opacity:0.25}.link-opacity-50{--bs-link-opacity:0.5}.link-opacity-50-hover:hover{--bs-link-opacity:0.5}.link-opacity-75{--bs-link-opacity:0.75}.link-opacity-75-hover:hover{--bs-link-opacity:0.75}.link-opacity-100{--bs-link-opacity:1}.link-opacity-100-hover:hover{--bs-link-opacity:1}.link-offset-1{text-underline-offset:.125em!important}.link-offset-1-hover:hover{text-underline-offset:.125em!important}.link-offset-2{text-underline-offset:.25em!important}.link-offset-2-hover:hover{text-underline-offset:.25em!important}.link-offset-3{text-underline-offset:.375em!important}.link-offset-3-hover:hover{text-underline-offset:.375em!important}.link-underline-primary{--bs-link-underline-opacity:1;text-decoration-color:rgba(var(--bs-primary-rgb),var(--bs-link-underline-opacity))!important}.link-underline-secondary{--bs-link-underline-opacity:1;text-decoration-color:rgba(var(--bs-secondary-rgb),var(--bs-link-underline-opacity))!important}.link-underline-success{--bs-link-underline-opacity:1;text-decoration-color:rgba(var(--bs-success-rgb),var(--bs-link-underline-opacity))!important}.link-underline-info{--bs-link-underline-opacity:1;text-decoration-color:rgba(var(--bs-info-rgb),var(--bs-link-underline-opacity))!important}.link-underline-warning{--bs-link-underline-opacity:1;text-decoration-color:rgba(var(--bs-warning-rgb),var(--bs-link-underline-opacity))!important}.link-underline-danger{--bs-link-underline-opacity:1;text-decoration-color:rgba(var(--bs-danger-rgb),var(--bs-link-underline-opacity))!important}.link-underline-light{--bs-link-underline-opacity:1;text-decoration-color:rgba(var(--bs-light-rgb),var(--bs-link-underline-opacity))!important}.link-underline-dark{--bs-link-underline-opacity:1;text-decoration-color:rgba(var(--bs-dark-rgb),var(--bs-link-underline-opacity))!important}.link-underline{--bs-link-underline-opacity:1;text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-underline-opacity,1))!important}.link-underline-opacity-0{--bs-link-underline-opacity:0}.link-underline-opacity-0-hover:hover{--bs-link-underline-opacity:0}.link-underline-opacity-10{--bs-link-underline-opacity:0.1}.link-underline-opacity-10-hover:hover{--bs-link-underline-opacity:0.1}.link-underline-opacity-25{--bs-link-underline-opacity:0.25}.link-underline-opacity-25-hover:hover{--bs-link-underline-opacity:0.25}.link-underline-opacity-50{--bs-link-underline-opacity:0.5}.link-underline-opacity-50-hover:hover{--bs-link-underline-opacity:0.5}.link-underline-opacity-75{--bs-link-underline-opacity:0.75}.link-underline-opacity-75-hover:hover{--bs-link-underline-opacity:0.75}.link-underline-opacity-100{--bs-link-underline-opacity:1}.link-underline-opacity-100-hover:hover{--bs-link-underline-opacity:1}.bg-primary{--bs-bg-opacity:1;background-color:rgba(var(--bs-primary-rgb),var(--bs-bg-opacity))!important}.bg-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-rgb),var(--bs-bg-opacity))!important}.bg-success{--bs-bg-opacity:1;background-color:rgba(var(--bs-success-rgb),var(--bs-bg-opacity))!important}.bg-info{--bs-bg-opacity:1;background-color:rgba(var(--bs-info-rgb),var(--bs-bg-opacity))!important}.bg-warning{--bs-bg-opacity:1;background-color:rgba(var(--bs-warning-rgb),var(--bs-bg-opacity))!important}.bg-danger{--bs-bg-opacity:1;background-color:rgba(var(--bs-danger-rgb),var(--bs-bg-opacity))!important}.bg-light{--bs-bg-opacity:1;background-color:rgba(var(--bs-light-rgb),var(--bs-bg-opacity))!important}.bg-dark{--bs-bg-opacity:1;background-color:rgba(var(--bs-dark-rgb),var(--bs-bg-opacity))!important}.bg-black{--bs-bg-opacity:1;background-color:rgba(var(--bs-black-rgb),var(--bs-bg-opacity))!important}.bg-white{--bs-bg-opacity:1;background-color:rgba(var(--bs-white-rgb),var(--bs-bg-opacity))!important}.bg-body{--bs-bg-opacity:1;background-color:rgba(var(--bs-body-bg-rgb),var(--bs-bg-opacity))!important}.bg-transparent{--bs-bg-opacity:1;background-color:transparent!important}.bg-body-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-bg-rgb),var(--bs-bg-opacity))!important}.bg-body-tertiary{--bs-bg-opacity:1;background-color:rgba(var(--bs-tertiary-bg-rgb),var(--bs-bg-opacity))!important}.bg-opacity-10{--bs-bg-opacity:0.1}.bg-opacity-25{--bs-bg-opacity:0.25}.bg-opacity-50{--bs-bg-opacity:0.5}.bg-opacity-75{--bs-bg-opacity:0.75}.bg-opacity-100{--bs-bg-opacity:1}.bg-primary-subtle{background-color:var(--bs-primary-bg-subtle)!important}.bg-secondary-subtle{background-color:var(--bs-secondary-bg-subtle)!important}.bg-success-subtle{background-color:var(--bs-success-bg-subtle)!important}.bg-info-subtle{background-color:var(--bs-info-bg-subtle)!important}.bg-warning-subtle{background-color:var(--bs-warning-bg-subtle)!important}.bg-danger-subtle{background-color:var(--bs-danger-bg-subtle)!important}.bg-light-subtle{background-color:var(--bs-light-bg-subtle)!important}.bg-dark-subtle{background-color:var(--bs-dark-bg-subtle)!important}.bg-gradient{background-image:var(--bs-gradient)!important}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}.pe-none{pointer-events:none!important}.pe-auto{pointer-events:auto!important}.rounded{border-radius:var(--bs-border-radius)!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:var(--bs-border-radius-sm)!important}.rounded-2{border-radius:var(--bs-border-radius)!important}.rounded-3{border-radius:var(--bs-border-radius-lg)!important}.rounded-4{border-radius:var(--bs-border-radius-xl)!important}.rounded-5{border-radius:var(--bs-border-radius-xxl)!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:var(--bs-border-radius-pill)!important}.rounded-top{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important}.rounded-top-0{border-top-left-radius:0!important;border-top-right-radius:0!important}.rounded-top-1{border-top-left-radius:var(--bs-border-radius-sm)!important;border-top-right-radius:var(--bs-border-radius-sm)!important}.rounded-top-2{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important}.rounded-top-3{border-top-left-radius:var(--bs-border-radius-lg)!important;border-top-right-radius:var(--bs-border-radius-lg)!important}.rounded-top-4{border-top-left-radius:var(--bs-border-radius-xl)!important;border-top-right-radius:var(--bs-border-radius-xl)!important}.rounded-top-5{border-top-left-radius:var(--bs-border-radius-xxl)!important;border-top-right-radius:var(--bs-border-radius-xxl)!important}.rounded-top-circle{border-top-left-radius:50%!important;border-top-right-radius:50%!important}.rounded-top-pill{border-top-left-radius:var(--bs-border-radius-pill)!important;border-top-right-radius:var(--bs-border-radius-pill)!important}.rounded-end{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important}.rounded-end-0{border-top-right-radius:0!important;border-bottom-right-radius:0!important}.rounded-end-1{border-top-right-radius:var(--bs-border-radius-sm)!important;border-bottom-right-radius:var(--bs-border-radius-sm)!important}.rounded-end-2{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important}.rounded-end-3{border-top-right-radius:var(--bs-border-radius-lg)!important;border-bottom-right-radius:var(--bs-border-radius-lg)!important}.rounded-end-4{border-top-right-radius:var(--bs-border-radius-xl)!important;border-bottom-right-radius:var(--bs-border-radius-xl)!important}.rounded-end-5{border-top-right-radius:var(--bs-border-radius-xxl)!important;border-bottom-right-radius:var(--bs-border-radius-xxl)!important}.rounded-end-circle{border-top-right-radius:50%!important;border-bottom-right-radius:50%!important}.rounded-end-pill{border-top-right-radius:var(--bs-border-radius-pill)!important;border-bottom-right-radius:var(--bs-border-radius-pill)!important}.rounded-bottom{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}.rounded-bottom-0{border-bottom-right-radius:0!important;border-bottom-left-radius:0!important}.rounded-bottom-1{border-bottom-right-radius:var(--bs-border-radius-sm)!important;border-bottom-left-radius:var(--bs-border-radius-sm)!important}.rounded-bottom-2{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}.rounded-bottom-3{border-bottom-right-radius:var(--bs-border-radius-lg)!important;border-bottom-left-radius:var(--bs-border-radius-lg)!important}.rounded-bottom-4{border-bottom-right-radius:var(--bs-border-radius-xl)!important;border-bottom-left-radius:var(--bs-border-radius-xl)!important}.rounded-bottom-5{border-bottom-right-radius:var(--bs-border-radius-xxl)!important;border-bottom-left-radius:var(--bs-border-radius-xxl)!important}.rounded-bottom-circle{border-bottom-right-radius:50%!important;border-bottom-left-radius:50%!important}.rounded-bottom-pill{border-bottom-right-radius:var(--bs-border-radius-pill)!important;border-bottom-left-radius:var(--bs-border-radius-pill)!important}.rounded-start{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important}.rounded-start-0{border-bottom-left-radius:0!important;border-top-left-radius:0!important}.rounded-start-1{border-bottom-left-radius:var(--bs-border-radius-sm)!important;border-top-left-radius:var(--bs-border-radius-sm)!important}.rounded-start-2{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important}.rounded-start-3{border-bottom-left-radius:var(--bs-border-radius-lg)!important;border-top-left-radius:var(--bs-border-radius-lg)!important}.rounded-start-4{border-bottom-left-radius:var(--bs-border-radius-xl)!important;border-top-left-radius:var(--bs-border-radius-xl)!important}.rounded-start-5{border-bottom-left-radius:var(--bs-border-radius-xxl)!important;border-top-left-radius:var(--bs-border-radius-xxl)!important}.rounded-start-circle{border-bottom-left-radius:50%!important;border-top-left-radius:50%!important}.rounded-start-pill{border-bottom-left-radius:var(--bs-border-radius-pill)!important;border-top-left-radius:var(--bs-border-radius-pill)!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}.z-n1{z-index:-1!important}.z-0{z-index:0!important}.z-1{z-index:1!important}.z-2{z-index:2!important}.z-3{z-index:3!important}@media(min-width:576px){.float-sm-start{float:left!important}.float-sm-end{float:right!important}.float-sm-none{float:none!important}.object-fit-sm-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-sm-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-sm-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-sm-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-sm-none{-o-object-fit:none!important;object-fit:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-inline-grid{display:inline-grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:3rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-right:0!important;margin-left:0!important}.mx-sm-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-sm-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-sm-3{margin-right:1rem!important;margin-left:1rem!important}.mx-sm-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-sm-5{margin-right:3rem!important;margin-left:3rem!important}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-sm-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:3rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.25rem!important}.me-sm-2{margin-right:.5rem!important}.me-sm-3{margin-right:1rem!important}.me-sm-4{margin-right:1.5rem!important}.me-sm-5{margin-right:3rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:3rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.25rem!important}.ms-sm-2{margin-left:.5rem!important}.ms-sm-3{margin-left:1rem!important}.ms-sm-4{margin-left:1.5rem!important}.ms-sm-5{margin-left:3rem!important}.ms-sm-auto{margin-left:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:3rem!important}.px-sm-0{padding-right:0!important;padding-left:0!important}.px-sm-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-sm-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-sm-3{padding-right:1rem!important;padding-left:1rem!important}.px-sm-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-sm-5{padding-right:3rem!important;padding-left:3rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-sm-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:3rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.25rem!important}.pe-sm-2{padding-right:.5rem!important}.pe-sm-3{padding-right:1rem!important}.pe-sm-4{padding-right:1.5rem!important}.pe-sm-5{padding-right:3rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:3rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.25rem!important}.ps-sm-2{padding-left:.5rem!important}.ps-sm-3{padding-left:1rem!important}.ps-sm-4{padding-left:1.5rem!important}.ps-sm-5{padding-left:3rem!important}.gap-sm-0{gap:0!important}.gap-sm-1{gap:.25rem!important}.gap-sm-2{gap:.5rem!important}.gap-sm-3{gap:1rem!important}.gap-sm-4{gap:1.5rem!important}.gap-sm-5{gap:3rem!important}.row-gap-sm-0{row-gap:0!important}.row-gap-sm-1{row-gap:.25rem!important}.row-gap-sm-2{row-gap:.5rem!important}.row-gap-sm-3{row-gap:1rem!important}.row-gap-sm-4{row-gap:1.5rem!important}.row-gap-sm-5{row-gap:3rem!important}.column-gap-sm-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-sm-1{-moz-column-gap:.25rem!important;column-gap:.25rem!important}.column-gap-sm-2{-moz-column-gap:.5rem!important;column-gap:.5rem!important}.column-gap-sm-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-sm-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-sm-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-sm-start{text-align:left!important}.text-sm-end{text-align:right!important}.text-sm-center{text-align:center!important}}@media(min-width:768px){.float-md-start{float:left!important}.float-md-end{float:right!important}.float-md-none{float:none!important}.object-fit-md-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-md-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-md-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-md-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-md-none{-o-object-fit:none!important;object-fit:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-inline-grid{display:inline-grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:3rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-right:0!important;margin-left:0!important}.mx-md-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-md-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-md-3{margin-right:1rem!important;margin-left:1rem!important}.mx-md-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-md-5{margin-right:3rem!important;margin-left:3rem!important}.mx-md-auto{margin-right:auto!important;margin-left:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-md-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:3rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.25rem!important}.me-md-2{margin-right:.5rem!important}.me-md-3{margin-right:1rem!important}.me-md-4{margin-right:1.5rem!important}.me-md-5{margin-right:3rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:3rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.25rem!important}.ms-md-2{margin-left:.5rem!important}.ms-md-3{margin-left:1rem!important}.ms-md-4{margin-left:1.5rem!important}.ms-md-5{margin-left:3rem!important}.ms-md-auto{margin-left:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:3rem!important}.px-md-0{padding-right:0!important;padding-left:0!important}.px-md-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-md-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-md-3{padding-right:1rem!important;padding-left:1rem!important}.px-md-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-md-5{padding-right:3rem!important;padding-left:3rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-md-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:3rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.25rem!important}.pe-md-2{padding-right:.5rem!important}.pe-md-3{padding-right:1rem!important}.pe-md-4{padding-right:1.5rem!important}.pe-md-5{padding-right:3rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:3rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.25rem!important}.ps-md-2{padding-left:.5rem!important}.ps-md-3{padding-left:1rem!important}.ps-md-4{padding-left:1.5rem!important}.ps-md-5{padding-left:3rem!important}.gap-md-0{gap:0!important}.gap-md-1{gap:.25rem!important}.gap-md-2{gap:.5rem!important}.gap-md-3{gap:1rem!important}.gap-md-4{gap:1.5rem!important}.gap-md-5{gap:3rem!important}.row-gap-md-0{row-gap:0!important}.row-gap-md-1{row-gap:.25rem!important}.row-gap-md-2{row-gap:.5rem!important}.row-gap-md-3{row-gap:1rem!important}.row-gap-md-4{row-gap:1.5rem!important}.row-gap-md-5{row-gap:3rem!important}.column-gap-md-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-md-1{-moz-column-gap:.25rem!important;column-gap:.25rem!important}.column-gap-md-2{-moz-column-gap:.5rem!important;column-gap:.5rem!important}.column-gap-md-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-md-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-md-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-md-start{text-align:left!important}.text-md-end{text-align:right!important}.text-md-center{text-align:center!important}}@media(min-width:992px){.float-lg-start{float:left!important}.float-lg-end{float:right!important}.float-lg-none{float:none!important}.object-fit-lg-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-lg-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-lg-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-lg-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-lg-none{-o-object-fit:none!important;object-fit:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block,.td-blog .td-rss-button{display:block!important}.d-lg-grid{display:grid!important}.d-lg-inline-grid{display:inline-grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:3rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-right:0!important;margin-left:0!important}.mx-lg-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-lg-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-lg-3{margin-right:1rem!important;margin-left:1rem!important}.mx-lg-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-lg-5{margin-right:3rem!important;margin-left:3rem!important}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-lg-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:3rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.25rem!important}.me-lg-2{margin-right:.5rem!important}.me-lg-3{margin-right:1rem!important}.me-lg-4{margin-right:1.5rem!important}.me-lg-5{margin-right:3rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:3rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.25rem!important}.ms-lg-2{margin-left:.5rem!important}.ms-lg-3{margin-left:1rem!important}.ms-lg-4{margin-left:1.5rem!important}.ms-lg-5{margin-left:3rem!important}.ms-lg-auto{margin-left:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:3rem!important}.px-lg-0{padding-right:0!important;padding-left:0!important}.px-lg-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-lg-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-lg-3{padding-right:1rem!important;padding-left:1rem!important}.px-lg-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-lg-5{padding-right:3rem!important;padding-left:3rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-lg-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:3rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.25rem!important}.pe-lg-2{padding-right:.5rem!important}.pe-lg-3{padding-right:1rem!important}.pe-lg-4{padding-right:1.5rem!important}.pe-lg-5{padding-right:3rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:3rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.25rem!important}.ps-lg-2{padding-left:.5rem!important}.ps-lg-3{padding-left:1rem!important}.ps-lg-4{padding-left:1.5rem!important}.ps-lg-5{padding-left:3rem!important}.gap-lg-0{gap:0!important}.gap-lg-1{gap:.25rem!important}.gap-lg-2{gap:.5rem!important}.gap-lg-3{gap:1rem!important}.gap-lg-4{gap:1.5rem!important}.gap-lg-5{gap:3rem!important}.row-gap-lg-0{row-gap:0!important}.row-gap-lg-1{row-gap:.25rem!important}.row-gap-lg-2{row-gap:.5rem!important}.row-gap-lg-3{row-gap:1rem!important}.row-gap-lg-4{row-gap:1.5rem!important}.row-gap-lg-5{row-gap:3rem!important}.column-gap-lg-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-lg-1{-moz-column-gap:.25rem!important;column-gap:.25rem!important}.column-gap-lg-2{-moz-column-gap:.5rem!important;column-gap:.5rem!important}.column-gap-lg-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-lg-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-lg-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-lg-start{text-align:left!important}.text-lg-end{text-align:right!important}.text-lg-center{text-align:center!important}}@media(min-width:1200px){.float-xl-start{float:left!important}.float-xl-end{float:right!important}.float-xl-none{float:none!important}.object-fit-xl-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-xl-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-xl-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-xl-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-xl-none{-o-object-fit:none!important;object-fit:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-inline-grid{display:inline-grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:3rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-right:0!important;margin-left:0!important}.mx-xl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:3rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.25rem!important}.me-xl-2{margin-right:.5rem!important}.me-xl-3{margin-right:1rem!important}.me-xl-4{margin-right:1.5rem!important}.me-xl-5{margin-right:3rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:3rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.25rem!important}.ms-xl-2{margin-left:.5rem!important}.ms-xl-3{margin-left:1rem!important}.ms-xl-4{margin-left:1.5rem!important}.ms-xl-5{margin-left:3rem!important}.ms-xl-auto{margin-left:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:3rem!important}.px-xl-0{padding-right:0!important;padding-left:0!important}.px-xl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:3rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.25rem!important}.pe-xl-2{padding-right:.5rem!important}.pe-xl-3{padding-right:1rem!important}.pe-xl-4{padding-right:1.5rem!important}.pe-xl-5{padding-right:3rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:3rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.25rem!important}.ps-xl-2{padding-left:.5rem!important}.ps-xl-3{padding-left:1rem!important}.ps-xl-4{padding-left:1.5rem!important}.ps-xl-5{padding-left:3rem!important}.gap-xl-0{gap:0!important}.gap-xl-1{gap:.25rem!important}.gap-xl-2{gap:.5rem!important}.gap-xl-3{gap:1rem!important}.gap-xl-4{gap:1.5rem!important}.gap-xl-5{gap:3rem!important}.row-gap-xl-0{row-gap:0!important}.row-gap-xl-1{row-gap:.25rem!important}.row-gap-xl-2{row-gap:.5rem!important}.row-gap-xl-3{row-gap:1rem!important}.row-gap-xl-4{row-gap:1.5rem!important}.row-gap-xl-5{row-gap:3rem!important}.column-gap-xl-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-xl-1{-moz-column-gap:.25rem!important;column-gap:.25rem!important}.column-gap-xl-2{-moz-column-gap:.5rem!important;column-gap:.5rem!important}.column-gap-xl-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-xl-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-xl-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-xl-start{text-align:left!important}.text-xl-end{text-align:right!important}.text-xl-center{text-align:center!important}}@media(min-width:1400px){.float-xxl-start{float:left!important}.float-xxl-end{float:right!important}.float-xxl-none{float:none!important}.object-fit-xxl-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-xxl-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-xxl-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-xxl-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-xxl-none{-o-object-fit:none!important;object-fit:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-inline-grid{display:inline-grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:3rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-right:0!important;margin-left:0!important}.mx-xxl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xxl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xxl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xxl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xxl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xxl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:3rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.25rem!important}.me-xxl-2{margin-right:.5rem!important}.me-xxl-3{margin-right:1rem!important}.me-xxl-4{margin-right:1.5rem!important}.me-xxl-5{margin-right:3rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:3rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.25rem!important}.ms-xxl-2{margin-left:.5rem!important}.ms-xxl-3{margin-left:1rem!important}.ms-xxl-4{margin-left:1.5rem!important}.ms-xxl-5{margin-left:3rem!important}.ms-xxl-auto{margin-left:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:3rem!important}.px-xxl-0{padding-right:0!important;padding-left:0!important}.px-xxl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xxl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xxl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xxl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xxl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xxl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:3rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.25rem!important}.pe-xxl-2{padding-right:.5rem!important}.pe-xxl-3{padding-right:1rem!important}.pe-xxl-4{padding-right:1.5rem!important}.pe-xxl-5{padding-right:3rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:3rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.25rem!important}.ps-xxl-2{padding-left:.5rem!important}.ps-xxl-3{padding-left:1rem!important}.ps-xxl-4{padding-left:1.5rem!important}.ps-xxl-5{padding-left:3rem!important}.gap-xxl-0{gap:0!important}.gap-xxl-1{gap:.25rem!important}.gap-xxl-2{gap:.5rem!important}.gap-xxl-3{gap:1rem!important}.gap-xxl-4{gap:1.5rem!important}.gap-xxl-5{gap:3rem!important}.row-gap-xxl-0{row-gap:0!important}.row-gap-xxl-1{row-gap:.25rem!important}.row-gap-xxl-2{row-gap:.5rem!important}.row-gap-xxl-3{row-gap:1rem!important}.row-gap-xxl-4{row-gap:1.5rem!important}.row-gap-xxl-5{row-gap:3rem!important}.column-gap-xxl-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-xxl-1{-moz-column-gap:.25rem!important;column-gap:.25rem!important}.column-gap-xxl-2{-moz-column-gap:.5rem!important;column-gap:.5rem!important}.column-gap-xxl-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-xxl-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-xxl-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-xxl-start{text-align:left!important}.text-xxl-end{text-align:right!important}.text-xxl-center{text-align:center!important}}@media(min-width:1200px){.fs-1{font-size:2.5rem!important}.fs-2{font-size:2rem!important}.fs-3{font-size:1.5rem!important}.fs-4{font-size:1.35rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-inline-grid{display:inline-grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}}/*!* Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com +* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) +* Copyright 2024 Fonticons, Inc.*/.fa,.td-search__icon:before{font-family:var(--fa-style-family,"Font Awesome 6 Free");font-weight:var(--fa-style,900)}.fa,.td-search__icon:before,.fa-classic,.fa-sharp,.fas,.td-offline-search-results__close-button:after,.fa-solid,.far,.fa-regular,.fab,.fa-brands{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:var(--fa-display,inline-block);font-style:normal;font-variant:normal;line-height:1;text-rendering:auto}.fas,.td-offline-search-results__close-button:after,.fa-classic,.fa-solid,.far,.fa-regular{font-family:'font awesome 6 free'}.fab,.fa-brands{font-family:'font awesome 6 brands'}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-2xs{font-size:.625em;line-height:.1em;vertical-align:.225em}.fa-xs{font-size:.75em;line-height:.08333333em;vertical-align:.125em}.fa-sm{font-size:.875em;line-height:.07142857em;vertical-align:.05357143em}.fa-lg{font-size:1.25em;line-height:.05em;vertical-align:-.075em}.fa-xl{font-size:1.5em;line-height:.04166667em;vertical-align:-.125em}.fa-2xl{font-size:2em;line-height:.03125em;vertical-align:-.1875em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:var(--fa-li-margin,2.5em);padding-left:0}.fa-ul>li{position:relative}.fa-li{left:calc(var(--fa-li-width,2em) * -1);position:absolute;text-align:center;width:var(--fa-li-width,2em);line-height:inherit}.fa-border{border-color:var(--fa-border-color,#eee);border-radius:var(--fa-border-radius,.1em);border-style:var(--fa-border-style,solid);border-width:var(--fa-border-width,.08em);padding:var(--fa-border-padding,.2em .25em .15em)}.fa-pull-left{float:left;margin-right:var(--fa-pull-margin,.3em)}.fa-pull-right{float:right;margin-left:var(--fa-pull-margin,.3em)}.fa-beat{animation-name:fa-beat;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-bounce{animation-name:fa-bounce;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1))}.fa-fade{animation-name:fa-fade;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-beat-fade{animation-name:fa-beat-fade;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-flip{animation-name:fa-flip;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-shake{animation-name:fa-shake;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,linear)}.fa-spin{animation-name:fa-spin;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,2s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,linear)}.fa-spin-reverse{--fa-animation-direction:reverse}.fa-pulse,.fa-spin-pulse{animation-name:fa-spin;animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,steps(8))}@media(prefers-reduced-motion:reduce){.fa-beat,.fa-bounce,.fa-fade,.fa-beat-fade,.fa-flip,.fa-pulse,.fa-shake,.fa-spin,.fa-spin-pulse{animation-delay:-1ms;animation-duration:1ms;animation-iteration-count:1;transition-delay:0s;transition-duration:0s}}@keyframes fa-beat{0%,90%{transform:scale(1)}45%{transform:scale(var(--fa-beat-scale,1.25))}}@keyframes fa-bounce{0%{transform:scale(1,1)translateY(0)}10%{transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9))translateY(0)}30%{transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1))translateY(var(--fa-bounce-height,-.5em))}50%{transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95))translateY(0)}57%{transform:scale(1,1)translateY(var(--fa-bounce-rebound,-.125em))}64%{transform:scale(1,1)translateY(0)}100%{transform:scale(1,1)translateY(0)}}@keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@keyframes fa-beat-fade{0%,100%{opacity:var(--fa-beat-fade-opacity,.4);transform:scale(1)}50%{opacity:1;transform:scale(var(--fa-beat-fade-scale,1.125))}}@keyframes fa-flip{50%{transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@keyframes fa-shake{0%{transform:rotate(-15deg)}4%{transform:rotate(15deg)}8%,24%{transform:rotate(-18deg)}12%,28%{transform:rotate(18deg)}16%{transform:rotate(-22deg)}20%{transform:rotate(22deg)}32%{transform:rotate(-12deg)}36%{transform:rotate(12deg)}40%,100%{transform:rotate(0)}}@keyframes fa-spin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}.fa-rotate-90{transform:rotate(90deg)}.fa-rotate-180{transform:rotate(180deg)}.fa-rotate-270{transform:rotate(270deg)}.fa-flip-horizontal{transform:scale(-1,1)}.fa-flip-vertical{transform:scale(1,-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{transform:scale(-1,-1)}.fa-rotate-by{transform:rotate(var(--fa-rotate-angle,0))}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%;z-index:var(--fa-stack-z-index,auto)}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:var(--fa-inverse,#fff)}.fa-0::before{content:"\30"}.fa-1::before{content:"\31"}.fa-2::before{content:"\32"}.fa-3::before{content:"\33"}.fa-4::before{content:"\34"}.fa-5::before{content:"\35"}.fa-6::before{content:"\36"}.fa-7::before{content:"\37"}.fa-8::before{content:"\38"}.fa-9::before{content:"\39"}.fa-fill-drip::before{content:"\f576"}.fa-arrows-to-circle::before{content:"\e4bd"}.fa-circle-chevron-right::before{content:"\f138"}.fa-chevron-circle-right::before{content:"\f138"}.fa-at::before{content:"\40"}.fa-trash-can::before{content:"\f2ed"}.fa-trash-alt::before{content:"\f2ed"}.fa-text-height::before{content:"\f034"}.fa-user-xmark::before{content:"\f235"}.fa-user-times::before{content:"\f235"}.fa-stethoscope::before{content:"\f0f1"}.fa-message::before{content:"\f27a"}.fa-comment-alt::before{content:"\f27a"}.fa-info::before{content:"\f129"}.fa-down-left-and-up-right-to-center::before{content:"\f422"}.fa-compress-alt::before{content:"\f422"}.fa-explosion::before{content:"\e4e9"}.fa-file-lines::before{content:"\f15c"}.fa-file-alt::before{content:"\f15c"}.fa-file-text::before{content:"\f15c"}.fa-wave-square::before{content:"\f83e"}.fa-ring::before{content:"\f70b"}.fa-building-un::before{content:"\e4d9"}.fa-dice-three::before{content:"\f527"}.fa-calendar-days::before{content:"\f073"}.fa-calendar-alt::before{content:"\f073"}.fa-anchor-circle-check::before{content:"\e4aa"}.fa-building-circle-arrow-right::before{content:"\e4d1"}.fa-volleyball::before{content:"\f45f"}.fa-volleyball-ball::before{content:"\f45f"}.fa-arrows-up-to-line::before{content:"\e4c2"}.fa-sort-down::before{content:"\f0dd"}.fa-sort-desc::before{content:"\f0dd"}.fa-circle-minus::before{content:"\f056"}.fa-minus-circle::before{content:"\f056"}.fa-door-open::before{content:"\f52b"}.fa-right-from-bracket::before{content:"\f2f5"}.fa-sign-out-alt::before{content:"\f2f5"}.fa-atom::before{content:"\f5d2"}.fa-soap::before{content:"\e06e"}.fa-icons::before{content:"\f86d"}.fa-heart-music-camera-bolt::before{content:"\f86d"}.fa-microphone-lines-slash::before{content:"\f539"}.fa-microphone-alt-slash::before{content:"\f539"}.fa-bridge-circle-check::before{content:"\e4c9"}.fa-pump-medical::before{content:"\e06a"}.fa-fingerprint::before{content:"\f577"}.fa-hand-point-right::before{content:"\f0a4"}.fa-magnifying-glass-location::before{content:"\f689"}.fa-search-location::before{content:"\f689"}.fa-forward-step::before{content:"\f051"}.fa-step-forward::before{content:"\f051"}.fa-face-smile-beam::before{content:"\f5b8"}.fa-smile-beam::before{content:"\f5b8"}.fa-flag-checkered::before{content:"\f11e"}.fa-football::before{content:"\f44e"}.fa-football-ball::before{content:"\f44e"}.fa-school-circle-exclamation::before{content:"\e56c"}.fa-crop::before{content:"\f125"}.fa-angles-down::before{content:"\f103"}.fa-angle-double-down::before{content:"\f103"}.fa-users-rectangle::before{content:"\e594"}.fa-people-roof::before{content:"\e537"}.fa-people-line::before{content:"\e534"}.fa-beer-mug-empty::before{content:"\f0fc"}.fa-beer::before{content:"\f0fc"}.fa-diagram-predecessor::before{content:"\e477"}.fa-arrow-up-long::before{content:"\f176"}.fa-long-arrow-up::before{content:"\f176"}.fa-fire-flame-simple::before{content:"\f46a"}.fa-burn::before{content:"\f46a"}.fa-person::before{content:"\f183"}.fa-male::before{content:"\f183"}.fa-laptop::before{content:"\f109"}.fa-file-csv::before{content:"\f6dd"}.fa-menorah::before{content:"\f676"}.fa-truck-plane::before{content:"\e58f"}.fa-record-vinyl::before{content:"\f8d9"}.fa-face-grin-stars::before{content:"\f587"}.fa-grin-stars::before{content:"\f587"}.fa-bong::before{content:"\f55c"}.fa-spaghetti-monster-flying::before{content:"\f67b"}.fa-pastafarianism::before{content:"\f67b"}.fa-arrow-down-up-across-line::before{content:"\e4af"}.fa-spoon::before{content:"\f2e5"}.fa-utensil-spoon::before{content:"\f2e5"}.fa-jar-wheat::before{content:"\e517"}.fa-envelopes-bulk::before{content:"\f674"}.fa-mail-bulk::before{content:"\f674"}.fa-file-circle-exclamation::before{content:"\e4eb"}.fa-circle-h::before{content:"\f47e"}.fa-hospital-symbol::before{content:"\f47e"}.fa-pager::before{content:"\f815"}.fa-address-book::before{content:"\f2b9"}.fa-contact-book::before{content:"\f2b9"}.fa-strikethrough::before{content:"\f0cc"}.fa-k::before{content:"\4b"}.fa-landmark-flag::before{content:"\e51c"}.fa-pencil::before{content:"\f303"}.fa-pencil-alt::before{content:"\f303"}.fa-backward::before{content:"\f04a"}.fa-caret-right::before{content:"\f0da"}.fa-comments::before{content:"\f086"}.fa-paste::before{content:"\f0ea"}.fa-file-clipboard::before{content:"\f0ea"}.fa-code-pull-request::before{content:"\e13c"}.fa-clipboard-list::before{content:"\f46d"}.fa-truck-ramp-box::before{content:"\f4de"}.fa-truck-loading::before{content:"\f4de"}.fa-user-check::before{content:"\f4fc"}.fa-vial-virus::before{content:"\e597"}.fa-sheet-plastic::before{content:"\e571"}.fa-blog::before{content:"\f781"}.fa-user-ninja::before{content:"\f504"}.fa-person-arrow-up-from-line::before{content:"\e539"}.fa-scroll-torah::before{content:"\f6a0"}.fa-torah::before{content:"\f6a0"}.fa-broom-ball::before{content:"\f458"}.fa-quidditch::before{content:"\f458"}.fa-quidditch-broom-ball::before{content:"\f458"}.fa-toggle-off::before{content:"\f204"}.fa-box-archive::before{content:"\f187"}.fa-archive::before{content:"\f187"}.fa-person-drowning::before{content:"\e545"}.fa-arrow-down-9-1::before{content:"\f886"}.fa-sort-numeric-desc::before{content:"\f886"}.fa-sort-numeric-down-alt::before{content:"\f886"}.fa-face-grin-tongue-squint::before{content:"\f58a"}.fa-grin-tongue-squint::before{content:"\f58a"}.fa-spray-can::before{content:"\f5bd"}.fa-truck-monster::before{content:"\f63b"}.fa-w::before{content:"\57"}.fa-earth-africa::before{content:"\f57c"}.fa-globe-africa::before{content:"\f57c"}.fa-rainbow::before{content:"\f75b"}.fa-circle-notch::before{content:"\f1ce"}.fa-tablet-screen-button::before{content:"\f3fa"}.fa-tablet-alt::before{content:"\f3fa"}.fa-paw::before{content:"\f1b0"}.fa-cloud::before{content:"\f0c2"}.fa-trowel-bricks::before{content:"\e58a"}.fa-face-flushed::before{content:"\f579"}.fa-flushed::before{content:"\f579"}.fa-hospital-user::before{content:"\f80d"}.fa-tent-arrow-left-right::before{content:"\e57f"}.fa-gavel::before{content:"\f0e3"}.fa-legal::before{content:"\f0e3"}.fa-binoculars::before{content:"\f1e5"}.fa-microphone-slash::before{content:"\f131"}.fa-box-tissue::before{content:"\e05b"}.fa-motorcycle::before{content:"\f21c"}.fa-bell-concierge::before{content:"\f562"}.fa-concierge-bell::before{content:"\f562"}.fa-pen-ruler::before{content:"\f5ae"}.fa-pencil-ruler::before{content:"\f5ae"}.fa-people-arrows::before{content:"\e068"}.fa-people-arrows-left-right::before{content:"\e068"}.fa-mars-and-venus-burst::before{content:"\e523"}.fa-square-caret-right::before{content:"\f152"}.fa-caret-square-right::before{content:"\f152"}.fa-scissors::before{content:"\f0c4"}.fa-cut::before{content:"\f0c4"}.fa-sun-plant-wilt::before{content:"\e57a"}.fa-toilets-portable::before{content:"\e584"}.fa-hockey-puck::before{content:"\f453"}.fa-table::before{content:"\f0ce"}.fa-magnifying-glass-arrow-right::before{content:"\e521"}.fa-tachograph-digital::before{content:"\f566"}.fa-digital-tachograph::before{content:"\f566"}.fa-users-slash::before{content:"\e073"}.fa-clover::before{content:"\e139"}.fa-reply::before{content:"\f3e5"}.fa-mail-reply::before{content:"\f3e5"}.fa-star-and-crescent::before{content:"\f699"}.fa-house-fire::before{content:"\e50c"}.fa-square-minus::before{content:"\f146"}.fa-minus-square::before{content:"\f146"}.fa-helicopter::before{content:"\f533"}.fa-compass::before{content:"\f14e"}.fa-square-caret-down::before{content:"\f150"}.fa-caret-square-down::before{content:"\f150"}.fa-file-circle-question::before{content:"\e4ef"}.fa-laptop-code::before{content:"\f5fc"}.fa-swatchbook::before{content:"\f5c3"}.fa-prescription-bottle::before{content:"\f485"}.fa-bars::before{content:"\f0c9"}.fa-navicon::before{content:"\f0c9"}.fa-people-group::before{content:"\e533"}.fa-hourglass-end::before{content:"\f253"}.fa-hourglass-3::before{content:"\f253"}.fa-heart-crack::before{content:"\f7a9"}.fa-heart-broken::before{content:"\f7a9"}.fa-square-up-right::before{content:"\f360"}.fa-external-link-square-alt::before{content:"\f360"}.fa-face-kiss-beam::before{content:"\f597"}.fa-kiss-beam::before{content:"\f597"}.fa-film::before{content:"\f008"}.fa-ruler-horizontal::before{content:"\f547"}.fa-people-robbery::before{content:"\e536"}.fa-lightbulb::before{content:"\f0eb"}.fa-caret-left::before{content:"\f0d9"}.fa-circle-exclamation::before{content:"\f06a"}.fa-exclamation-circle::before{content:"\f06a"}.fa-school-circle-xmark::before{content:"\e56d"}.fa-arrow-right-from-bracket::before{content:"\f08b"}.fa-sign-out::before{content:"\f08b"}.fa-circle-chevron-down::before{content:"\f13a"}.fa-chevron-circle-down::before{content:"\f13a"}.fa-unlock-keyhole::before{content:"\f13e"}.fa-unlock-alt::before{content:"\f13e"}.fa-cloud-showers-heavy::before{content:"\f740"}.fa-headphones-simple::before{content:"\f58f"}.fa-headphones-alt::before{content:"\f58f"}.fa-sitemap::before{content:"\f0e8"}.fa-circle-dollar-to-slot::before{content:"\f4b9"}.fa-donate::before{content:"\f4b9"}.fa-memory::before{content:"\f538"}.fa-road-spikes::before{content:"\e568"}.fa-fire-burner::before{content:"\e4f1"}.fa-flag::before{content:"\f024"}.fa-hanukiah::before{content:"\f6e6"}.fa-feather::before{content:"\f52d"}.fa-volume-low::before{content:"\f027"}.fa-volume-down::before{content:"\f027"}.fa-comment-slash::before{content:"\f4b3"}.fa-cloud-sun-rain::before{content:"\f743"}.fa-compress::before{content:"\f066"}.fa-wheat-awn::before{content:"\e2cd"}.fa-wheat-alt::before{content:"\e2cd"}.fa-ankh::before{content:"\f644"}.fa-hands-holding-child::before{content:"\e4fa"}.fa-asterisk::before{content:"\2a"}.fa-square-check::before{content:"\f14a"}.fa-check-square::before{content:"\f14a"}.fa-peseta-sign::before{content:"\e221"}.fa-heading::before{content:"\f1dc"}.fa-header::before{content:"\f1dc"}.fa-ghost::before{content:"\f6e2"}.fa-list::before{content:"\f03a"}.fa-list-squares::before{content:"\f03a"}.fa-square-phone-flip::before{content:"\f87b"}.fa-phone-square-alt::before{content:"\f87b"}.fa-cart-plus::before{content:"\f217"}.fa-gamepad::before{content:"\f11b"}.fa-circle-dot::before{content:"\f192"}.fa-dot-circle::before{content:"\f192"}.fa-face-dizzy::before{content:"\f567"}.fa-dizzy::before{content:"\f567"}.fa-egg::before{content:"\f7fb"}.fa-house-medical-circle-xmark::before{content:"\e513"}.fa-campground::before{content:"\f6bb"}.fa-folder-plus::before{content:"\f65e"}.fa-futbol::before{content:"\f1e3"}.fa-futbol-ball::before{content:"\f1e3"}.fa-soccer-ball::before{content:"\f1e3"}.fa-paintbrush::before{content:"\f1fc"}.fa-paint-brush::before{content:"\f1fc"}.fa-lock::before{content:"\f023"}.fa-gas-pump::before{content:"\f52f"}.fa-hot-tub-person::before{content:"\f593"}.fa-hot-tub::before{content:"\f593"}.fa-map-location::before{content:"\f59f"}.fa-map-marked::before{content:"\f59f"}.fa-house-flood-water::before{content:"\e50e"}.fa-tree::before{content:"\f1bb"}.fa-bridge-lock::before{content:"\e4cc"}.fa-sack-dollar::before{content:"\f81d"}.fa-pen-to-square::before{content:"\f044"}.fa-edit::before{content:"\f044"}.fa-car-side::before{content:"\f5e4"}.fa-share-nodes::before{content:"\f1e0"}.fa-share-alt::before{content:"\f1e0"}.fa-heart-circle-minus::before{content:"\e4ff"}.fa-hourglass-half::before{content:"\f252"}.fa-hourglass-2::before{content:"\f252"}.fa-microscope::before{content:"\f610"}.fa-sink::before{content:"\e06d"}.fa-bag-shopping::before{content:"\f290"}.fa-shopping-bag::before{content:"\f290"}.fa-arrow-down-z-a::before{content:"\f881"}.fa-sort-alpha-desc::before{content:"\f881"}.fa-sort-alpha-down-alt::before{content:"\f881"}.fa-mitten::before{content:"\f7b5"}.fa-person-rays::before{content:"\e54d"}.fa-users::before{content:"\f0c0"}.fa-eye-slash::before{content:"\f070"}.fa-flask-vial::before{content:"\e4f3"}.fa-hand::before{content:"\f256"}.fa-hand-paper::before{content:"\f256"}.fa-om::before{content:"\f679"}.fa-worm::before{content:"\e599"}.fa-house-circle-xmark::before{content:"\e50b"}.fa-plug::before{content:"\f1e6"}.fa-chevron-up::before{content:"\f077"}.fa-hand-spock::before{content:"\f259"}.fa-stopwatch::before{content:"\f2f2"}.fa-face-kiss::before{content:"\f596"}.fa-kiss::before{content:"\f596"}.fa-bridge-circle-xmark::before{content:"\e4cb"}.fa-face-grin-tongue::before{content:"\f589"}.fa-grin-tongue::before{content:"\f589"}.fa-chess-bishop::before{content:"\f43a"}.fa-face-grin-wink::before{content:"\f58c"}.fa-grin-wink::before{content:"\f58c"}.fa-ear-deaf::before{content:"\f2a4"}.fa-deaf::before{content:"\f2a4"}.fa-deafness::before{content:"\f2a4"}.fa-hard-of-hearing::before{content:"\f2a4"}.fa-road-circle-check::before{content:"\e564"}.fa-dice-five::before{content:"\f523"}.fa-square-rss::before{content:"\f143"}.fa-rss-square::before{content:"\f143"}.fa-land-mine-on::before{content:"\e51b"}.fa-i-cursor::before{content:"\f246"}.fa-stamp::before{content:"\f5bf"}.fa-stairs::before{content:"\e289"}.fa-i::before{content:"\49"}.fa-hryvnia-sign::before{content:"\f6f2"}.fa-hryvnia::before{content:"\f6f2"}.fa-pills::before{content:"\f484"}.fa-face-grin-wide::before{content:"\f581"}.fa-grin-alt::before{content:"\f581"}.fa-tooth::before{content:"\f5c9"}.fa-v::before{content:"\56"}.fa-bangladeshi-taka-sign::before{content:"\e2e6"}.fa-bicycle::before{content:"\f206"}.fa-staff-snake::before{content:"\e579"}.fa-rod-asclepius::before{content:"\e579"}.fa-rod-snake::before{content:"\e579"}.fa-staff-aesculapius::before{content:"\e579"}.fa-head-side-cough-slash::before{content:"\e062"}.fa-truck-medical::before{content:"\f0f9"}.fa-ambulance::before{content:"\f0f9"}.fa-wheat-awn-circle-exclamation::before{content:"\e598"}.fa-snowman::before{content:"\f7d0"}.fa-mortar-pestle::before{content:"\f5a7"}.fa-road-barrier::before{content:"\e562"}.fa-school::before{content:"\f549"}.fa-igloo::before{content:"\f7ae"}.fa-joint::before{content:"\f595"}.fa-angle-right::before{content:"\f105"}.fa-horse::before{content:"\f6f0"}.fa-q::before{content:"\51"}.fa-g::before{content:"\47"}.fa-notes-medical::before{content:"\f481"}.fa-temperature-half::before{content:"\f2c9"}.fa-temperature-2::before{content:"\f2c9"}.fa-thermometer-2::before{content:"\f2c9"}.fa-thermometer-half::before{content:"\f2c9"}.fa-dong-sign::before{content:"\e169"}.fa-capsules::before{content:"\f46b"}.fa-poo-storm::before{content:"\f75a"}.fa-poo-bolt::before{content:"\f75a"}.fa-face-frown-open::before{content:"\f57a"}.fa-frown-open::before{content:"\f57a"}.fa-hand-point-up::before{content:"\f0a6"}.fa-money-bill::before{content:"\f0d6"}.fa-bookmark::before{content:"\f02e"}.fa-align-justify::before{content:"\f039"}.fa-umbrella-beach::before{content:"\f5ca"}.fa-helmet-un::before{content:"\e503"}.fa-bullseye::before{content:"\f140"}.fa-bacon::before{content:"\f7e5"}.fa-hand-point-down::before{content:"\f0a7"}.fa-arrow-up-from-bracket::before{content:"\e09a"}.fa-folder::before{content:"\f07b"}.fa-folder-blank::before{content:"\f07b"}.fa-file-waveform::before{content:"\f478"}.fa-file-medical-alt::before{content:"\f478"}.fa-radiation::before{content:"\f7b9"}.fa-chart-simple::before{content:"\e473"}.fa-mars-stroke::before{content:"\f229"}.fa-vial::before{content:"\f492"}.fa-gauge::before{content:"\f624"}.fa-dashboard::before{content:"\f624"}.fa-gauge-med::before{content:"\f624"}.fa-tachometer-alt-average::before{content:"\f624"}.fa-wand-magic-sparkles::before{content:"\e2ca"}.fa-magic-wand-sparkles::before{content:"\e2ca"}.fa-e::before{content:"\45"}.fa-pen-clip::before{content:"\f305"}.fa-pen-alt::before{content:"\f305"}.fa-bridge-circle-exclamation::before{content:"\e4ca"}.fa-user::before{content:"\f007"}.fa-school-circle-check::before{content:"\e56b"}.fa-dumpster::before{content:"\f793"}.fa-van-shuttle::before{content:"\f5b6"}.fa-shuttle-van::before{content:"\f5b6"}.fa-building-user::before{content:"\e4da"}.fa-square-caret-left::before{content:"\f191"}.fa-caret-square-left::before{content:"\f191"}.fa-highlighter::before{content:"\f591"}.fa-key::before{content:"\f084"}.fa-bullhorn::before{content:"\f0a1"}.fa-globe::before{content:"\f0ac"}.fa-synagogue::before{content:"\f69b"}.fa-person-half-dress::before{content:"\e548"}.fa-road-bridge::before{content:"\e563"}.fa-location-arrow::before{content:"\f124"}.fa-c::before{content:"\43"}.fa-tablet-button::before{content:"\f10a"}.fa-building-lock::before{content:"\e4d6"}.fa-pizza-slice::before{content:"\f818"}.fa-money-bill-wave::before{content:"\f53a"}.fa-chart-area::before{content:"\f1fe"}.fa-area-chart::before{content:"\f1fe"}.fa-house-flag::before{content:"\e50d"}.fa-person-circle-minus::before{content:"\e540"}.fa-ban::before{content:"\f05e"}.fa-cancel::before{content:"\f05e"}.fa-camera-rotate::before{content:"\e0d8"}.fa-spray-can-sparkles::before{content:"\f5d0"}.fa-air-freshener::before{content:"\f5d0"}.fa-star::before{content:"\f005"}.fa-repeat::before{content:"\f363"}.fa-cross::before{content:"\f654"}.fa-box::before{content:"\f466"}.fa-venus-mars::before{content:"\f228"}.fa-arrow-pointer::before{content:"\f245"}.fa-mouse-pointer::before{content:"\f245"}.fa-maximize::before{content:"\f31e"}.fa-expand-arrows-alt::before{content:"\f31e"}.fa-charging-station::before{content:"\f5e7"}.fa-shapes::before{content:"\f61f"}.fa-triangle-circle-square::before{content:"\f61f"}.fa-shuffle::before{content:"\f074"}.fa-random::before{content:"\f074"}.fa-person-running::before{content:"\f70c"}.fa-running::before{content:"\f70c"}.fa-mobile-retro::before{content:"\e527"}.fa-grip-lines-vertical::before{content:"\f7a5"}.fa-spider::before{content:"\f717"}.fa-hands-bound::before{content:"\e4f9"}.fa-file-invoice-dollar::before{content:"\f571"}.fa-plane-circle-exclamation::before{content:"\e556"}.fa-x-ray::before{content:"\f497"}.fa-spell-check::before{content:"\f891"}.fa-slash::before{content:"\f715"}.fa-computer-mouse::before{content:"\f8cc"}.fa-mouse::before{content:"\f8cc"}.fa-arrow-right-to-bracket::before{content:"\f090"}.fa-sign-in::before{content:"\f090"}.fa-shop-slash::before{content:"\e070"}.fa-store-alt-slash::before{content:"\e070"}.fa-server::before{content:"\f233"}.fa-virus-covid-slash::before{content:"\e4a9"}.fa-shop-lock::before{content:"\e4a5"}.fa-hourglass-start::before{content:"\f251"}.fa-hourglass-1::before{content:"\f251"}.fa-blender-phone::before{content:"\f6b6"}.fa-building-wheat::before{content:"\e4db"}.fa-person-breastfeeding::before{content:"\e53a"}.fa-right-to-bracket::before{content:"\f2f6"}.fa-sign-in-alt::before{content:"\f2f6"}.fa-venus::before{content:"\f221"}.fa-passport::before{content:"\f5ab"}.fa-heart-pulse::before{content:"\f21e"}.fa-heartbeat::before{content:"\f21e"}.fa-people-carry-box::before{content:"\f4ce"}.fa-people-carry::before{content:"\f4ce"}.fa-temperature-high::before{content:"\f769"}.fa-microchip::before{content:"\f2db"}.fa-crown::before{content:"\f521"}.fa-weight-hanging::before{content:"\f5cd"}.fa-xmarks-lines::before{content:"\e59a"}.fa-file-prescription::before{content:"\f572"}.fa-weight-scale::before{content:"\f496"}.fa-weight::before{content:"\f496"}.fa-user-group::before{content:"\f500"}.fa-user-friends::before{content:"\f500"}.fa-arrow-up-a-z::before{content:"\f15e"}.fa-sort-alpha-up::before{content:"\f15e"}.fa-chess-knight::before{content:"\f441"}.fa-face-laugh-squint::before{content:"\f59b"}.fa-laugh-squint::before{content:"\f59b"}.fa-wheelchair::before{content:"\f193"}.fa-circle-arrow-up::before{content:"\f0aa"}.fa-arrow-circle-up::before{content:"\f0aa"}.fa-toggle-on::before{content:"\f205"}.fa-person-walking::before{content:"\f554"}.fa-walking::before{content:"\f554"}.fa-l::before{content:"\4c"}.fa-fire::before{content:"\f06d"}.fa-bed-pulse::before{content:"\f487"}.fa-procedures::before{content:"\f487"}.fa-shuttle-space::before{content:"\f197"}.fa-space-shuttle::before{content:"\f197"}.fa-face-laugh::before{content:"\f599"}.fa-laugh::before{content:"\f599"}.fa-folder-open::before{content:"\f07c"}.fa-heart-circle-plus::before{content:"\e500"}.fa-code-fork::before{content:"\e13b"}.fa-city::before{content:"\f64f"}.fa-microphone-lines::before{content:"\f3c9"}.fa-microphone-alt::before{content:"\f3c9"}.fa-pepper-hot::before{content:"\f816"}.fa-unlock::before{content:"\f09c"}.fa-colon-sign::before{content:"\e140"}.fa-headset::before{content:"\f590"}.fa-store-slash::before{content:"\e071"}.fa-road-circle-xmark::before{content:"\e566"}.fa-user-minus::before{content:"\f503"}.fa-mars-stroke-up::before{content:"\f22a"}.fa-mars-stroke-v::before{content:"\f22a"}.fa-champagne-glasses::before{content:"\f79f"}.fa-glass-cheers::before{content:"\f79f"}.fa-clipboard::before{content:"\f328"}.fa-house-circle-exclamation::before{content:"\e50a"}.fa-file-arrow-up::before{content:"\f574"}.fa-file-upload::before{content:"\f574"}.fa-wifi::before{content:"\f1eb"}.fa-wifi-3::before{content:"\f1eb"}.fa-wifi-strong::before{content:"\f1eb"}.fa-bath::before{content:"\f2cd"}.fa-bathtub::before{content:"\f2cd"}.fa-underline::before{content:"\f0cd"}.fa-user-pen::before{content:"\f4ff"}.fa-user-edit::before{content:"\f4ff"}.fa-signature::before{content:"\f5b7"}.fa-stroopwafel::before{content:"\f551"}.fa-bold::before{content:"\f032"}.fa-anchor-lock::before{content:"\e4ad"}.fa-building-ngo::before{content:"\e4d7"}.fa-manat-sign::before{content:"\e1d5"}.fa-not-equal::before{content:"\f53e"}.fa-border-top-left::before{content:"\f853"}.fa-border-style::before{content:"\f853"}.fa-map-location-dot::before{content:"\f5a0"}.fa-map-marked-alt::before{content:"\f5a0"}.fa-jedi::before{content:"\f669"}.fa-square-poll-vertical::before{content:"\f681"}.fa-poll::before{content:"\f681"}.fa-mug-hot::before{content:"\f7b6"}.fa-car-battery::before{content:"\f5df"}.fa-battery-car::before{content:"\f5df"}.fa-gift::before{content:"\f06b"}.fa-dice-two::before{content:"\f528"}.fa-chess-queen::before{content:"\f445"}.fa-glasses::before{content:"\f530"}.fa-chess-board::before{content:"\f43c"}.fa-building-circle-check::before{content:"\e4d2"}.fa-person-chalkboard::before{content:"\e53d"}.fa-mars-stroke-right::before{content:"\f22b"}.fa-mars-stroke-h::before{content:"\f22b"}.fa-hand-back-fist::before{content:"\f255"}.fa-hand-rock::before{content:"\f255"}.fa-square-caret-up::before{content:"\f151"}.fa-caret-square-up::before{content:"\f151"}.fa-cloud-showers-water::before{content:"\e4e4"}.fa-chart-bar::before{content:"\f080"}.fa-bar-chart::before{content:"\f080"}.fa-hands-bubbles::before{content:"\e05e"}.fa-hands-wash::before{content:"\e05e"}.fa-less-than-equal::before{content:"\f537"}.fa-train::before{content:"\f238"}.fa-eye-low-vision::before{content:"\f2a8"}.fa-low-vision::before{content:"\f2a8"}.fa-crow::before{content:"\f520"}.fa-sailboat::before{content:"\e445"}.fa-window-restore::before{content:"\f2d2"}.fa-square-plus::before{content:"\f0fe"}.fa-plus-square::before{content:"\f0fe"}.fa-torii-gate::before{content:"\f6a1"}.fa-frog::before{content:"\f52e"}.fa-bucket::before{content:"\e4cf"}.fa-image::before{content:"\f03e"}.fa-microphone::before{content:"\f130"}.fa-cow::before{content:"\f6c8"}.fa-caret-up::before{content:"\f0d8"}.fa-screwdriver::before{content:"\f54a"}.fa-folder-closed::before{content:"\e185"}.fa-house-tsunami::before{content:"\e515"}.fa-square-nfi::before{content:"\e576"}.fa-arrow-up-from-ground-water::before{content:"\e4b5"}.fa-martini-glass::before{content:"\f57b"}.fa-glass-martini-alt::before{content:"\f57b"}.fa-rotate-left::before{content:"\f2ea"}.fa-rotate-back::before{content:"\f2ea"}.fa-rotate-backward::before{content:"\f2ea"}.fa-undo-alt::before{content:"\f2ea"}.fa-table-columns::before{content:"\f0db"}.fa-columns::before{content:"\f0db"}.fa-lemon::before{content:"\f094"}.fa-head-side-mask::before{content:"\e063"}.fa-handshake::before{content:"\f2b5"}.fa-gem::before{content:"\f3a5"}.fa-dolly::before{content:"\f472"}.fa-dolly-box::before{content:"\f472"}.fa-smoking::before{content:"\f48d"}.fa-minimize::before{content:"\f78c"}.fa-compress-arrows-alt::before{content:"\f78c"}.fa-monument::before{content:"\f5a6"}.fa-snowplow::before{content:"\f7d2"}.fa-angles-right::before{content:"\f101"}.fa-angle-double-right::before{content:"\f101"}.fa-cannabis::before{content:"\f55f"}.fa-circle-play::before{content:"\f144"}.fa-play-circle::before{content:"\f144"}.fa-tablets::before{content:"\f490"}.fa-ethernet::before{content:"\f796"}.fa-euro-sign::before{content:"\f153"}.fa-eur::before{content:"\f153"}.fa-euro::before{content:"\f153"}.fa-chair::before{content:"\f6c0"}.fa-circle-check::before{content:"\f058"}.fa-check-circle::before{content:"\f058"}.fa-circle-stop::before{content:"\f28d"}.fa-stop-circle::before{content:"\f28d"}.fa-compass-drafting::before{content:"\f568"}.fa-drafting-compass::before{content:"\f568"}.fa-plate-wheat::before{content:"\e55a"}.fa-icicles::before{content:"\f7ad"}.fa-person-shelter::before{content:"\e54f"}.fa-neuter::before{content:"\f22c"}.fa-id-badge::before{content:"\f2c1"}.fa-marker::before{content:"\f5a1"}.fa-face-laugh-beam::before{content:"\f59a"}.fa-laugh-beam::before{content:"\f59a"}.fa-helicopter-symbol::before{content:"\e502"}.fa-universal-access::before{content:"\f29a"}.fa-circle-chevron-up::before{content:"\f139"}.fa-chevron-circle-up::before{content:"\f139"}.fa-lari-sign::before{content:"\e1c8"}.fa-volcano::before{content:"\f770"}.fa-person-walking-dashed-line-arrow-right::before{content:"\e553"}.fa-sterling-sign::before{content:"\f154"}.fa-gbp::before{content:"\f154"}.fa-pound-sign::before{content:"\f154"}.fa-viruses::before{content:"\e076"}.fa-square-person-confined::before{content:"\e577"}.fa-user-tie::before{content:"\f508"}.fa-arrow-down-long::before{content:"\f175"}.fa-long-arrow-down::before{content:"\f175"}.fa-tent-arrow-down-to-line::before{content:"\e57e"}.fa-certificate::before{content:"\f0a3"}.fa-reply-all::before{content:"\f122"}.fa-mail-reply-all::before{content:"\f122"}.fa-suitcase::before{content:"\f0f2"}.fa-person-skating::before{content:"\f7c5"}.fa-skating::before{content:"\f7c5"}.fa-filter-circle-dollar::before{content:"\f662"}.fa-funnel-dollar::before{content:"\f662"}.fa-camera-retro::before{content:"\f083"}.fa-circle-arrow-down::before{content:"\f0ab"}.fa-arrow-circle-down::before{content:"\f0ab"}.fa-file-import::before{content:"\f56f"}.fa-arrow-right-to-file::before{content:"\f56f"}.fa-square-arrow-up-right::before{content:"\f14c"}.fa-external-link-square::before{content:"\f14c"}.fa-box-open::before{content:"\f49e"}.fa-scroll::before{content:"\f70e"}.fa-spa::before{content:"\f5bb"}.fa-location-pin-lock::before{content:"\e51f"}.fa-pause::before{content:"\f04c"}.fa-hill-avalanche::before{content:"\e507"}.fa-temperature-empty::before{content:"\f2cb"}.fa-temperature-0::before{content:"\f2cb"}.fa-thermometer-0::before{content:"\f2cb"}.fa-thermometer-empty::before{content:"\f2cb"}.fa-bomb::before{content:"\f1e2"}.fa-registered::before{content:"\f25d"}.fa-address-card::before{content:"\f2bb"}.fa-contact-card::before{content:"\f2bb"}.fa-vcard::before{content:"\f2bb"}.fa-scale-unbalanced-flip::before{content:"\f516"}.fa-balance-scale-right::before{content:"\f516"}.fa-subscript::before{content:"\f12c"}.fa-diamond-turn-right::before{content:"\f5eb"}.fa-directions::before{content:"\f5eb"}.fa-burst::before{content:"\e4dc"}.fa-house-laptop::before{content:"\e066"}.fa-laptop-house::before{content:"\e066"}.fa-face-tired::before{content:"\f5c8"}.fa-tired::before{content:"\f5c8"}.fa-money-bills::before{content:"\e1f3"}.fa-smog::before{content:"\f75f"}.fa-crutch::before{content:"\f7f7"}.fa-cloud-arrow-up::before{content:"\f0ee"}.fa-cloud-upload::before{content:"\f0ee"}.fa-cloud-upload-alt::before{content:"\f0ee"}.fa-palette::before{content:"\f53f"}.fa-arrows-turn-right::before{content:"\e4c0"}.fa-vest::before{content:"\e085"}.fa-ferry::before{content:"\e4ea"}.fa-arrows-down-to-people::before{content:"\e4b9"}.fa-seedling::before{content:"\f4d8"}.fa-sprout::before{content:"\f4d8"}.fa-left-right::before{content:"\f337"}.fa-arrows-alt-h::before{content:"\f337"}.fa-boxes-packing::before{content:"\e4c7"}.fa-circle-arrow-left::before{content:"\f0a8"}.fa-arrow-circle-left::before{content:"\f0a8"}.fa-group-arrows-rotate::before{content:"\e4f6"}.fa-bowl-food::before{content:"\e4c6"}.fa-candy-cane::before{content:"\f786"}.fa-arrow-down-wide-short::before{content:"\f160"}.fa-sort-amount-asc::before{content:"\f160"}.fa-sort-amount-down::before{content:"\f160"}.fa-cloud-bolt::before{content:"\f76c"}.fa-thunderstorm::before{content:"\f76c"}.fa-text-slash::before{content:"\f87d"}.fa-remove-format::before{content:"\f87d"}.fa-face-smile-wink::before{content:"\f4da"}.fa-smile-wink::before{content:"\f4da"}.fa-file-word::before{content:"\f1c2"}.fa-file-powerpoint::before{content:"\f1c4"}.fa-arrows-left-right::before{content:"\f07e"}.fa-arrows-h::before{content:"\f07e"}.fa-house-lock::before{content:"\e510"}.fa-cloud-arrow-down::before{content:"\f0ed"}.fa-cloud-download::before{content:"\f0ed"}.fa-cloud-download-alt::before{content:"\f0ed"}.fa-children::before{content:"\e4e1"}.fa-chalkboard::before{content:"\f51b"}.fa-blackboard::before{content:"\f51b"}.fa-user-large-slash::before{content:"\f4fa"}.fa-user-alt-slash::before{content:"\f4fa"}.fa-envelope-open::before{content:"\f2b6"}.fa-handshake-simple-slash::before{content:"\e05f"}.fa-handshake-alt-slash::before{content:"\e05f"}.fa-mattress-pillow::before{content:"\e525"}.fa-guarani-sign::before{content:"\e19a"}.fa-arrows-rotate::before{content:"\f021"}.fa-refresh::before{content:"\f021"}.fa-sync::before{content:"\f021"}.fa-fire-extinguisher::before{content:"\f134"}.fa-cruzeiro-sign::before{content:"\e152"}.fa-greater-than-equal::before{content:"\f532"}.fa-shield-halved::before{content:"\f3ed"}.fa-shield-alt::before{content:"\f3ed"}.fa-book-atlas::before{content:"\f558"}.fa-atlas::before{content:"\f558"}.fa-virus::before{content:"\e074"}.fa-envelope-circle-check::before{content:"\e4e8"}.fa-layer-group::before{content:"\f5fd"}.fa-arrows-to-dot::before{content:"\e4be"}.fa-archway::before{content:"\f557"}.fa-heart-circle-check::before{content:"\e4fd"}.fa-house-chimney-crack::before{content:"\f6f1"}.fa-house-damage::before{content:"\f6f1"}.fa-file-zipper::before{content:"\f1c6"}.fa-file-archive::before{content:"\f1c6"}.fa-square::before{content:"\f0c8"}.fa-martini-glass-empty::before{content:"\f000"}.fa-glass-martini::before{content:"\f000"}.fa-couch::before{content:"\f4b8"}.fa-cedi-sign::before{content:"\e0df"}.fa-italic::before{content:"\f033"}.fa-table-cells-column-lock::before{content:"\e678"}.fa-church::before{content:"\f51d"}.fa-comments-dollar::before{content:"\f653"}.fa-democrat::before{content:"\f747"}.fa-z::before{content:"\5a"}.fa-person-skiing::before{content:"\f7c9"}.fa-skiing::before{content:"\f7c9"}.fa-road-lock::before{content:"\e567"}.fa-a::before{content:"\41"}.fa-temperature-arrow-down::before{content:"\e03f"}.fa-temperature-down::before{content:"\e03f"}.fa-feather-pointed::before{content:"\f56b"}.fa-feather-alt::before{content:"\f56b"}.fa-p::before{content:"\50"}.fa-snowflake::before{content:"\f2dc"}.fa-newspaper::before{content:"\f1ea"}.fa-rectangle-ad::before{content:"\f641"}.fa-ad::before{content:"\f641"}.fa-circle-arrow-right::before{content:"\f0a9"}.fa-arrow-circle-right::before{content:"\f0a9"}.fa-filter-circle-xmark::before{content:"\e17b"}.fa-locust::before{content:"\e520"}.fa-sort::before{content:"\f0dc"}.fa-unsorted::before{content:"\f0dc"}.fa-list-ol::before{content:"\f0cb"}.fa-list-1-2::before{content:"\f0cb"}.fa-list-numeric::before{content:"\f0cb"}.fa-person-dress-burst::before{content:"\e544"}.fa-money-check-dollar::before{content:"\f53d"}.fa-money-check-alt::before{content:"\f53d"}.fa-vector-square::before{content:"\f5cb"}.fa-bread-slice::before{content:"\f7ec"}.fa-language::before{content:"\f1ab"}.fa-face-kiss-wink-heart::before{content:"\f598"}.fa-kiss-wink-heart::before{content:"\f598"}.fa-filter::before{content:"\f0b0"}.fa-question::before{content:"\3f"}.fa-file-signature::before{content:"\f573"}.fa-up-down-left-right::before{content:"\f0b2"}.fa-arrows-alt::before{content:"\f0b2"}.fa-house-chimney-user::before{content:"\e065"}.fa-hand-holding-heart::before{content:"\f4be"}.fa-puzzle-piece::before{content:"\f12e"}.fa-money-check::before{content:"\f53c"}.fa-star-half-stroke::before{content:"\f5c0"}.fa-star-half-alt::before{content:"\f5c0"}.fa-code::before{content:"\f121"}.fa-whiskey-glass::before{content:"\f7a0"}.fa-glass-whiskey::before{content:"\f7a0"}.fa-building-circle-exclamation::before{content:"\e4d3"}.fa-magnifying-glass-chart::before{content:"\e522"}.fa-arrow-up-right-from-square::before{content:"\f08e"}.fa-external-link::before{content:"\f08e"}.fa-cubes-stacked::before{content:"\e4e6"}.fa-won-sign::before{content:"\f159"}.fa-krw::before{content:"\f159"}.fa-won::before{content:"\f159"}.fa-virus-covid::before{content:"\e4a8"}.fa-austral-sign::before{content:"\e0a9"}.fa-f::before{content:"\46"}.fa-leaf::before{content:"\f06c"}.fa-road::before{content:"\f018"}.fa-taxi::before{content:"\f1ba"}.fa-cab::before{content:"\f1ba"}.fa-person-circle-plus::before{content:"\e541"}.fa-chart-pie::before{content:"\f200"}.fa-pie-chart::before{content:"\f200"}.fa-bolt-lightning::before{content:"\e0b7"}.fa-sack-xmark::before{content:"\e56a"}.fa-file-excel::before{content:"\f1c3"}.fa-file-contract::before{content:"\f56c"}.fa-fish-fins::before{content:"\e4f2"}.fa-building-flag::before{content:"\e4d5"}.fa-face-grin-beam::before{content:"\f582"}.fa-grin-beam::before{content:"\f582"}.fa-object-ungroup::before{content:"\f248"}.fa-poop::before{content:"\f619"}.fa-location-pin::before{content:"\f041"}.fa-map-marker::before{content:"\f041"}.fa-kaaba::before{content:"\f66b"}.fa-toilet-paper::before{content:"\f71e"}.fa-helmet-safety::before{content:"\f807"}.fa-hard-hat::before{content:"\f807"}.fa-hat-hard::before{content:"\f807"}.fa-eject::before{content:"\f052"}.fa-circle-right::before{content:"\f35a"}.fa-arrow-alt-circle-right::before{content:"\f35a"}.fa-plane-circle-check::before{content:"\e555"}.fa-face-rolling-eyes::before{content:"\f5a5"}.fa-meh-rolling-eyes::before{content:"\f5a5"}.fa-object-group::before{content:"\f247"}.fa-chart-line::before{content:"\f201"}.fa-line-chart::before{content:"\f201"}.fa-mask-ventilator::before{content:"\e524"}.fa-arrow-right::before{content:"\f061"}.fa-signs-post::before{content:"\f277"}.fa-map-signs::before{content:"\f277"}.fa-cash-register::before{content:"\f788"}.fa-person-circle-question::before{content:"\e542"}.fa-h::before{content:"\48"}.fa-tarp::before{content:"\e57b"}.fa-screwdriver-wrench::before{content:"\f7d9"}.fa-tools::before{content:"\f7d9"}.fa-arrows-to-eye::before{content:"\e4bf"}.fa-plug-circle-bolt::before{content:"\e55b"}.fa-heart::before{content:"\f004"}.fa-mars-and-venus::before{content:"\f224"}.fa-house-user::before{content:"\e1b0"}.fa-home-user::before{content:"\e1b0"}.fa-dumpster-fire::before{content:"\f794"}.fa-house-crack::before{content:"\e3b1"}.fa-martini-glass-citrus::before{content:"\f561"}.fa-cocktail::before{content:"\f561"}.fa-face-surprise::before{content:"\f5c2"}.fa-surprise::before{content:"\f5c2"}.fa-bottle-water::before{content:"\e4c5"}.fa-circle-pause::before{content:"\f28b"}.fa-pause-circle::before{content:"\f28b"}.fa-toilet-paper-slash::before{content:"\e072"}.fa-apple-whole::before{content:"\f5d1"}.fa-apple-alt::before{content:"\f5d1"}.fa-kitchen-set::before{content:"\e51a"}.fa-r::before{content:"\52"}.fa-temperature-quarter::before{content:"\f2ca"}.fa-temperature-1::before{content:"\f2ca"}.fa-thermometer-1::before{content:"\f2ca"}.fa-thermometer-quarter::before{content:"\f2ca"}.fa-cube::before{content:"\f1b2"}.fa-bitcoin-sign::before{content:"\e0b4"}.fa-shield-dog::before{content:"\e573"}.fa-solar-panel::before{content:"\f5ba"}.fa-lock-open::before{content:"\f3c1"}.fa-elevator::before{content:"\e16d"}.fa-money-bill-transfer::before{content:"\e528"}.fa-money-bill-trend-up::before{content:"\e529"}.fa-house-flood-water-circle-arrow-right::before{content:"\e50f"}.fa-square-poll-horizontal::before{content:"\f682"}.fa-poll-h::before{content:"\f682"}.fa-circle::before{content:"\f111"}.fa-backward-fast::before{content:"\f049"}.fa-fast-backward::before{content:"\f049"}.fa-recycle::before{content:"\f1b8"}.fa-user-astronaut::before{content:"\f4fb"}.fa-plane-slash::before{content:"\e069"}.fa-trademark::before{content:"\f25c"}.fa-basketball::before{content:"\f434"}.fa-basketball-ball::before{content:"\f434"}.fa-satellite-dish::before{content:"\f7c0"}.fa-circle-up::before{content:"\f35b"}.fa-arrow-alt-circle-up::before{content:"\f35b"}.fa-mobile-screen-button::before{content:"\f3cd"}.fa-mobile-alt::before{content:"\f3cd"}.fa-volume-high::before{content:"\f028"}.fa-volume-up::before{content:"\f028"}.fa-users-rays::before{content:"\e593"}.fa-wallet::before{content:"\f555"}.fa-clipboard-check::before{content:"\f46c"}.fa-file-audio::before{content:"\f1c7"}.fa-burger::before{content:"\f805"}.fa-hamburger::before{content:"\f805"}.fa-wrench::before{content:"\f0ad"}.fa-bugs::before{content:"\e4d0"}.fa-rupee-sign::before{content:"\f156"}.fa-rupee::before{content:"\f156"}.fa-file-image::before{content:"\f1c5"}.fa-circle-question::before{content:"\f059"}.fa-question-circle::before{content:"\f059"}.fa-plane-departure::before{content:"\f5b0"}.fa-handshake-slash::before{content:"\e060"}.fa-book-bookmark::before{content:"\e0bb"}.fa-code-branch::before{content:"\f126"}.fa-hat-cowboy::before{content:"\f8c0"}.fa-bridge::before{content:"\e4c8"}.fa-phone-flip::before{content:"\f879"}.fa-phone-alt::before{content:"\f879"}.fa-truck-front::before{content:"\e2b7"}.fa-cat::before{content:"\f6be"}.fa-anchor-circle-exclamation::before{content:"\e4ab"}.fa-truck-field::before{content:"\e58d"}.fa-route::before{content:"\f4d7"}.fa-clipboard-question::before{content:"\e4e3"}.fa-panorama::before{content:"\e209"}.fa-comment-medical::before{content:"\f7f5"}.fa-teeth-open::before{content:"\f62f"}.fa-file-circle-minus::before{content:"\e4ed"}.fa-tags::before{content:"\f02c"}.fa-wine-glass::before{content:"\f4e3"}.fa-forward-fast::before{content:"\f050"}.fa-fast-forward::before{content:"\f050"}.fa-face-meh-blank::before{content:"\f5a4"}.fa-meh-blank::before{content:"\f5a4"}.fa-square-parking::before{content:"\f540"}.fa-parking::before{content:"\f540"}.fa-house-signal::before{content:"\e012"}.fa-bars-progress::before{content:"\f828"}.fa-tasks-alt::before{content:"\f828"}.fa-faucet-drip::before{content:"\e006"}.fa-cart-flatbed::before{content:"\f474"}.fa-dolly-flatbed::before{content:"\f474"}.fa-ban-smoking::before{content:"\f54d"}.fa-smoking-ban::before{content:"\f54d"}.fa-terminal::before{content:"\f120"}.fa-mobile-button::before{content:"\f10b"}.fa-house-medical-flag::before{content:"\e514"}.fa-basket-shopping::before{content:"\f291"}.fa-shopping-basket::before{content:"\f291"}.fa-tape::before{content:"\f4db"}.fa-bus-simple::before{content:"\f55e"}.fa-bus-alt::before{content:"\f55e"}.fa-eye::before{content:"\f06e"}.fa-face-sad-cry::before{content:"\f5b3"}.fa-sad-cry::before{content:"\f5b3"}.fa-audio-description::before{content:"\f29e"}.fa-person-military-to-person::before{content:"\e54c"}.fa-file-shield::before{content:"\e4f0"}.fa-user-slash::before{content:"\f506"}.fa-pen::before{content:"\f304"}.fa-tower-observation::before{content:"\e586"}.fa-file-code::before{content:"\f1c9"}.fa-signal::before{content:"\f012"}.fa-signal-5::before{content:"\f012"}.fa-signal-perfect::before{content:"\f012"}.fa-bus::before{content:"\f207"}.fa-heart-circle-xmark::before{content:"\e501"}.fa-house-chimney::before{content:"\e3af"}.fa-home-lg::before{content:"\e3af"}.fa-window-maximize::before{content:"\f2d0"}.fa-face-frown::before{content:"\f119"}.fa-frown::before{content:"\f119"}.fa-prescription::before{content:"\f5b1"}.fa-shop::before{content:"\f54f"}.fa-store-alt::before{content:"\f54f"}.fa-floppy-disk::before{content:"\f0c7"}.fa-save::before{content:"\f0c7"}.fa-vihara::before{content:"\f6a7"}.fa-scale-unbalanced::before{content:"\f515"}.fa-balance-scale-left::before{content:"\f515"}.fa-sort-up::before{content:"\f0de"}.fa-sort-asc::before{content:"\f0de"}.fa-comment-dots::before{content:"\f4ad"}.fa-commenting::before{content:"\f4ad"}.fa-plant-wilt::before{content:"\e5aa"}.fa-diamond::before{content:"\f219"}.fa-face-grin-squint::before{content:"\f585"}.fa-grin-squint::before{content:"\f585"}.fa-hand-holding-dollar::before{content:"\f4c0"}.fa-hand-holding-usd::before{content:"\f4c0"}.fa-bacterium::before{content:"\e05a"}.fa-hand-pointer::before{content:"\f25a"}.fa-drum-steelpan::before{content:"\f56a"}.fa-hand-scissors::before{content:"\f257"}.fa-hands-praying::before{content:"\f684"}.fa-praying-hands::before{content:"\f684"}.fa-arrow-rotate-right::before{content:"\f01e"}.fa-arrow-right-rotate::before{content:"\f01e"}.fa-arrow-rotate-forward::before{content:"\f01e"}.fa-redo::before{content:"\f01e"}.fa-biohazard::before{content:"\f780"}.fa-location-crosshairs::before{content:"\f601"}.fa-location::before{content:"\f601"}.fa-mars-double::before{content:"\f227"}.fa-child-dress::before{content:"\e59c"}.fa-users-between-lines::before{content:"\e591"}.fa-lungs-virus::before{content:"\e067"}.fa-face-grin-tears::before{content:"\f588"}.fa-grin-tears::before{content:"\f588"}.fa-phone::before{content:"\f095"}.fa-calendar-xmark::before{content:"\f273"}.fa-calendar-times::before{content:"\f273"}.fa-child-reaching::before{content:"\e59d"}.fa-head-side-virus::before{content:"\e064"}.fa-user-gear::before{content:"\f4fe"}.fa-user-cog::before{content:"\f4fe"}.fa-arrow-up-1-9::before{content:"\f163"}.fa-sort-numeric-up::before{content:"\f163"}.fa-door-closed::before{content:"\f52a"}.fa-shield-virus::before{content:"\e06c"}.fa-dice-six::before{content:"\f526"}.fa-mosquito-net::before{content:"\e52c"}.fa-bridge-water::before{content:"\e4ce"}.fa-person-booth::before{content:"\f756"}.fa-text-width::before{content:"\f035"}.fa-hat-wizard::before{content:"\f6e8"}.fa-pen-fancy::before{content:"\f5ac"}.fa-person-digging::before{content:"\f85e"}.fa-digging::before{content:"\f85e"}.fa-trash::before{content:"\f1f8"}.fa-gauge-simple::before{content:"\f629"}.fa-gauge-simple-med::before{content:"\f629"}.fa-tachometer-average::before{content:"\f629"}.fa-book-medical::before{content:"\f7e6"}.fa-poo::before{content:"\f2fe"}.fa-quote-right::before{content:"\f10e"}.fa-quote-right-alt::before{content:"\f10e"}.fa-shirt::before{content:"\f553"}.fa-t-shirt::before{content:"\f553"}.fa-tshirt::before{content:"\f553"}.fa-cubes::before{content:"\f1b3"}.fa-divide::before{content:"\f529"}.fa-tenge-sign::before{content:"\f7d7"}.fa-tenge::before{content:"\f7d7"}.fa-headphones::before{content:"\f025"}.fa-hands-holding::before{content:"\f4c2"}.fa-hands-clapping::before{content:"\e1a8"}.fa-republican::before{content:"\f75e"}.fa-arrow-left::before{content:"\f060"}.fa-person-circle-xmark::before{content:"\e543"}.fa-ruler::before{content:"\f545"}.fa-align-left::before{content:"\f036"}.fa-dice-d6::before{content:"\f6d1"}.fa-restroom::before{content:"\f7bd"}.fa-j::before{content:"\4a"}.fa-users-viewfinder::before{content:"\e595"}.fa-file-video::before{content:"\f1c8"}.fa-up-right-from-square::before{content:"\f35d"}.fa-external-link-alt::before{content:"\f35d"}.fa-table-cells::before{content:"\f00a"}.fa-th::before{content:"\f00a"}.fa-file-pdf::before{content:"\f1c1"}.fa-book-bible::before{content:"\f647"}.fa-bible::before{content:"\f647"}.fa-o::before{content:"\4f"}.fa-suitcase-medical::before{content:"\f0fa"}.fa-medkit::before{content:"\f0fa"}.fa-user-secret::before{content:"\f21b"}.fa-otter::before{content:"\f700"}.fa-person-dress::before{content:"\f182"}.fa-female::before{content:"\f182"}.fa-comment-dollar::before{content:"\f651"}.fa-business-time::before{content:"\f64a"}.fa-briefcase-clock::before{content:"\f64a"}.fa-table-cells-large::before{content:"\f009"}.fa-th-large::before{content:"\f009"}.fa-book-tanakh::before{content:"\f827"}.fa-tanakh::before{content:"\f827"}.fa-phone-volume::before{content:"\f2a0"}.fa-volume-control-phone::before{content:"\f2a0"}.fa-hat-cowboy-side::before{content:"\f8c1"}.fa-clipboard-user::before{content:"\f7f3"}.fa-child::before{content:"\f1ae"}.fa-lira-sign::before{content:"\f195"}.fa-satellite::before{content:"\f7bf"}.fa-plane-lock::before{content:"\e558"}.fa-tag::before{content:"\f02b"}.fa-comment::before{content:"\f075"}.fa-cake-candles::before{content:"\f1fd"}.fa-birthday-cake::before{content:"\f1fd"}.fa-cake::before{content:"\f1fd"}.fa-envelope::before{content:"\f0e0"}.fa-angles-up::before{content:"\f102"}.fa-angle-double-up::before{content:"\f102"}.fa-paperclip::before{content:"\f0c6"}.fa-arrow-right-to-city::before{content:"\e4b3"}.fa-ribbon::before{content:"\f4d6"}.fa-lungs::before{content:"\f604"}.fa-arrow-up-9-1::before{content:"\f887"}.fa-sort-numeric-up-alt::before{content:"\f887"}.fa-litecoin-sign::before{content:"\e1d3"}.fa-border-none::before{content:"\f850"}.fa-circle-nodes::before{content:"\e4e2"}.fa-parachute-box::before{content:"\f4cd"}.fa-indent::before{content:"\f03c"}.fa-truck-field-un::before{content:"\e58e"}.fa-hourglass::before{content:"\f254"}.fa-hourglass-empty::before{content:"\f254"}.fa-mountain::before{content:"\f6fc"}.fa-user-doctor::before{content:"\f0f0"}.fa-user-md::before{content:"\f0f0"}.fa-circle-info::before{content:"\f05a"}.fa-info-circle::before{content:"\f05a"}.fa-cloud-meatball::before{content:"\f73b"}.fa-camera::before{content:"\f030"}.fa-camera-alt::before{content:"\f030"}.fa-square-virus::before{content:"\e578"}.fa-meteor::before{content:"\f753"}.fa-car-on::before{content:"\e4dd"}.fa-sleigh::before{content:"\f7cc"}.fa-arrow-down-1-9::before{content:"\f162"}.fa-sort-numeric-asc::before{content:"\f162"}.fa-sort-numeric-down::before{content:"\f162"}.fa-hand-holding-droplet::before{content:"\f4c1"}.fa-hand-holding-water::before{content:"\f4c1"}.fa-water::before{content:"\f773"}.fa-calendar-check::before{content:"\f274"}.fa-braille::before{content:"\f2a1"}.fa-prescription-bottle-medical::before{content:"\f486"}.fa-prescription-bottle-alt::before{content:"\f486"}.fa-landmark::before{content:"\f66f"}.fa-truck::before{content:"\f0d1"}.fa-crosshairs::before{content:"\f05b"}.fa-person-cane::before{content:"\e53c"}.fa-tent::before{content:"\e57d"}.fa-vest-patches::before{content:"\e086"}.fa-check-double::before{content:"\f560"}.fa-arrow-down-a-z::before{content:"\f15d"}.fa-sort-alpha-asc::before{content:"\f15d"}.fa-sort-alpha-down::before{content:"\f15d"}.fa-money-bill-wheat::before{content:"\e52a"}.fa-cookie::before{content:"\f563"}.fa-arrow-rotate-left::before{content:"\f0e2"}.fa-arrow-left-rotate::before{content:"\f0e2"}.fa-arrow-rotate-back::before{content:"\f0e2"}.fa-arrow-rotate-backward::before{content:"\f0e2"}.fa-undo::before{content:"\f0e2"}.fa-hard-drive::before{content:"\f0a0"}.fa-hdd::before{content:"\f0a0"}.fa-face-grin-squint-tears::before{content:"\f586"}.fa-grin-squint-tears::before{content:"\f586"}.fa-dumbbell::before{content:"\f44b"}.fa-rectangle-list::before{content:"\f022"}.fa-list-alt::before{content:"\f022"}.fa-tarp-droplet::before{content:"\e57c"}.fa-house-medical-circle-check::before{content:"\e511"}.fa-person-skiing-nordic::before{content:"\f7ca"}.fa-skiing-nordic::before{content:"\f7ca"}.fa-calendar-plus::before{content:"\f271"}.fa-plane-arrival::before{content:"\f5af"}.fa-circle-left::before{content:"\f359"}.fa-arrow-alt-circle-left::before{content:"\f359"}.fa-train-subway::before{content:"\f239"}.fa-subway::before{content:"\f239"}.fa-chart-gantt::before{content:"\e0e4"}.fa-indian-rupee-sign::before{content:"\e1bc"}.fa-indian-rupee::before{content:"\e1bc"}.fa-inr::before{content:"\e1bc"}.fa-crop-simple::before{content:"\f565"}.fa-crop-alt::before{content:"\f565"}.fa-money-bill-1::before{content:"\f3d1"}.fa-money-bill-alt::before{content:"\f3d1"}.fa-left-long::before{content:"\f30a"}.fa-long-arrow-alt-left::before{content:"\f30a"}.fa-dna::before{content:"\f471"}.fa-virus-slash::before{content:"\e075"}.fa-minus::before{content:"\f068"}.fa-subtract::before{content:"\f068"}.fa-chess::before{content:"\f439"}.fa-arrow-left-long::before{content:"\f177"}.fa-long-arrow-left::before{content:"\f177"}.fa-plug-circle-check::before{content:"\e55c"}.fa-street-view::before{content:"\f21d"}.fa-franc-sign::before{content:"\e18f"}.fa-volume-off::before{content:"\f026"}.fa-hands-asl-interpreting::before{content:"\f2a3"}.fa-american-sign-language-interpreting::before{content:"\f2a3"}.fa-asl-interpreting::before{content:"\f2a3"}.fa-hands-american-sign-language-interpreting::before{content:"\f2a3"}.fa-gear::before{content:"\f013"}.fa-cog::before{content:"\f013"}.fa-droplet-slash::before{content:"\f5c7"}.fa-tint-slash::before{content:"\f5c7"}.fa-mosque::before{content:"\f678"}.fa-mosquito::before{content:"\e52b"}.fa-star-of-david::before{content:"\f69a"}.fa-person-military-rifle::before{content:"\e54b"}.fa-cart-shopping::before{content:"\f07a"}.fa-shopping-cart::before{content:"\f07a"}.fa-vials::before{content:"\f493"}.fa-plug-circle-plus::before{content:"\e55f"}.fa-place-of-worship::before{content:"\f67f"}.fa-grip-vertical::before{content:"\f58e"}.fa-arrow-turn-up::before{content:"\f148"}.fa-level-up::before{content:"\f148"}.fa-u::before{content:"\55"}.fa-square-root-variable::before{content:"\f698"}.fa-square-root-alt::before{content:"\f698"}.fa-clock::before{content:"\f017"}.fa-clock-four::before{content:"\f017"}.fa-backward-step::before{content:"\f048"}.fa-step-backward::before{content:"\f048"}.fa-pallet::before{content:"\f482"}.fa-faucet::before{content:"\e005"}.fa-baseball-bat-ball::before{content:"\f432"}.fa-s::before{content:"\53"}.fa-timeline::before{content:"\e29c"}.fa-keyboard::before{content:"\f11c"}.fa-caret-down::before{content:"\f0d7"}.fa-house-chimney-medical::before{content:"\f7f2"}.fa-clinic-medical::before{content:"\f7f2"}.fa-temperature-three-quarters::before{content:"\f2c8"}.fa-temperature-3::before{content:"\f2c8"}.fa-thermometer-3::before{content:"\f2c8"}.fa-thermometer-three-quarters::before{content:"\f2c8"}.fa-mobile-screen::before{content:"\f3cf"}.fa-mobile-android-alt::before{content:"\f3cf"}.fa-plane-up::before{content:"\e22d"}.fa-piggy-bank::before{content:"\f4d3"}.fa-battery-half::before{content:"\f242"}.fa-battery-3::before{content:"\f242"}.fa-mountain-city::before{content:"\e52e"}.fa-coins::before{content:"\f51e"}.fa-khanda::before{content:"\f66d"}.fa-sliders::before{content:"\f1de"}.fa-sliders-h::before{content:"\f1de"}.fa-folder-tree::before{content:"\f802"}.fa-network-wired::before{content:"\f6ff"}.fa-map-pin::before{content:"\f276"}.fa-hamsa::before{content:"\f665"}.fa-cent-sign::before{content:"\e3f5"}.fa-flask::before{content:"\f0c3"}.fa-person-pregnant::before{content:"\e31e"}.fa-wand-sparkles::before{content:"\f72b"}.fa-ellipsis-vertical::before{content:"\f142"}.fa-ellipsis-v::before{content:"\f142"}.fa-ticket::before{content:"\f145"}.fa-power-off::before{content:"\f011"}.fa-right-long::before{content:"\f30b"}.fa-long-arrow-alt-right::before{content:"\f30b"}.fa-flag-usa::before{content:"\f74d"}.fa-laptop-file::before{content:"\e51d"}.fa-tty::before{content:"\f1e4"}.fa-teletype::before{content:"\f1e4"}.fa-diagram-next::before{content:"\e476"}.fa-person-rifle::before{content:"\e54e"}.fa-house-medical-circle-exclamation::before{content:"\e512"}.fa-closed-captioning::before{content:"\f20a"}.fa-person-hiking::before{content:"\f6ec"}.fa-hiking::before{content:"\f6ec"}.fa-venus-double::before{content:"\f226"}.fa-images::before{content:"\f302"}.fa-calculator::before{content:"\f1ec"}.fa-people-pulling::before{content:"\e535"}.fa-n::before{content:"\4e"}.fa-cable-car::before{content:"\f7da"}.fa-tram::before{content:"\f7da"}.fa-cloud-rain::before{content:"\f73d"}.fa-building-circle-xmark::before{content:"\e4d4"}.fa-ship::before{content:"\f21a"}.fa-arrows-down-to-line::before{content:"\e4b8"}.fa-download::before{content:"\f019"}.fa-face-grin::before{content:"\f580"}.fa-grin::before{content:"\f580"}.fa-delete-left::before{content:"\f55a"}.fa-backspace::before{content:"\f55a"}.fa-eye-dropper::before{content:"\f1fb"}.fa-eye-dropper-empty::before{content:"\f1fb"}.fa-eyedropper::before{content:"\f1fb"}.fa-file-circle-check::before{content:"\e5a0"}.fa-forward::before{content:"\f04e"}.fa-mobile::before{content:"\f3ce"}.fa-mobile-android::before{content:"\f3ce"}.fa-mobile-phone::before{content:"\f3ce"}.fa-face-meh::before{content:"\f11a"}.fa-meh::before{content:"\f11a"}.fa-align-center::before{content:"\f037"}.fa-book-skull::before{content:"\f6b7"}.fa-book-dead::before{content:"\f6b7"}.fa-id-card::before{content:"\f2c2"}.fa-drivers-license::before{content:"\f2c2"}.fa-outdent::before{content:"\f03b"}.fa-dedent::before{content:"\f03b"}.fa-heart-circle-exclamation::before{content:"\e4fe"}.fa-house::before{content:"\f015"}.fa-home::before{content:"\f015"}.fa-home-alt::before{content:"\f015"}.fa-home-lg-alt::before{content:"\f015"}.fa-calendar-week::before{content:"\f784"}.fa-laptop-medical::before{content:"\f812"}.fa-b::before{content:"\42"}.fa-file-medical::before{content:"\f477"}.fa-dice-one::before{content:"\f525"}.fa-kiwi-bird::before{content:"\f535"}.fa-arrow-right-arrow-left::before{content:"\f0ec"}.fa-exchange::before{content:"\f0ec"}.fa-rotate-right::before{content:"\f2f9"}.fa-redo-alt::before{content:"\f2f9"}.fa-rotate-forward::before{content:"\f2f9"}.fa-utensils::before{content:"\f2e7"}.fa-cutlery::before{content:"\f2e7"}.fa-arrow-up-wide-short::before{content:"\f161"}.fa-sort-amount-up::before{content:"\f161"}.fa-mill-sign::before{content:"\e1ed"}.fa-bowl-rice::before{content:"\e2eb"}.fa-skull::before{content:"\f54c"}.fa-tower-broadcast::before{content:"\f519"}.fa-broadcast-tower::before{content:"\f519"}.fa-truck-pickup::before{content:"\f63c"}.fa-up-long::before{content:"\f30c"}.fa-long-arrow-alt-up::before{content:"\f30c"}.fa-stop::before{content:"\f04d"}.fa-code-merge::before{content:"\f387"}.fa-upload::before{content:"\f093"}.fa-hurricane::before{content:"\f751"}.fa-mound::before{content:"\e52d"}.fa-toilet-portable::before{content:"\e583"}.fa-compact-disc::before{content:"\f51f"}.fa-file-arrow-down::before{content:"\f56d"}.fa-file-download::before{content:"\f56d"}.fa-caravan::before{content:"\f8ff"}.fa-shield-cat::before{content:"\e572"}.fa-bolt::before{content:"\f0e7"}.fa-zap::before{content:"\f0e7"}.fa-glass-water::before{content:"\e4f4"}.fa-oil-well::before{content:"\e532"}.fa-vault::before{content:"\e2c5"}.fa-mars::before{content:"\f222"}.fa-toilet::before{content:"\f7d8"}.fa-plane-circle-xmark::before{content:"\e557"}.fa-yen-sign::before{content:"\f157"}.fa-cny::before{content:"\f157"}.fa-jpy::before{content:"\f157"}.fa-rmb::before{content:"\f157"}.fa-yen::before{content:"\f157"}.fa-ruble-sign::before{content:"\f158"}.fa-rouble::before{content:"\f158"}.fa-rub::before{content:"\f158"}.fa-ruble::before{content:"\f158"}.fa-sun::before{content:"\f185"}.fa-guitar::before{content:"\f7a6"}.fa-face-laugh-wink::before{content:"\f59c"}.fa-laugh-wink::before{content:"\f59c"}.fa-horse-head::before{content:"\f7ab"}.fa-bore-hole::before{content:"\e4c3"}.fa-industry::before{content:"\f275"}.fa-circle-down::before{content:"\f358"}.fa-arrow-alt-circle-down::before{content:"\f358"}.fa-arrows-turn-to-dots::before{content:"\e4c1"}.fa-florin-sign::before{content:"\e184"}.fa-arrow-down-short-wide::before{content:"\f884"}.fa-sort-amount-desc::before{content:"\f884"}.fa-sort-amount-down-alt::before{content:"\f884"}.fa-less-than::before{content:"\3c"}.fa-angle-down::before{content:"\f107"}.fa-car-tunnel::before{content:"\e4de"}.fa-head-side-cough::before{content:"\e061"}.fa-grip-lines::before{content:"\f7a4"}.fa-thumbs-down::before{content:"\f165"}.fa-user-lock::before{content:"\f502"}.fa-arrow-right-long::before{content:"\f178"}.fa-long-arrow-right::before{content:"\f178"}.fa-anchor-circle-xmark::before{content:"\e4ac"}.fa-ellipsis::before{content:"\f141"}.fa-ellipsis-h::before{content:"\f141"}.fa-chess-pawn::before{content:"\f443"}.fa-kit-medical::before{content:"\f479"}.fa-first-aid::before{content:"\f479"}.fa-person-through-window::before{content:"\e5a9"}.fa-toolbox::before{content:"\f552"}.fa-hands-holding-circle::before{content:"\e4fb"}.fa-bug::before{content:"\f188"}.fa-credit-card::before{content:"\f09d"}.fa-credit-card-alt::before{content:"\f09d"}.fa-car::before{content:"\f1b9"}.fa-automobile::before{content:"\f1b9"}.fa-hand-holding-hand::before{content:"\e4f7"}.fa-book-open-reader::before{content:"\f5da"}.fa-book-reader::before{content:"\f5da"}.fa-mountain-sun::before{content:"\e52f"}.fa-arrows-left-right-to-line::before{content:"\e4ba"}.fa-dice-d20::before{content:"\f6cf"}.fa-truck-droplet::before{content:"\e58c"}.fa-file-circle-xmark::before{content:"\e5a1"}.fa-temperature-arrow-up::before{content:"\e040"}.fa-temperature-up::before{content:"\e040"}.fa-medal::before{content:"\f5a2"}.fa-bed::before{content:"\f236"}.fa-square-h::before{content:"\f0fd"}.fa-h-square::before{content:"\f0fd"}.fa-podcast::before{content:"\f2ce"}.fa-temperature-full::before{content:"\f2c7"}.fa-temperature-4::before{content:"\f2c7"}.fa-thermometer-4::before{content:"\f2c7"}.fa-thermometer-full::before{content:"\f2c7"}.fa-bell::before{content:"\f0f3"}.fa-superscript::before{content:"\f12b"}.fa-plug-circle-xmark::before{content:"\e560"}.fa-star-of-life::before{content:"\f621"}.fa-phone-slash::before{content:"\f3dd"}.fa-paint-roller::before{content:"\f5aa"}.fa-handshake-angle::before{content:"\f4c4"}.fa-hands-helping::before{content:"\f4c4"}.fa-location-dot::before{content:"\f3c5"}.fa-map-marker-alt::before{content:"\f3c5"}.fa-file::before{content:"\f15b"}.fa-greater-than::before{content:"\3e"}.fa-person-swimming::before{content:"\f5c4"}.fa-swimmer::before{content:"\f5c4"}.fa-arrow-down::before{content:"\f063"}.fa-droplet::before{content:"\f043"}.fa-tint::before{content:"\f043"}.fa-eraser::before{content:"\f12d"}.fa-earth-americas::before{content:"\f57d"}.fa-earth::before{content:"\f57d"}.fa-earth-america::before{content:"\f57d"}.fa-globe-americas::before{content:"\f57d"}.fa-person-burst::before{content:"\e53b"}.fa-dove::before{content:"\f4ba"}.fa-battery-empty::before{content:"\f244"}.fa-battery-0::before{content:"\f244"}.fa-socks::before{content:"\f696"}.fa-inbox::before{content:"\f01c"}.fa-section::before{content:"\e447"}.fa-gauge-high::before{content:"\f625"}.fa-tachometer-alt::before{content:"\f625"}.fa-tachometer-alt-fast::before{content:"\f625"}.fa-envelope-open-text::before{content:"\f658"}.fa-hospital::before{content:"\f0f8"}.fa-hospital-alt::before{content:"\f0f8"}.fa-hospital-wide::before{content:"\f0f8"}.fa-wine-bottle::before{content:"\f72f"}.fa-chess-rook::before{content:"\f447"}.fa-bars-staggered::before{content:"\f550"}.fa-reorder::before{content:"\f550"}.fa-stream::before{content:"\f550"}.fa-dharmachakra::before{content:"\f655"}.fa-hotdog::before{content:"\f80f"}.fa-person-walking-with-cane::before{content:"\f29d"}.fa-blind::before{content:"\f29d"}.fa-drum::before{content:"\f569"}.fa-ice-cream::before{content:"\f810"}.fa-heart-circle-bolt::before{content:"\e4fc"}.fa-fax::before{content:"\f1ac"}.fa-paragraph::before{content:"\f1dd"}.fa-check-to-slot::before{content:"\f772"}.fa-vote-yea::before{content:"\f772"}.fa-star-half::before{content:"\f089"}.fa-boxes-stacked::before{content:"\f468"}.fa-boxes::before{content:"\f468"}.fa-boxes-alt::before{content:"\f468"}.fa-link::before{content:"\f0c1"}.fa-chain::before{content:"\f0c1"}.fa-ear-listen::before{content:"\f2a2"}.fa-assistive-listening-systems::before{content:"\f2a2"}.fa-tree-city::before{content:"\e587"}.fa-play::before{content:"\f04b"}.fa-font::before{content:"\f031"}.fa-table-cells-row-lock::before{content:"\e67a"}.fa-rupiah-sign::before{content:"\e23d"}.fa-magnifying-glass::before{content:"\f002"}.fa-search::before{content:"\f002"}.fa-table-tennis-paddle-ball::before{content:"\f45d"}.fa-ping-pong-paddle-ball::before{content:"\f45d"}.fa-table-tennis::before{content:"\f45d"}.fa-person-dots-from-line::before{content:"\f470"}.fa-diagnoses::before{content:"\f470"}.fa-trash-can-arrow-up::before{content:"\f82a"}.fa-trash-restore-alt::before{content:"\f82a"}.fa-naira-sign::before{content:"\e1f6"}.fa-cart-arrow-down::before{content:"\f218"}.fa-walkie-talkie::before{content:"\f8ef"}.fa-file-pen::before{content:"\f31c"}.fa-file-edit::before{content:"\f31c"}.fa-receipt::before{content:"\f543"}.fa-square-pen::before{content:"\f14b"}.fa-pen-square::before{content:"\f14b"}.fa-pencil-square::before{content:"\f14b"}.fa-suitcase-rolling::before{content:"\f5c1"}.fa-person-circle-exclamation::before{content:"\e53f"}.fa-chevron-down::before{content:"\f078"}.fa-battery-full::before{content:"\f240"}.fa-battery::before{content:"\f240"}.fa-battery-5::before{content:"\f240"}.fa-skull-crossbones::before{content:"\f714"}.fa-code-compare::before{content:"\e13a"}.fa-list-ul::before{content:"\f0ca"}.fa-list-dots::before{content:"\f0ca"}.fa-school-lock::before{content:"\e56f"}.fa-tower-cell::before{content:"\e585"}.fa-down-long::before{content:"\f309"}.fa-long-arrow-alt-down::before{content:"\f309"}.fa-ranking-star::before{content:"\e561"}.fa-chess-king::before{content:"\f43f"}.fa-person-harassing::before{content:"\e549"}.fa-brazilian-real-sign::before{content:"\e46c"}.fa-landmark-dome::before{content:"\f752"}.fa-landmark-alt::before{content:"\f752"}.fa-arrow-up::before{content:"\f062"}.fa-tv::before{content:"\f26c"}.fa-television::before{content:"\f26c"}.fa-tv-alt::before{content:"\f26c"}.fa-shrimp::before{content:"\e448"}.fa-list-check::before{content:"\f0ae"}.fa-tasks::before{content:"\f0ae"}.fa-jug-detergent::before{content:"\e519"}.fa-circle-user::before{content:"\f2bd"}.fa-user-circle::before{content:"\f2bd"}.fa-user-shield::before{content:"\f505"}.fa-wind::before{content:"\f72e"}.fa-car-burst::before{content:"\f5e1"}.fa-car-crash::before{content:"\f5e1"}.fa-y::before{content:"\59"}.fa-person-snowboarding::before{content:"\f7ce"}.fa-snowboarding::before{content:"\f7ce"}.fa-truck-fast::before{content:"\f48b"}.fa-shipping-fast::before{content:"\f48b"}.fa-fish::before{content:"\f578"}.fa-user-graduate::before{content:"\f501"}.fa-circle-half-stroke::before{content:"\f042"}.fa-adjust::before{content:"\f042"}.fa-clapperboard::before{content:"\e131"}.fa-circle-radiation::before{content:"\f7ba"}.fa-radiation-alt::before{content:"\f7ba"}.fa-baseball::before{content:"\f433"}.fa-baseball-ball::before{content:"\f433"}.fa-jet-fighter-up::before{content:"\e518"}.fa-diagram-project::before{content:"\f542"}.fa-project-diagram::before{content:"\f542"}.fa-copy::before{content:"\f0c5"}.fa-volume-xmark::before{content:"\f6a9"}.fa-volume-mute::before{content:"\f6a9"}.fa-volume-times::before{content:"\f6a9"}.fa-hand-sparkles::before{content:"\e05d"}.fa-grip::before{content:"\f58d"}.fa-grip-horizontal::before{content:"\f58d"}.fa-share-from-square::before{content:"\f14d"}.fa-share-square::before{content:"\f14d"}.fa-child-combatant::before{content:"\e4e0"}.fa-child-rifle::before{content:"\e4e0"}.fa-gun::before{content:"\e19b"}.fa-square-phone::before{content:"\f098"}.fa-phone-square::before{content:"\f098"}.fa-plus::before{content:"\2b"}.fa-add::before{content:"\2b"}.fa-expand::before{content:"\f065"}.fa-computer::before{content:"\e4e5"}.fa-xmark::before{content:"\f00d"}.fa-close::before{content:"\f00d"}.fa-multiply::before{content:"\f00d"}.fa-remove::before{content:"\f00d"}.fa-times::before{content:"\f00d"}.fa-arrows-up-down-left-right::before{content:"\f047"}.fa-arrows::before{content:"\f047"}.fa-chalkboard-user::before{content:"\f51c"}.fa-chalkboard-teacher::before{content:"\f51c"}.fa-peso-sign::before{content:"\e222"}.fa-building-shield::before{content:"\e4d8"}.fa-baby::before{content:"\f77c"}.fa-users-line::before{content:"\e592"}.fa-quote-left::before{content:"\f10d"}.fa-quote-left-alt::before{content:"\f10d"}.fa-tractor::before{content:"\f722"}.fa-trash-arrow-up::before{content:"\f829"}.fa-trash-restore::before{content:"\f829"}.fa-arrow-down-up-lock::before{content:"\e4b0"}.fa-lines-leaning::before{content:"\e51e"}.fa-ruler-combined::before{content:"\f546"}.fa-copyright::before{content:"\f1f9"}.fa-equals::before{content:"\3d"}.fa-blender::before{content:"\f517"}.fa-teeth::before{content:"\f62e"}.fa-shekel-sign::before{content:"\f20b"}.fa-ils::before{content:"\f20b"}.fa-shekel::before{content:"\f20b"}.fa-sheqel::before{content:"\f20b"}.fa-sheqel-sign::before{content:"\f20b"}.fa-map::before{content:"\f279"}.fa-rocket::before{content:"\f135"}.fa-photo-film::before{content:"\f87c"}.fa-photo-video::before{content:"\f87c"}.fa-folder-minus::before{content:"\f65d"}.fa-store::before{content:"\f54e"}.fa-arrow-trend-up::before{content:"\e098"}.fa-plug-circle-minus::before{content:"\e55e"}.fa-sign-hanging::before{content:"\f4d9"}.fa-sign::before{content:"\f4d9"}.fa-bezier-curve::before{content:"\f55b"}.fa-bell-slash::before{content:"\f1f6"}.fa-tablet::before{content:"\f3fb"}.fa-tablet-android::before{content:"\f3fb"}.fa-school-flag::before{content:"\e56e"}.fa-fill::before{content:"\f575"}.fa-angle-up::before{content:"\f106"}.fa-drumstick-bite::before{content:"\f6d7"}.fa-holly-berry::before{content:"\f7aa"}.fa-chevron-left::before{content:"\f053"}.fa-bacteria::before{content:"\e059"}.fa-hand-lizard::before{content:"\f258"}.fa-notdef::before{content:"\e1fe"}.fa-disease::before{content:"\f7fa"}.fa-briefcase-medical::before{content:"\f469"}.fa-genderless::before{content:"\f22d"}.fa-chevron-right::before{content:"\f054"}.fa-retweet::before{content:"\f079"}.fa-car-rear::before{content:"\f5de"}.fa-car-alt::before{content:"\f5de"}.fa-pump-soap::before{content:"\e06b"}.fa-video-slash::before{content:"\f4e2"}.fa-battery-quarter::before{content:"\f243"}.fa-battery-2::before{content:"\f243"}.fa-radio::before{content:"\f8d7"}.fa-baby-carriage::before{content:"\f77d"}.fa-carriage-baby::before{content:"\f77d"}.fa-traffic-light::before{content:"\f637"}.fa-thermometer::before{content:"\f491"}.fa-vr-cardboard::before{content:"\f729"}.fa-hand-middle-finger::before{content:"\f806"}.fa-percent::before{content:"\25"}.fa-percentage::before{content:"\25"}.fa-truck-moving::before{content:"\f4df"}.fa-glass-water-droplet::before{content:"\e4f5"}.fa-display::before{content:"\e163"}.fa-face-smile::before{content:"\f118"}.fa-smile::before{content:"\f118"}.fa-thumbtack::before{content:"\f08d"}.fa-thumb-tack::before{content:"\f08d"}.fa-trophy::before{content:"\f091"}.fa-person-praying::before{content:"\f683"}.fa-pray::before{content:"\f683"}.fa-hammer::before{content:"\f6e3"}.fa-hand-peace::before{content:"\f25b"}.fa-rotate::before{content:"\f2f1"}.fa-sync-alt::before{content:"\f2f1"}.fa-spinner::before{content:"\f110"}.fa-robot::before{content:"\f544"}.fa-peace::before{content:"\f67c"}.fa-gears::before{content:"\f085"}.fa-cogs::before{content:"\f085"}.fa-warehouse::before{content:"\f494"}.fa-arrow-up-right-dots::before{content:"\e4b7"}.fa-splotch::before{content:"\f5bc"}.fa-face-grin-hearts::before{content:"\f584"}.fa-grin-hearts::before{content:"\f584"}.fa-dice-four::before{content:"\f524"}.fa-sim-card::before{content:"\f7c4"}.fa-transgender::before{content:"\f225"}.fa-transgender-alt::before{content:"\f225"}.fa-mercury::before{content:"\f223"}.fa-arrow-turn-down::before{content:"\f149"}.fa-level-down::before{content:"\f149"}.fa-person-falling-burst::before{content:"\e547"}.fa-award::before{content:"\f559"}.fa-ticket-simple::before{content:"\f3ff"}.fa-ticket-alt::before{content:"\f3ff"}.fa-building::before{content:"\f1ad"}.fa-angles-left::before{content:"\f100"}.fa-angle-double-left::before{content:"\f100"}.fa-qrcode::before{content:"\f029"}.fa-clock-rotate-left::before{content:"\f1da"}.fa-history::before{content:"\f1da"}.fa-face-grin-beam-sweat::before{content:"\f583"}.fa-grin-beam-sweat::before{content:"\f583"}.fa-file-export::before{content:"\f56e"}.fa-arrow-right-from-file::before{content:"\f56e"}.fa-shield::before{content:"\f132"}.fa-shield-blank::before{content:"\f132"}.fa-arrow-up-short-wide::before{content:"\f885"}.fa-sort-amount-up-alt::before{content:"\f885"}.fa-house-medical::before{content:"\e3b2"}.fa-golf-ball-tee::before{content:"\f450"}.fa-golf-ball::before{content:"\f450"}.fa-circle-chevron-left::before{content:"\f137"}.fa-chevron-circle-left::before{content:"\f137"}.fa-house-chimney-window::before{content:"\e00d"}.fa-pen-nib::before{content:"\f5ad"}.fa-tent-arrow-turn-left::before{content:"\e580"}.fa-tents::before{content:"\e582"}.fa-wand-magic::before{content:"\f0d0"}.fa-magic::before{content:"\f0d0"}.fa-dog::before{content:"\f6d3"}.fa-carrot::before{content:"\f787"}.fa-moon::before{content:"\f186"}.fa-wine-glass-empty::before{content:"\f5ce"}.fa-wine-glass-alt::before{content:"\f5ce"}.fa-cheese::before{content:"\f7ef"}.fa-yin-yang::before{content:"\f6ad"}.fa-music::before{content:"\f001"}.fa-code-commit::before{content:"\f386"}.fa-temperature-low::before{content:"\f76b"}.fa-person-biking::before{content:"\f84a"}.fa-biking::before{content:"\f84a"}.fa-broom::before{content:"\f51a"}.fa-shield-heart::before{content:"\e574"}.fa-gopuram::before{content:"\f664"}.fa-earth-oceania::before{content:"\e47b"}.fa-globe-oceania::before{content:"\e47b"}.fa-square-xmark::before{content:"\f2d3"}.fa-times-square::before{content:"\f2d3"}.fa-xmark-square::before{content:"\f2d3"}.fa-hashtag::before{content:"\23"}.fa-up-right-and-down-left-from-center::before{content:"\f424"}.fa-expand-alt::before{content:"\f424"}.fa-oil-can::before{content:"\f613"}.fa-t::before{content:"\54"}.fa-hippo::before{content:"\f6ed"}.fa-chart-column::before{content:"\e0e3"}.fa-infinity::before{content:"\f534"}.fa-vial-circle-check::before{content:"\e596"}.fa-person-arrow-down-to-line::before{content:"\e538"}.fa-voicemail::before{content:"\f897"}.fa-fan::before{content:"\f863"}.fa-person-walking-luggage::before{content:"\e554"}.fa-up-down::before{content:"\f338"}.fa-arrows-alt-v::before{content:"\f338"}.fa-cloud-moon-rain::before{content:"\f73c"}.fa-calendar::before{content:"\f133"}.fa-trailer::before{content:"\e041"}.fa-bahai::before{content:"\f666"}.fa-haykal::before{content:"\f666"}.fa-sd-card::before{content:"\f7c2"}.fa-dragon::before{content:"\f6d5"}.fa-shoe-prints::before{content:"\f54b"}.fa-circle-plus::before{content:"\f055"}.fa-plus-circle::before{content:"\f055"}.fa-face-grin-tongue-wink::before{content:"\f58b"}.fa-grin-tongue-wink::before{content:"\f58b"}.fa-hand-holding::before{content:"\f4bd"}.fa-plug-circle-exclamation::before{content:"\e55d"}.fa-link-slash::before{content:"\f127"}.fa-chain-broken::before{content:"\f127"}.fa-chain-slash::before{content:"\f127"}.fa-unlink::before{content:"\f127"}.fa-clone::before{content:"\f24d"}.fa-person-walking-arrow-loop-left::before{content:"\e551"}.fa-arrow-up-z-a::before{content:"\f882"}.fa-sort-alpha-up-alt::before{content:"\f882"}.fa-fire-flame-curved::before{content:"\f7e4"}.fa-fire-alt::before{content:"\f7e4"}.fa-tornado::before{content:"\f76f"}.fa-file-circle-plus::before{content:"\e494"}.fa-book-quran::before{content:"\f687"}.fa-quran::before{content:"\f687"}.fa-anchor::before{content:"\f13d"}.fa-border-all::before{content:"\f84c"}.fa-face-angry::before{content:"\f556"}.fa-angry::before{content:"\f556"}.fa-cookie-bite::before{content:"\f564"}.fa-arrow-trend-down::before{content:"\e097"}.fa-rss::before{content:"\f09e"}.fa-feed::before{content:"\f09e"}.fa-draw-polygon::before{content:"\f5ee"}.fa-scale-balanced::before{content:"\f24e"}.fa-balance-scale::before{content:"\f24e"}.fa-gauge-simple-high::before{content:"\f62a"}.fa-tachometer::before{content:"\f62a"}.fa-tachometer-fast::before{content:"\f62a"}.fa-shower::before{content:"\f2cc"}.fa-desktop::before{content:"\f390"}.fa-desktop-alt::before{content:"\f390"}.fa-m::before{content:"\4d"}.fa-table-list::before{content:"\f00b"}.fa-th-list::before{content:"\f00b"}.fa-comment-sms::before{content:"\f7cd"}.fa-sms::before{content:"\f7cd"}.fa-book::before{content:"\f02d"}.fa-user-plus::before{content:"\f234"}.fa-check::before{content:"\f00c"}.fa-battery-three-quarters::before{content:"\f241"}.fa-battery-4::before{content:"\f241"}.fa-house-circle-check::before{content:"\e509"}.fa-angle-left::before{content:"\f104"}.fa-diagram-successor::before{content:"\e47a"}.fa-truck-arrow-right::before{content:"\e58b"}.fa-arrows-split-up-and-left::before{content:"\e4bc"}.fa-hand-fist::before{content:"\f6de"}.fa-fist-raised::before{content:"\f6de"}.fa-cloud-moon::before{content:"\f6c3"}.fa-briefcase::before{content:"\f0b1"}.fa-person-falling::before{content:"\e546"}.fa-image-portrait::before{content:"\f3e0"}.fa-portrait::before{content:"\f3e0"}.fa-user-tag::before{content:"\f507"}.fa-rug::before{content:"\e569"}.fa-earth-europe::before{content:"\f7a2"}.fa-globe-europe::before{content:"\f7a2"}.fa-cart-flatbed-suitcase::before{content:"\f59d"}.fa-luggage-cart::before{content:"\f59d"}.fa-rectangle-xmark::before{content:"\f410"}.fa-rectangle-times::before{content:"\f410"}.fa-times-rectangle::before{content:"\f410"}.fa-window-close::before{content:"\f410"}.fa-baht-sign::before{content:"\e0ac"}.fa-book-open::before{content:"\f518"}.fa-book-journal-whills::before{content:"\f66a"}.fa-journal-whills::before{content:"\f66a"}.fa-handcuffs::before{content:"\e4f8"}.fa-triangle-exclamation::before{content:"\f071"}.fa-exclamation-triangle::before{content:"\f071"}.fa-warning::before{content:"\f071"}.fa-database::before{content:"\f1c0"}.fa-share::before{content:"\f064"}.fa-mail-forward::before{content:"\f064"}.fa-bottle-droplet::before{content:"\e4c4"}.fa-mask-face::before{content:"\e1d7"}.fa-hill-rockslide::before{content:"\e508"}.fa-right-left::before{content:"\f362"}.fa-exchange-alt::before{content:"\f362"}.fa-paper-plane::before{content:"\f1d8"}.fa-road-circle-exclamation::before{content:"\e565"}.fa-dungeon::before{content:"\f6d9"}.fa-align-right::before{content:"\f038"}.fa-money-bill-1-wave::before{content:"\f53b"}.fa-money-bill-wave-alt::before{content:"\f53b"}.fa-life-ring::before{content:"\f1cd"}.fa-hands::before{content:"\f2a7"}.fa-sign-language::before{content:"\f2a7"}.fa-signing::before{content:"\f2a7"}.fa-calendar-day::before{content:"\f783"}.fa-water-ladder::before{content:"\f5c5"}.fa-ladder-water::before{content:"\f5c5"}.fa-swimming-pool::before{content:"\f5c5"}.fa-arrows-up-down::before{content:"\f07d"}.fa-arrows-v::before{content:"\f07d"}.fa-face-grimace::before{content:"\f57f"}.fa-grimace::before{content:"\f57f"}.fa-wheelchair-move::before{content:"\e2ce"}.fa-wheelchair-alt::before{content:"\e2ce"}.fa-turn-down::before{content:"\f3be"}.fa-level-down-alt::before{content:"\f3be"}.fa-person-walking-arrow-right::before{content:"\e552"}.fa-square-envelope::before{content:"\f199"}.fa-envelope-square::before{content:"\f199"}.fa-dice::before{content:"\f522"}.fa-bowling-ball::before{content:"\f436"}.fa-brain::before{content:"\f5dc"}.fa-bandage::before{content:"\f462"}.fa-band-aid::before{content:"\f462"}.fa-calendar-minus::before{content:"\f272"}.fa-circle-xmark::before{content:"\f057"}.fa-times-circle::before{content:"\f057"}.fa-xmark-circle::before{content:"\f057"}.fa-gifts::before{content:"\f79c"}.fa-hotel::before{content:"\f594"}.fa-earth-asia::before{content:"\f57e"}.fa-globe-asia::before{content:"\f57e"}.fa-id-card-clip::before{content:"\f47f"}.fa-id-card-alt::before{content:"\f47f"}.fa-magnifying-glass-plus::before{content:"\f00e"}.fa-search-plus::before{content:"\f00e"}.fa-thumbs-up::before{content:"\f164"}.fa-user-clock::before{content:"\f4fd"}.fa-hand-dots::before{content:"\f461"}.fa-allergies::before{content:"\f461"}.fa-file-invoice::before{content:"\f570"}.fa-window-minimize::before{content:"\f2d1"}.fa-mug-saucer::before{content:"\f0f4"}.fa-coffee::before{content:"\f0f4"}.fa-brush::before{content:"\f55d"}.fa-mask::before{content:"\f6fa"}.fa-magnifying-glass-minus::before{content:"\f010"}.fa-search-minus::before{content:"\f010"}.fa-ruler-vertical::before{content:"\f548"}.fa-user-large::before{content:"\f406"}.fa-user-alt::before{content:"\f406"}.fa-train-tram::before{content:"\e5b4"}.fa-user-nurse::before{content:"\f82f"}.fa-syringe::before{content:"\f48e"}.fa-cloud-sun::before{content:"\f6c4"}.fa-stopwatch-20::before{content:"\e06f"}.fa-square-full::before{content:"\f45c"}.fa-magnet::before{content:"\f076"}.fa-jar::before{content:"\e516"}.fa-note-sticky::before{content:"\f249"}.fa-sticky-note::before{content:"\f249"}.fa-bug-slash::before{content:"\e490"}.fa-arrow-up-from-water-pump::before{content:"\e4b6"}.fa-bone::before{content:"\f5d7"}.fa-user-injured::before{content:"\f728"}.fa-face-sad-tear::before{content:"\f5b4"}.fa-sad-tear::before{content:"\f5b4"}.fa-plane::before{content:"\f072"}.fa-tent-arrows-down::before{content:"\e581"}.fa-exclamation::before{content:"\21"}.fa-arrows-spin::before{content:"\e4bb"}.fa-print::before{content:"\f02f"}.fa-turkish-lira-sign::before{content:"\e2bb"}.fa-try::before{content:"\e2bb"}.fa-turkish-lira::before{content:"\e2bb"}.fa-dollar-sign::before{content:"\24"}.fa-dollar::before{content:"\24"}.fa-usd::before{content:"\24"}.fa-x::before{content:"\58"}.fa-magnifying-glass-dollar::before{content:"\f688"}.fa-search-dollar::before{content:"\f688"}.fa-users-gear::before{content:"\f509"}.fa-users-cog::before{content:"\f509"}.fa-person-military-pointing::before{content:"\e54a"}.fa-building-columns::before{content:"\f19c"}.fa-bank::before{content:"\f19c"}.fa-institution::before{content:"\f19c"}.fa-museum::before{content:"\f19c"}.fa-university::before{content:"\f19c"}.fa-umbrella::before{content:"\f0e9"}.fa-trowel::before{content:"\e589"}.fa-d::before{content:"\44"}.fa-stapler::before{content:"\e5af"}.fa-masks-theater::before{content:"\f630"}.fa-theater-masks::before{content:"\f630"}.fa-kip-sign::before{content:"\e1c4"}.fa-hand-point-left::before{content:"\f0a5"}.fa-handshake-simple::before{content:"\f4c6"}.fa-handshake-alt::before{content:"\f4c6"}.fa-jet-fighter::before{content:"\f0fb"}.fa-fighter-jet::before{content:"\f0fb"}.fa-square-share-nodes::before{content:"\f1e1"}.fa-share-alt-square::before{content:"\f1e1"}.fa-barcode::before{content:"\f02a"}.fa-plus-minus::before{content:"\e43c"}.fa-video::before{content:"\f03d"}.fa-video-camera::before{content:"\f03d"}.fa-graduation-cap::before{content:"\f19d"}.fa-mortar-board::before{content:"\f19d"}.fa-hand-holding-medical::before{content:"\e05c"}.fa-person-circle-check::before{content:"\e53e"}.fa-turn-up::before{content:"\f3bf"}.fa-level-up-alt::before{content:"\f3bf"}.sr-only,.fa-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.sr-only-focusable:not(:focus),.fa-sr-only-focusable:not(:focus){position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}/*!* Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com +* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) +* Copyright 2024 Fonticons, Inc.*/:root,:host{--fa-style-family-classic:'Font Awesome 6 Free';--fa-font-solid:normal 900 1em/1 'Font Awesome 6 Free'}@font-face{font-family:'font awesome 6 free';font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2)format("woff2"),url(../webfonts/fa-solid-900.ttf)format("truetype")}.fas,.td-offline-search-results__close-button:after,.fa-solid{font-weight:900}/*!* Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com +* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) +* Copyright 2024 Fonticons, Inc.*/:root,:host{--fa-style-family-brands:'Font Awesome 6 Brands';--fa-font-brands:normal 400 1em/1 'Font Awesome 6 Brands'}@font-face{font-family:'font awesome 6 brands';font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-brands-400.woff2)format("woff2"),url(../webfonts/fa-brands-400.ttf)format("truetype")}.fab,.fa-brands{font-weight:400}.fa-monero:before{content:"\f3d0"}.fa-hooli:before{content:"\f427"}.fa-yelp:before{content:"\f1e9"}.fa-cc-visa:before{content:"\f1f0"}.fa-lastfm:before{content:"\f202"}.fa-shopware:before{content:"\f5b5"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-aws:before{content:"\f375"}.fa-redhat:before{content:"\f7bc"}.fa-yoast:before{content:"\f2b1"}.fa-cloudflare:before{content:"\e07d"}.fa-ups:before{content:"\f7e0"}.fa-pixiv:before{content:"\e640"}.fa-wpexplorer:before{content:"\f2de"}.fa-dyalog:before{content:"\f399"}.fa-bity:before{content:"\f37a"}.fa-stackpath:before{content:"\f842"}.fa-buysellads:before{content:"\f20d"}.fa-first-order:before{content:"\f2b0"}.fa-modx:before{content:"\f285"}.fa-guilded:before{content:"\e07e"}.fa-vnv:before{content:"\f40b"}.fa-square-js:before{content:"\f3b9"}.fa-js-square:before{content:"\f3b9"}.fa-microsoft:before{content:"\f3ca"}.fa-qq:before{content:"\f1d6"}.fa-orcid:before{content:"\f8d2"}.fa-java:before{content:"\f4e4"}.fa-invision:before{content:"\f7b0"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-centercode:before{content:"\f380"}.fa-glide-g:before{content:"\f2a6"}.fa-drupal:before{content:"\f1a9"}.fa-jxl:before{content:"\e67b"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-unity:before{content:"\e049"}.fa-whmcs:before{content:"\f40d"}.fa-rocketchat:before{content:"\f3e8"}.fa-vk:before{content:"\f189"}.fa-untappd:before{content:"\f405"}.fa-mailchimp:before{content:"\f59e"}.fa-css3-alt:before{content:"\f38b"}.fa-square-reddit:before{content:"\f1a2"}.fa-reddit-square:before{content:"\f1a2"}.fa-vimeo-v:before{content:"\f27d"}.fa-contao:before{content:"\f26d"}.fa-square-font-awesome:before{content:"\e5ad"}.fa-deskpro:before{content:"\f38f"}.fa-brave:before{content:"\e63c"}.fa-sistrix:before{content:"\f3ee"}.fa-square-instagram:before{content:"\e055"}.fa-instagram-square:before{content:"\e055"}.fa-battle-net:before{content:"\f835"}.fa-the-red-yeti:before{content:"\f69d"}.fa-square-hacker-news:before{content:"\f3af"}.fa-hacker-news-square:before{content:"\f3af"}.fa-edge:before{content:"\f282"}.fa-threads:before{content:"\e618"}.fa-napster:before{content:"\f3d2"}.fa-square-snapchat:before{content:"\f2ad"}.fa-snapchat-square:before{content:"\f2ad"}.fa-google-plus-g:before{content:"\f0d5"}.fa-artstation:before{content:"\f77a"}.fa-markdown:before{content:"\f60f"}.fa-sourcetree:before{content:"\f7d3"}.fa-google-plus:before{content:"\f2b3"}.fa-diaspora:before{content:"\f791"}.fa-foursquare:before{content:"\f180"}.fa-stack-overflow:before{content:"\f16c"}.fa-github-alt:before{content:"\f113"}.fa-phoenix-squadron:before{content:"\f511"}.fa-pagelines:before{content:"\f18c"}.fa-algolia:before{content:"\f36c"}.fa-red-river:before{content:"\f3e3"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-safari:before{content:"\f267"}.fa-google:before{content:"\f1a0"}.fa-square-font-awesome-stroke:before{content:"\f35c"}.fa-font-awesome-alt:before{content:"\f35c"}.fa-atlassian:before{content:"\f77b"}.fa-linkedin-in:before{content:"\f0e1"}.fa-digital-ocean:before{content:"\f391"}.fa-nimblr:before{content:"\f5a8"}.fa-chromecast:before{content:"\f838"}.fa-evernote:before{content:"\f839"}.fa-hacker-news:before{content:"\f1d4"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-adversal:before{content:"\f36a"}.fa-creative-commons:before{content:"\f25e"}.fa-watchman-monitoring:before{content:"\e087"}.fa-fonticons:before{content:"\f280"}.fa-weixin:before{content:"\f1d7"}.fa-shirtsinbulk:before{content:"\f214"}.fa-codepen:before{content:"\f1cb"}.fa-git-alt:before{content:"\f841"}.fa-lyft:before{content:"\f3c3"}.fa-rev:before{content:"\f5b2"}.fa-windows:before{content:"\f17a"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-square-viadeo:before{content:"\f2aa"}.fa-viadeo-square:before{content:"\f2aa"}.fa-meetup:before{content:"\f2e0"}.fa-centos:before{content:"\f789"}.fa-adn:before{content:"\f170"}.fa-cloudsmith:before{content:"\f384"}.fa-opensuse:before{content:"\e62b"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-square-dribbble:before{content:"\f397"}.fa-dribbble-square:before{content:"\f397"}.fa-codiepie:before{content:"\f284"}.fa-node:before{content:"\f419"}.fa-mix:before{content:"\f3cb"}.fa-steam:before{content:"\f1b6"}.fa-cc-apple-pay:before{content:"\f416"}.fa-scribd:before{content:"\f28a"}.fa-debian:before{content:"\e60b"}.fa-openid:before{content:"\f19b"}.fa-instalod:before{content:"\e081"}.fa-expeditedssl:before{content:"\f23e"}.fa-sellcast:before{content:"\f2da"}.fa-square-twitter:before{content:"\f081"}.fa-twitter-square:before{content:"\f081"}.fa-r-project:before{content:"\f4f7"}.fa-delicious:before{content:"\f1a5"}.fa-freebsd:before{content:"\f3a4"}.fa-vuejs:before{content:"\f41f"}.fa-accusoft:before{content:"\f369"}.fa-ioxhost:before{content:"\f208"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-app-store:before{content:"\f36f"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-itunes-note:before{content:"\f3b5"}.fa-golang:before{content:"\e40f"}.fa-kickstarter:before{content:"\f3bb"}.fa-square-kickstarter:before{content:"\f3bb"}.fa-grav:before{content:"\f2d6"}.fa-weibo:before{content:"\f18a"}.fa-uncharted:before{content:"\e084"}.fa-firstdraft:before{content:"\f3a1"}.fa-square-youtube:before{content:"\f431"}.fa-youtube-square:before{content:"\f431"}.fa-wikipedia-w:before{content:"\f266"}.fa-wpressr:before{content:"\f3e4"}.fa-rendact:before{content:"\f3e4"}.fa-angellist:before{content:"\f209"}.fa-galactic-republic:before{content:"\f50c"}.fa-nfc-directional:before{content:"\e530"}.fa-skype:before{content:"\f17e"}.fa-joget:before{content:"\f3b7"}.fa-fedora:before{content:"\f798"}.fa-stripe-s:before{content:"\f42a"}.fa-meta:before{content:"\e49b"}.fa-laravel:before{content:"\f3bd"}.fa-hotjar:before{content:"\f3b1"}.fa-bluetooth-b:before{content:"\f294"}.fa-square-letterboxd:before{content:"\e62e"}.fa-sticker-mule:before{content:"\f3f7"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-hips:before{content:"\f452"}.fa-behance:before{content:"\f1b4"}.fa-reddit:before{content:"\f1a1"}.fa-discord:before{content:"\f392"}.fa-chrome:before{content:"\f268"}.fa-app-store-ios:before{content:"\f370"}.fa-cc-discover:before{content:"\f1f2"}.fa-wpbeginner:before{content:"\f297"}.fa-confluence:before{content:"\f78d"}.fa-shoelace:before{content:"\e60c"}.fa-mdb:before{content:"\f8ca"}.fa-dochub:before{content:"\f394"}.fa-accessible-icon:before{content:"\f368"}.fa-ebay:before{content:"\f4f4"}.fa-amazon:before{content:"\f270"}.fa-unsplash:before{content:"\e07c"}.fa-yarn:before{content:"\f7e3"}.fa-square-steam:before{content:"\f1b7"}.fa-steam-square:before{content:"\f1b7"}.fa-500px:before{content:"\f26e"}.fa-square-vimeo:before{content:"\f194"}.fa-vimeo-square:before{content:"\f194"}.fa-asymmetrik:before{content:"\f372"}.fa-font-awesome:before{content:"\f2b4"}.fa-font-awesome-flag:before{content:"\f2b4"}.fa-font-awesome-logo-full:before{content:"\f2b4"}.fa-gratipay:before{content:"\f184"}.fa-apple:before{content:"\f179"}.fa-hive:before{content:"\e07f"}.fa-gitkraken:before{content:"\f3a6"}.fa-keybase:before{content:"\f4f5"}.fa-apple-pay:before{content:"\f415"}.fa-padlet:before{content:"\e4a0"}.fa-amazon-pay:before{content:"\f42c"}.fa-square-github:before{content:"\f092"}.fa-github-square:before{content:"\f092"}.fa-stumbleupon:before{content:"\f1a4"}.fa-fedex:before{content:"\f797"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-shopify:before{content:"\e057"}.fa-neos:before{content:"\f612"}.fa-square-threads:before{content:"\e619"}.fa-hackerrank:before{content:"\f5f7"}.fa-researchgate:before{content:"\f4f8"}.fa-swift:before{content:"\f8e1"}.fa-angular:before{content:"\f420"}.fa-speakap:before{content:"\f3f3"}.fa-angrycreative:before{content:"\f36e"}.fa-y-combinator:before{content:"\f23b"}.fa-empire:before{content:"\f1d1"}.fa-envira:before{content:"\f299"}.fa-google-scholar:before{content:"\e63b"}.fa-square-gitlab:before{content:"\e5ae"}.fa-gitlab-square:before{content:"\e5ae"}.fa-studiovinari:before{content:"\f3f8"}.fa-pied-piper:before{content:"\f2ae"}.fa-wordpress:before{content:"\f19a"}.fa-product-hunt:before{content:"\f288"}.fa-firefox:before{content:"\f269"}.fa-linode:before{content:"\f2b8"}.fa-goodreads:before{content:"\f3a8"}.fa-square-odnoklassniki:before{content:"\f264"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-jsfiddle:before{content:"\f1cc"}.fa-sith:before{content:"\f512"}.fa-themeisle:before{content:"\f2b2"}.fa-page4:before{content:"\f3d7"}.fa-hashnode:before{content:"\e499"}.fa-react:before{content:"\f41b"}.fa-cc-paypal:before{content:"\f1f4"}.fa-squarespace:before{content:"\f5be"}.fa-cc-stripe:before{content:"\f1f5"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-bitcoin:before{content:"\f379"}.fa-keycdn:before{content:"\f3ba"}.fa-opera:before{content:"\f26a"}.fa-itch-io:before{content:"\f83a"}.fa-umbraco:before{content:"\f8e8"}.fa-galactic-senate:before{content:"\f50d"}.fa-ubuntu:before{content:"\f7df"}.fa-draft2digital:before{content:"\f396"}.fa-stripe:before{content:"\f429"}.fa-houzz:before{content:"\f27c"}.fa-gg:before{content:"\f260"}.fa-dhl:before{content:"\f790"}.fa-square-pinterest:before{content:"\f0d3"}.fa-pinterest-square:before{content:"\f0d3"}.fa-xing:before{content:"\f168"}.fa-blackberry:before{content:"\f37b"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-playstation:before{content:"\f3df"}.fa-quinscape:before{content:"\f459"}.fa-less:before{content:"\f41d"}.fa-blogger-b:before{content:"\f37d"}.fa-opencart:before{content:"\f23d"}.fa-vine:before{content:"\f1ca"}.fa-signal-messenger:before{content:"\e663"}.fa-paypal:before{content:"\f1ed"}.fa-gitlab:before{content:"\f296"}.fa-typo3:before{content:"\f42b"}.fa-reddit-alien:before{content:"\f281"}.fa-yahoo:before{content:"\f19e"}.fa-dailymotion:before{content:"\e052"}.fa-affiliatetheme:before{content:"\f36b"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-bootstrap:before{content:"\f836"}.fa-odnoklassniki:before{content:"\f263"}.fa-nfc-symbol:before{content:"\e531"}.fa-mintbit:before{content:"\e62f"}.fa-ethereum:before{content:"\f42e"}.fa-speaker-deck:before{content:"\f83c"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-patreon:before{content:"\f3d9"}.fa-avianex:before{content:"\f374"}.fa-ello:before{content:"\f5f1"}.fa-gofore:before{content:"\f3a7"}.fa-bimobject:before{content:"\f378"}.fa-brave-reverse:before{content:"\e63d"}.fa-facebook-f:before{content:"\f39e"}.fa-square-google-plus:before{content:"\f0d4"}.fa-google-plus-square:before{content:"\f0d4"}.fa-web-awesome:before{content:"\e682"}.fa-mandalorian:before{content:"\f50f"}.fa-first-order-alt:before{content:"\f50a"}.fa-osi:before{content:"\f41a"}.fa-google-wallet:before{content:"\f1ee"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-periscope:before{content:"\f3da"}.fa-fulcrum:before{content:"\f50b"}.fa-cloudscale:before{content:"\f383"}.fa-forumbee:before{content:"\f211"}.fa-mizuni:before{content:"\f3cc"}.fa-schlix:before{content:"\f3ea"}.fa-square-xing:before{content:"\f169"}.fa-xing-square:before{content:"\f169"}.fa-bandcamp:before{content:"\f2d5"}.fa-wpforms:before{content:"\f298"}.fa-cloudversify:before{content:"\f385"}.fa-usps:before{content:"\f7e1"}.fa-megaport:before{content:"\f5a3"}.fa-magento:before{content:"\f3c4"}.fa-spotify:before{content:"\f1bc"}.fa-optin-monster:before{content:"\f23c"}.fa-fly:before{content:"\f417"}.fa-aviato:before{content:"\f421"}.fa-itunes:before{content:"\f3b4"}.fa-cuttlefish:before{content:"\f38c"}.fa-blogger:before{content:"\f37c"}.fa-flickr:before{content:"\f16e"}.fa-viber:before{content:"\f409"}.fa-soundcloud:before{content:"\f1be"}.fa-digg:before{content:"\f1a6"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-letterboxd:before{content:"\e62d"}.fa-symfony:before{content:"\f83d"}.fa-maxcdn:before{content:"\f136"}.fa-etsy:before{content:"\f2d7"}.fa-facebook-messenger:before{content:"\f39f"}.fa-audible:before{content:"\f373"}.fa-think-peaks:before{content:"\f731"}.fa-bilibili:before{content:"\e3d9"}.fa-erlang:before{content:"\f39d"}.fa-x-twitter:before{content:"\e61b"}.fa-cotton-bureau:before{content:"\f89e"}.fa-dashcube:before{content:"\f210"}.fa-42-group:before{content:"\e080"}.fa-innosoft:before{content:"\e080"}.fa-stack-exchange:before{content:"\f18d"}.fa-elementor:before{content:"\f430"}.fa-square-pied-piper:before{content:"\e01e"}.fa-pied-piper-square:before{content:"\e01e"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-palfed:before{content:"\f3d8"}.fa-superpowers:before{content:"\f2dd"}.fa-resolving:before{content:"\f3e7"}.fa-xbox:before{content:"\f412"}.fa-square-web-awesome-stroke:before{content:"\e684"}.fa-searchengin:before{content:"\f3eb"}.fa-tiktok:before{content:"\e07b"}.fa-square-facebook:before{content:"\f082"}.fa-facebook-square:before{content:"\f082"}.fa-renren:before{content:"\f18b"}.fa-linux:before{content:"\f17c"}.fa-glide:before{content:"\f2a5"}.fa-linkedin:before{content:"\f08c"}.fa-hubspot:before{content:"\f3b2"}.fa-deploydog:before{content:"\f38e"}.fa-twitch:before{content:"\f1e8"}.fa-ravelry:before{content:"\f2d9"}.fa-mixer:before{content:"\e056"}.fa-square-lastfm:before{content:"\f203"}.fa-lastfm-square:before{content:"\f203"}.fa-vimeo:before{content:"\f40a"}.fa-mendeley:before{content:"\f7b3"}.fa-uniregistry:before{content:"\f404"}.fa-figma:before{content:"\f799"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-dropbox:before{content:"\f16b"}.fa-instagram:before{content:"\f16d"}.fa-cmplid:before{content:"\e360"}.fa-upwork:before{content:"\e641"}.fa-facebook:before{content:"\f09a"}.fa-gripfire:before{content:"\f3ac"}.fa-jedi-order:before{content:"\f50e"}.fa-uikit:before{content:"\f403"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-phabricator:before{content:"\f3db"}.fa-ussunnah:before{content:"\f407"}.fa-earlybirds:before{content:"\f39a"}.fa-trade-federation:before{content:"\f513"}.fa-autoprefixer:before{content:"\f41c"}.fa-whatsapp:before{content:"\f232"}.fa-square-upwork:before{content:"\e67c"}.fa-slideshare:before{content:"\f1e7"}.fa-google-play:before{content:"\f3ab"}.fa-viadeo:before{content:"\f2a9"}.fa-line:before{content:"\f3c0"}.fa-google-drive:before{content:"\f3aa"}.fa-servicestack:before{content:"\f3ec"}.fa-simplybuilt:before{content:"\f215"}.fa-bitbucket:before{content:"\f171"}.fa-imdb:before{content:"\f2d8"}.fa-deezer:before{content:"\e077"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-jira:before{content:"\f7b1"}.fa-docker:before{content:"\f395"}.fa-screenpal:before{content:"\e570"}.fa-bluetooth:before{content:"\f293"}.fa-gitter:before{content:"\f426"}.fa-d-and-d:before{content:"\f38d"}.fa-microblog:before{content:"\e01a"}.fa-cc-diners-club:before{content:"\f24c"}.fa-gg-circle:before{content:"\f261"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-yandex:before{content:"\f413"}.fa-readme:before{content:"\f4d5"}.fa-html5:before{content:"\f13b"}.fa-sellsy:before{content:"\f213"}.fa-square-web-awesome:before{content:"\e683"}.fa-sass:before{content:"\f41e"}.fa-wirsindhandwerk:before{content:"\e2d0"}.fa-wsh:before{content:"\e2d0"}.fa-buromobelexperte:before{content:"\f37f"}.fa-salesforce:before{content:"\f83b"}.fa-octopus-deploy:before{content:"\e082"}.fa-medapps:before{content:"\f3c6"}.fa-ns8:before{content:"\f3d5"}.fa-pinterest-p:before{content:"\f231"}.fa-apper:before{content:"\f371"}.fa-fort-awesome:before{content:"\f286"}.fa-waze:before{content:"\f83f"}.fa-bluesky:before{content:"\e671"}.fa-cc-jcb:before{content:"\f24b"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ab"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-rust:before{content:"\e07a"}.fa-wix:before{content:"\f5cf"}.fa-square-behance:before{content:"\f1b5"}.fa-behance-square:before{content:"\f1b5"}.fa-supple:before{content:"\f3f9"}.fa-webflow:before{content:"\e65c"}.fa-rebel:before{content:"\f1d0"}.fa-css3:before{content:"\f13c"}.fa-staylinked:before{content:"\f3f5"}.fa-kaggle:before{content:"\f5fa"}.fa-space-awesome:before{content:"\e5ac"}.fa-deviantart:before{content:"\f1bd"}.fa-cpanel:before{content:"\f388"}.fa-goodreads-g:before{content:"\f3a9"}.fa-square-git:before{content:"\f1d2"}.fa-git-square:before{content:"\f1d2"}.fa-square-tumblr:before{content:"\f174"}.fa-tumblr-square:before{content:"\f174"}.fa-trello:before{content:"\f181"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-get-pocket:before{content:"\f265"}.fa-perbyte:before{content:"\e083"}.fa-grunt:before{content:"\f3ad"}.fa-weebly:before{content:"\f5cc"}.fa-connectdevelop:before{content:"\f20e"}.fa-leanpub:before{content:"\f212"}.fa-black-tie:before{content:"\f27e"}.fa-themeco:before{content:"\f5c6"}.fa-python:before{content:"\f3e2"}.fa-android:before{content:"\f17b"}.fa-bots:before{content:"\e340"}.fa-free-code-camp:before{content:"\f2c5"}.fa-hornbill:before{content:"\f592"}.fa-js:before{content:"\f3b8"}.fa-ideal:before{content:"\e013"}.fa-git:before{content:"\f1d3"}.fa-dev:before{content:"\f6cc"}.fa-sketch:before{content:"\f7c6"}.fa-yandex-international:before{content:"\f414"}.fa-cc-amex:before{content:"\f1f3"}.fa-uber:before{content:"\f402"}.fa-github:before{content:"\f09b"}.fa-php:before{content:"\f457"}.fa-alipay:before{content:"\f642"}.fa-youtube:before{content:"\f167"}.fa-skyatlas:before{content:"\f216"}.fa-firefox-browser:before{content:"\e007"}.fa-replyd:before{content:"\f3e6"}.fa-suse:before{content:"\f7d6"}.fa-jenkins:before{content:"\f3b6"}.fa-twitter:before{content:"\f099"}.fa-rockrms:before{content:"\f3e9"}.fa-pinterest:before{content:"\f0d2"}.fa-buffer:before{content:"\f837"}.fa-npm:before{content:"\f3d4"}.fa-yammer:before{content:"\f840"}.fa-btc:before{content:"\f15a"}.fa-dribbble:before{content:"\f17d"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-internet-explorer:before{content:"\f26b"}.fa-stubber:before{content:"\e5c7"}.fa-telegram:before{content:"\f2c6"}.fa-telegram-plane:before{content:"\f2c6"}.fa-old-republic:before{content:"\f510"}.fa-odysee:before{content:"\e5c6"}.fa-square-whatsapp:before{content:"\f40c"}.fa-whatsapp-square:before{content:"\f40c"}.fa-node-js:before{content:"\f3d3"}.fa-edge-legacy:before{content:"\e078"}.fa-slack:before{content:"\f198"}.fa-slack-hash:before{content:"\f198"}.fa-medrt:before{content:"\f3c8"}.fa-usb:before{content:"\f287"}.fa-tumblr:before{content:"\f173"}.fa-vaadin:before{content:"\f408"}.fa-quora:before{content:"\f2c4"}.fa-square-x-twitter:before{content:"\e61a"}.fa-reacteurope:before{content:"\f75d"}.fa-medium:before{content:"\f23a"}.fa-medium-m:before{content:"\f23a"}.fa-amilia:before{content:"\f36d"}.fa-mixcloud:before{content:"\f289"}.fa-flipboard:before{content:"\f44d"}.fa-viacoin:before{content:"\f237"}.fa-critical-role:before{content:"\f6c9"}.fa-sitrox:before{content:"\e44a"}.fa-discourse:before{content:"\f393"}.fa-joomla:before{content:"\f1aa"}.fa-mastodon:before{content:"\f4f6"}.fa-airbnb:before{content:"\f834"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-buy-n-large:before{content:"\f8a6"}.fa-gulp:before{content:"\f3ae"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-strava:before{content:"\f428"}.fa-ember:before{content:"\f423"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-teamspeak:before{content:"\f4f9"}.fa-pushed:before{content:"\f3e1"}.fa-wordpress-simple:before{content:"\f411"}.fa-nutritionix:before{content:"\f3d6"}.fa-wodu:before{content:"\e088"}.fa-google-pay:before{content:"\e079"}.fa-intercom:before{content:"\f7af"}.fa-zhihu:before{content:"\f63f"}.fa-korvue:before{content:"\f42f"}.fa-pix:before{content:"\e43a"}.fa-steam-symbol:before{content:"\f3f6"}.td-border-top{border:none;border-top:1px solid #eee}.td-border-none{border:none}.td-block-padding,.td-default main section{padding-top:4rem;padding-bottom:4rem}@media(min-width:768px){.td-block-padding,.td-default main section{padding-top:5rem;padding-bottom:5rem}}.td-overlay{position:relative}.td-overlay::after{content:"";position:absolute;top:0;right:0;bottom:0;left:0}.td-overlay--dark::after{background-color:rgba(51,63,71,.3)}.td-overlay--light::after{background-color:rgba(211,243,238,.3)}.td-overlay__inner{position:relative;z-index:1}@media(min-width:992px){.td-max-width-on-larger-screens,.td-card.card,.td-card-group.card-group,.td-content>.tab-content .tab-pane,.td-content .footnotes,.td-content>.alert,.td-content>.highlight,.td-content>.lead,.td-content>.td-table,.td-box .td-content>table,.td-content>table,.td-content>blockquote,.td-content>dl dd,.td-content>h1,.td-content>.h1,.td-content>h2,.td-content>.h2,.td-content>ol,.td-content>p,.td-content>pre,.td-content>ul{max-width:80%}}.-bg-blue{color:#fff;background-color:#0d6efd}.-bg-blue p:not(.p-initial)>a{color:#81b3fe}.-bg-blue p:not(.p-initial)>a:hover{color:#094db1}.-text-blue{color:#0d6efd}.-bg-indigo{color:#fff;background-color:#6610f2}.-bg-indigo p:not(.p-initial)>a{color:#85b6fe}.-bg-indigo p:not(.p-initial)>a:hover{color:#094db1}.-text-indigo{color:#6610f2}.-bg-purple{color:#fff;background-color:#6f42c1}.-bg-purple p:not(.p-initial)>a{color:#84b5fe}.-bg-purple p:not(.p-initial)>a:hover{color:#094db1}.-text-purple{color:#6f42c1}.-bg-pink{color:#fff;background-color:#d63384}.-bg-pink p:not(.p-initial)>a{color:#81b4fe}.-bg-pink p:not(.p-initial)>a:hover{color:#094db1}.-text-pink{color:#d63384}.-bg-red{color:#fff;background-color:#dc3545}.-bg-red p:not(.p-initial)>a{color:#7db1fe}.-bg-red p:not(.p-initial)>a:hover{color:#094db1}.-text-red{color:#dc3545}.-bg-orange{color:#000;background-color:#fd7e14}.-bg-orange p:not(.p-initial)>a{color:#073b87}.-bg-orange p:not(.p-initial)>a:hover{color:#094db1}.-text-orange{color:#fd7e14}.-bg-yellow{color:#000;background-color:#ffc107}.-bg-yellow p:not(.p-initial)>a{color:#073982}.-bg-yellow p:not(.p-initial)>a:hover{color:#094db1}.-text-yellow{color:#ffc107}.-bg-green{color:#fff;background-color:#198754}.-bg-green p:not(.p-initial)>a{color:#b3d2fe}.-bg-green p:not(.p-initial)>a:hover{color:#094db1}.-text-green{color:#198754}.-bg-teal{color:#000;background-color:#20c997}.-bg-teal p:not(.p-initial)>a{color:#063274}.-bg-teal p:not(.p-initial)>a:hover{color:#094db1}.-text-teal{color:#20c997}.-bg-cyan{color:#000;background-color:#0dcaf0}.-bg-cyan p:not(.p-initial)>a{color:#06377e}.-bg-cyan p:not(.p-initial)>a:hover{color:#094db1}.-text-cyan{color:#0dcaf0}.-bg-black{color:#fff;background-color:#000}.-bg-black p:not(.p-initial)>a{color:#fff}.-bg-black p:not(.p-initial)>a:hover{color:#094db1}.-text-black{color:#000}.-bg-white{color:#000;background-color:#fff}.-bg-white p:not(.p-initial)>a{color:#0d6efd}.-bg-white p:not(.p-initial)>a:hover{color:#094db1}.-text-white{color:#fff}.-bg-gray{color:#fff;background-color:#6c757d}.-bg-gray p:not(.p-initial)>a{color:#90bdfe}.-bg-gray p:not(.p-initial)>a:hover{color:#094db1}.-text-gray{color:#6c757d}.-bg-gray-dark{color:#fff;background-color:#343a40}.-bg-gray-dark p:not(.p-initial)>a{color:#c8deff}.-bg-gray-dark p:not(.p-initial)>a:hover{color:#094db1}.-text-gray-dark{color:#343a40}.-bg-primary{color:#fff;background-color:#5f7681}.-bg-primary p:not(.p-initial)>a{color:#95bffe}.-bg-primary p:not(.p-initial)>a:hover{color:#094db1}.-text-primary{color:#5f7681}.-bg-secondary{color:#000;background-color:#f58301}.-bg-secondary p:not(.p-initial)>a{color:#06357a}.-bg-secondary p:not(.p-initial)>a:hover{color:#094db1}.-text-secondary{color:#f58301}.-bg-success{color:#000;background-color:#3772ff}.-bg-success p:not(.p-initial)>a{color:#08439a}.-bg-success p:not(.p-initial)>a:hover{color:#094db1}.-text-success{color:#3772ff}.-bg-info{color:#fff;background-color:#007ac0}.-bg-info p:not(.p-initial)>a{color:#a4c8fe}.-bg-info p:not(.p-initial)>a:hover{color:#094db1}.-text-info{color:#007ac0}.-bg-warning{color:#000;background-color:#e33030}.-bg-warning p:not(.p-initial)>a{color:#073b88}.-bg-warning p:not(.p-initial)>a:hover{color:#094db1}.-text-warning{color:#e33030}.-bg-danger{color:#000;background-color:#ed6a5a}.-bg-danger p:not(.p-initial)>a{color:#0847a2}.-bg-danger p:not(.p-initial)>a:hover{color:#094db1}.-text-danger{color:#ed6a5a}.-bg-light{color:#000;background-color:#d3f3ee}.-bg-light p:not(.p-initial)>a{color:#0c62e1}.-bg-light p:not(.p-initial)>a:hover{color:#094db1}.-text-light{color:#d3f3ee}.-bg-dark{color:#fff;background-color:#333f47}.-bg-dark p:not(.p-initial)>a{color:#c5dcff}.-bg-dark p:not(.p-initial)>a:hover{color:#094db1}.-text-dark{color:#333f47}.-bg-100{color:#000;background-color:#f8f9fa}.-bg-100 p:not(.p-initial)>a{color:#0d6bf7}.-bg-100 p:not(.p-initial)>a:hover{color:#094db1}.-text-100{color:#f8f9fa}.-bg-200{color:#000;background-color:#e9ecef}.-bg-200 p:not(.p-initial)>a{color:#0c66ea}.-bg-200 p:not(.p-initial)>a:hover{color:#094db1}.-text-200{color:#e9ecef}.-bg-300{color:#000;background-color:#dee2e6}.-bg-300 p:not(.p-initial)>a{color:#0c61e0}.-bg-300 p:not(.p-initial)>a:hover{color:#094db1}.-text-300{color:#dee2e6}.-bg-400{color:#000;background-color:#ced4da}.-bg-400 p:not(.p-initial)>a{color:#0b5bd2}.-bg-400 p:not(.p-initial)>a:hover{color:#094db1}.-text-400{color:#ced4da}.-bg-500{color:#000;background-color:#adb5bd}.-bg-500 p:not(.p-initial)>a{color:#094eb4}.-bg-500 p:not(.p-initial)>a:hover{color:#094db1}.-text-500{color:#adb5bd}.-bg-600{color:#fff;background-color:#6c757d}.-bg-600 p:not(.p-initial)>a{color:#90bdfe}.-bg-600 p:not(.p-initial)>a:hover{color:#094db1}.-text-600{color:#6c757d}.-bg-700{color:#fff;background-color:#495057}.-bg-700 p:not(.p-initial)>a{color:#b3d2fe}.-bg-700 p:not(.p-initial)>a:hover{color:#094db1}.-text-700{color:#495057}.-bg-800{color:#fff;background-color:#343a40}.-bg-800 p:not(.p-initial)>a{color:#c8deff}.-bg-800 p:not(.p-initial)>a:hover{color:#094db1}.-text-800{color:#343a40}.-bg-900{color:#fff;background-color:#212529}.-bg-900 p:not(.p-initial)>a{color:#dceaff}.-bg-900 p:not(.p-initial)>a:hover{color:#094db1}.-text-900{color:#212529}.-bg-0{color:#fff;background-color:#403f4c}.-bg-0 p:not(.p-initial)>a{color:#bdd7fe}.-bg-0 p:not(.p-initial)>a:hover{color:#094db1}.-text-0{color:#403f4c}.-bg-1{color:#fff;background-color:#30638e}.-bg-1 p:not(.p-initial)>a{color:#a5c9fe}.-bg-1 p:not(.p-initial)>a:hover{color:#094db1}.-text-1{color:#30638e}.-bg-2{color:#000;background-color:#ffa630}.-bg-2 p:not(.p-initial)>a{color:#084196}.-bg-2 p:not(.p-initial)>a:hover{color:#094db1}.-text-2{color:#ffa630}.-bg-3{color:#000;background-color:#c0e0de}.-bg-3 p:not(.p-initial)>a{color:#0b5ace}.-bg-3 p:not(.p-initial)>a:hover{color:#094db1}.-text-3{color:#c0e0de}.-bg-4{color:#000;background-color:#fff}.-bg-4 p:not(.p-initial)>a{color:#0d6efd}.-bg-4 p:not(.p-initial)>a:hover{color:#094db1}.-text-4{color:#fff}.-bg-5{color:#fff;background-color:#6c757d}.-bg-5 p:not(.p-initial)>a{color:#90bdfe}.-bg-5 p:not(.p-initial)>a:hover{color:#094db1}.-text-5{color:#6c757d}.-bg-6{color:#000;background-color:#3772ff}.-bg-6 p:not(.p-initial)>a{color:#08439a}.-bg-6 p:not(.p-initial)>a:hover{color:#094db1}.-text-6{color:#3772ff}.-bg-7{color:#000;background-color:#ed6a5a}.-bg-7 p:not(.p-initial)>a{color:#0847a2}.-bg-7 p:not(.p-initial)>a:hover{color:#094db1}.-text-7{color:#ed6a5a}.-bg-8{color:#fff;background-color:#403f4c}.-bg-8 p:not(.p-initial)>a{color:#bdd7fe}.-bg-8 p:not(.p-initial)>a:hover{color:#094db1}.-text-8{color:#403f4c}.-bg-9{color:#000;background-color:#ed6a5a}.-bg-9 p:not(.p-initial)>a{color:#0847a2}.-bg-9 p:not(.p-initial)>a:hover{color:#094db1}.-text-9{color:#ed6a5a}.-bg-10{color:#fff;background-color:#30638e}.-bg-10 p:not(.p-initial)>a{color:#a5c9fe}.-bg-10 p:not(.p-initial)>a:hover{color:#094db1}.-text-10{color:#30638e}.-bg-11{color:#000;background-color:#ffa630}.-bg-11 p:not(.p-initial)>a{color:#084196}.-bg-11 p:not(.p-initial)>a:hover{color:#094db1}.-text-11{color:#ffa630}.-bg-12{color:#000;background-color:#c0e0de}.-bg-12 p:not(.p-initial)>a{color:#0b5ace}.-bg-12 p:not(.p-initial)>a:hover{color:#094db1}.-text-12{color:#c0e0de}.td-table:not(.td-initial),.td-content table:not(.td-initial),.td-box table:not(.td-initial){display:block}.td-box--height-min{min-height:300px}.td-box--height-med{min-height:400px}.td-box--height-max{min-height:500px}.td-box--height-full{min-height:100vh}@media(min-width:768px){.td-box--height-min{min-height:450px}.td-box--height-med{min-height:500px}.td-box--height-max{min-height:650px}}.td-box .row{padding-left:5vw;padding-right:5vw}.td-box.linkbox{padding:5vh 5vw}.td-box--0{color:#fff;background-color:#403f4c}.td-box--0 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#403f4c transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--0 p>a,.td-box--0 span>a{color:#bdd7fe}.td-box--0 p>a:hover,.td-box--0 span>a:hover{color:#d1e3fe}.td-box--1{color:#fff;background-color:#30638e}.td-box--1 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#30638e transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--1 p>a,.td-box--1 span>a{color:#a5c9fe}.td-box--1 p>a:hover,.td-box--1 span>a:hover{color:#c0d9fe}.td-box--2{color:#000;background-color:#ffa630}.td-box--2 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#ffa630 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--2 p>a,.td-box--2 span>a{color:#084196}.td-box--2 p>a:hover,.td-box--2 span>a:hover{color:#062e69}.td-box--3{color:#000;background-color:#c0e0de}.td-box--3 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#c0e0de transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--3 p>a,.td-box--3 span>a{color:#0b5ace}.td-box--3 p>a:hover,.td-box--3 span>a:hover{color:#083f90}.td-box--4{color:#000;background-color:#fff}.td-box--4 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#fff transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--4 p>a,.td-box--4 span>a{color:#0d6efd}.td-box--4 p>a:hover,.td-box--4 span>a:hover{color:#094db1}.td-box--5{color:#fff;background-color:#6c757d}.td-box--5 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#6c757d transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--5 p>a,.td-box--5 span>a{color:#90bdfe}.td-box--5 p>a:hover,.td-box--5 span>a:hover{color:#b1d1fe}.td-box--6{color:#000;background-color:#3772ff}.td-box--6 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#3772ff transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--6 p>a,.td-box--6 span>a{color:#08439a}.td-box--6 p>a:hover,.td-box--6 span>a:hover{color:#062f6c}.td-box--7{color:#000;background-color:#ed6a5a}.td-box--7 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#ed6a5a transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--7 p>a,.td-box--7 span>a{color:#0847a2}.td-box--7 p>a:hover,.td-box--7 span>a:hover{color:#063271}.td-box--8{color:#fff;background-color:#403f4c}.td-box--8 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#403f4c transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--8 p>a,.td-box--8 span>a{color:#bdd7fe}.td-box--8 p>a:hover,.td-box--8 span>a:hover{color:#d1e3fe}.td-box--9{color:#000;background-color:#ed6a5a}.td-box--9 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#ed6a5a transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--9 p>a,.td-box--9 span>a{color:#0847a2}.td-box--9 p>a:hover,.td-box--9 span>a:hover{color:#063271}.td-box--10{color:#fff;background-color:#30638e}.td-box--10 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#30638e transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--10 p>a,.td-box--10 span>a{color:#a5c9fe}.td-box--10 p>a:hover,.td-box--10 span>a:hover{color:#c0d9fe}.td-box--11{color:#000;background-color:#ffa630}.td-box--11 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#ffa630 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--11 p>a,.td-box--11 span>a{color:#084196}.td-box--11 p>a:hover,.td-box--11 span>a:hover{color:#062e69}.td-box--12{color:#000;background-color:#c0e0de}.td-box--12 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#c0e0de transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--12 p>a,.td-box--12 span>a{color:#0b5ace}.td-box--12 p>a:hover,.td-box--12 span>a:hover{color:#083f90}.td-box--blue{color:#fff;background-color:#0d6efd}.td-box--blue .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#0d6efd transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--blue p>a,.td-box--blue span>a{color:#81b3fe}.td-box--blue p>a:hover,.td-box--blue span>a:hover{color:#a7cafe}.td-box--indigo{color:#fff;background-color:#6610f2}.td-box--indigo .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#6610f2 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--indigo p>a,.td-box--indigo span>a{color:#85b6fe}.td-box--indigo p>a:hover,.td-box--indigo span>a:hover{color:#aaccfe}.td-box--purple{color:#fff;background-color:#6f42c1}.td-box--purple .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#6f42c1 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--purple p>a,.td-box--purple span>a{color:#84b5fe}.td-box--purple p>a:hover,.td-box--purple span>a:hover{color:#a9cbfe}.td-box--pink{color:#fff;background-color:#d63384}.td-box--pink .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#d63384 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--pink p>a,.td-box--pink span>a{color:#81b4fe}.td-box--pink p>a:hover,.td-box--pink span>a:hover{color:#a7cbfe}.td-box--red{color:#fff;background-color:#dc3545}.td-box--red .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#dc3545 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--red p>a,.td-box--red span>a{color:#7db1fe}.td-box--red p>a:hover,.td-box--red span>a:hover{color:#a4c8fe}.td-box--orange{color:#000;background-color:#fd7e14}.td-box--orange .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#fd7e14 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--orange p>a,.td-box--orange span>a{color:#073b87}.td-box--orange p>a:hover,.td-box--orange span>a:hover{color:#05295f}.td-box--yellow{color:#000;background-color:#ffc107}.td-box--yellow .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#ffc107 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--yellow p>a,.td-box--yellow span>a{color:#073982}.td-box--yellow p>a:hover,.td-box--yellow span>a:hover{color:#05285b}.td-box--green{color:#fff;background-color:#198754}.td-box--green .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#198754 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--green p>a,.td-box--green span>a{color:#b3d2fe}.td-box--green p>a:hover,.td-box--green span>a:hover{color:#cae0fe}.td-box--teal{color:#000;background-color:#20c997}.td-box--teal .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#20c997 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--teal p>a,.td-box--teal span>a{color:#063274}.td-box--teal p>a:hover,.td-box--teal span>a:hover{color:#042351}.td-box--cyan{color:#000;background-color:#0dcaf0}.td-box--cyan .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#0dcaf0 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--cyan p>a,.td-box--cyan span>a{color:#06377e}.td-box--cyan p>a:hover,.td-box--cyan span>a:hover{color:#042758}.td-box--black{color:#fff;background-color:#000}.td-box--black .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#000 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--black p>a,.td-box--black span>a{color:#fff}.td-box--black p>a:hover,.td-box--black span>a:hover{color:#fff}.td-box--white{color:#000;background-color:#fff}.td-box--white .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#fff transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--white p>a,.td-box--white span>a{color:#0d6efd}.td-box--white p>a:hover,.td-box--white span>a:hover{color:#094db1}.td-box--gray{color:#fff;background-color:#6c757d}.td-box--gray .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#6c757d transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--gray p>a,.td-box--gray span>a{color:#90bdfe}.td-box--gray p>a:hover,.td-box--gray span>a:hover{color:#b1d1fe}.td-box--gray-dark{color:#fff;background-color:#343a40}.td-box--gray-dark .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#343a40 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--gray-dark p>a,.td-box--gray-dark span>a{color:#c8deff}.td-box--gray-dark p>a:hover,.td-box--gray-dark span>a:hover{color:#d9e8ff}.td-box--primary{color:#fff;background-color:#5f7681}.td-box--primary .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#5f7681 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--primary p>a,.td-box--primary span>a{color:#95bffe}.td-box--primary p>a:hover,.td-box--primary span>a:hover{color:#b5d2fe}.td-box--secondary{color:#000;background-color:#f58301}.td-box--secondary .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#f58301 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--secondary p>a,.td-box--secondary span>a{color:#06357a}.td-box--secondary p>a:hover,.td-box--secondary span>a:hover{color:#042555}.td-box--success{color:#000;background-color:#3772ff}.td-box--success .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#3772ff transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--success p>a,.td-box--success span>a{color:#08439a}.td-box--success p>a:hover,.td-box--success span>a:hover{color:#062f6c}.td-box--info{color:#fff;background-color:#007ac0}.td-box--info .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#007ac0 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--info p>a,.td-box--info span>a{color:#a4c8fe}.td-box--info p>a:hover,.td-box--info span>a:hover{color:#bfd9fe}.td-box--warning{color:#000;background-color:#e33030}.td-box--warning .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#e33030 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--warning p>a,.td-box--warning span>a{color:#073b88}.td-box--warning p>a:hover,.td-box--warning span>a:hover{color:#05295f}.td-box--danger{color:#000;background-color:#ed6a5a}.td-box--danger .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#ed6a5a transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--danger p>a,.td-box--danger span>a{color:#0847a2}.td-box--danger p>a:hover,.td-box--danger span>a:hover{color:#063271}.td-box--light{color:#000;background-color:#d3f3ee}.td-box--light .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#d3f3ee transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--light p>a,.td-box--light span>a{color:#0c62e1}.td-box--light p>a:hover,.td-box--light span>a:hover{color:#08459e}.td-box--dark,.td-footer{color:#fff;background-color:#333f47}.td-box--dark .td-arrow-down::before,.td-footer .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#333f47 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--dark p>a,.td-footer p>a,.td-box--dark span>a,.td-footer span>a{color:#c5dcff}.td-box--dark p>a:hover,.td-footer p>a:hover,.td-box--dark span>a:hover,.td-footer span>a:hover{color:#d6e7ff}.td-box--100{color:#000;background-color:#f8f9fa}.td-box--100 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#f8f9fa transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--100 p>a,.td-box--100 span>a{color:#0d6bf7}.td-box--100 p>a:hover,.td-box--100 span>a:hover{color:#094bad}.td-box--200{color:#000;background-color:#e9ecef}.td-box--200 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#e9ecef transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--200 p>a,.td-box--200 span>a{color:#0c66ea}.td-box--200 p>a:hover,.td-box--200 span>a:hover{color:#0847a4}.td-box--300{color:#000;background-color:#dee2e6}.td-box--300 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#dee2e6 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--300 p>a,.td-box--300 span>a{color:#0c61e0}.td-box--300 p>a:hover,.td-box--300 span>a:hover{color:#08449d}.td-box--400{color:#000;background-color:#ced4da}.td-box--400 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#ced4da transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--400 p>a,.td-box--400 span>a{color:#0b5bd2}.td-box--400 p>a:hover,.td-box--400 span>a:hover{color:#084093}.td-box--500{color:#000;background-color:#adb5bd}.td-box--500 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#adb5bd transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--500 p>a,.td-box--500 span>a{color:#094eb4}.td-box--500 p>a:hover,.td-box--500 span>a:hover{color:#06377e}.td-box--600{color:#fff;background-color:#6c757d}.td-box--600 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#6c757d transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--600 p>a,.td-box--600 span>a{color:#90bdfe}.td-box--600 p>a:hover,.td-box--600 span>a:hover{color:#b1d1fe}.td-box--700{color:#fff;background-color:#495057}.td-box--700 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#495057 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--700 p>a,.td-box--700 span>a{color:#b3d2fe}.td-box--700 p>a:hover,.td-box--700 span>a:hover{color:#cae0fe}.td-box--800{color:#fff;background-color:#343a40}.td-box--800 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#343a40 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--800 p>a,.td-box--800 span>a{color:#c8deff}.td-box--800 p>a:hover,.td-box--800 span>a:hover{color:#d9e8ff}.td-box--900{color:#fff;background-color:#212529}.td-box--900 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#212529 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--900 p>a,.td-box--900 span>a{color:#dceaff}.td-box--900 p>a:hover,.td-box--900 span>a:hover{color:#e7f0ff}[data-bs-theme=dark] .td-box--white{color:var(--bs-body-color);background-color:var(--bs-body-bg)}[data-bs-theme=dark] .td-box--white p>a,[data-bs-theme=dark] .td-box--white span>a{color:var(--bs-link-color)}[data-bs-theme=dark] .td-box--white p>a:focus,[data-bs-theme=dark] .td-box--white p>a:hover,[data-bs-theme=dark] .td-box--white span>a:focus,[data-bs-theme=dark] .td-box--white span>a:hover{color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,1))}[data-bs-theme=dark] .td-box--white .td-arrow-down::before{border-color:var(--bs-body-bg)transparent transparent transparent}.td-blog .td-rss-button{border-radius:2rem;float:right;display:none}.td-blog-posts-list{margin-top:1.5rem!important}.td-blog-posts-list__item{display:flex;align-items:flex-start;margin-bottom:1.5rem!important}.td-blog-posts-list__item__body{flex:1}[data-bs-theme=dark]{--td-pre-bg:#1b1f22}.td-content .highlight{margin:2rem 0;padding:0;position:relative}.td-content .highlight .click-to-copy{display:block;text-align:right}.td-content .highlight pre{margin:0;padding:1rem;border-radius:inherit}.td-content .highlight pre button.td-click-to-copy{position:absolute;color:var(--bs-tertiary-color);border-width:0;background-color:transparent;background-image:none;--bs-btn-box-shadow:0;padding:var(--bs-btn-padding-y)calc(var(--bs-btn-padding-x)/2);right:4px;top:2px}.td-content .highlight pre button.td-click-to-copy:hover{color:var(--bs-secondary-color);background-color:var(--bs-dark-bg-subtle)}.td-content .highlight pre button.td-click-to-copy:active{color:var(--bs-secondary-color);background-color:var(--bs-dark-bg-subtle);transform:translateY(2px)}.td-content p code,.td-content li>code,.td-content table code{color:inherit;padding:.2em .4em;margin:0;font-size:85%;word-break:normal;background-color:var(--td-pre-bg);border-radius:.375rem}.td-content p code br,.td-content li>code br,.td-content table code br{display:none}.td-content pre{word-wrap:normal;background-color:var(--td-pre-bg);border:solid var(--bs-border-color);border-width:1px;padding:1rem}.td-content pre>code{background-color:inherit!important;padding:0;margin:0;font-size:100%;word-break:normal;white-space:pre;border:0}.td-content pre.mermaid{background-color:inherit;font-size:0;padding:0}@media(min-width:768px){.td-navbar-cover{background:0 0!important}.td-navbar-cover .nav-link{text-shadow:1px 1px 2px #333f47}}.td-navbar-cover.navbar-bg-onscroll .nav-link{text-shadow:none}.navbar-bg-onscroll{background:#5f7681!important;opacity:inherit}.td-navbar{background:#5f7681;min-height:4rem;margin:0;z-index:32}.td-navbar .navbar-brand{text-transform:none}.td-navbar .navbar-brand__name{font-weight:700}.td-navbar .navbar-brand svg{display:inline-block;margin:0 10px;height:30px}.td-navbar .navbar-nav{padding-top:.5rem;white-space:nowrap}.td-navbar .nav-link{text-transform:none;font-weight:700}.td-navbar .dropdown{min-width:50px}@media(min-width:768px){.td-navbar{position:fixed;top:0;width:100%}.td-navbar .nav-item{padding-inline-end:.5rem}.td-navbar .navbar-nav{padding-top:0!important}}@media(max-width:991.98px){.td-navbar .td-navbar-nav-scroll{max-width:100%;height:2.5rem;overflow:hidden;font-size:.9rem}.td-navbar .navbar-brand{margin-right:0}.td-navbar .navbar-nav{padding-bottom:2rem;overflow-x:auto}}.td-navbar .td-light-dark-menu .bi{width:1em;height:1em;vertical-align:-.125em;fill:currentcolor}@media(max-width:991.98px){.td-navbar .td-light-dark-menu.dropdown{position:unset}}#main_navbar li i{padding-right:.5em}#main_navbar li i:before{display:inline-block;text-align:center;min-width:1em}#main_navbar .alert{background-color:inherit;padding:0;color:#f58301;border:0;font-weight:inherit}#main_navbar .alert:before{display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;-webkit-font-smoothing:antialiased;font-family:"font awesome 6 free";font-weight:900;content:"\f0d9";padding-left:.5em;padding-right:.5em}nav.foldable-nav#td-section-nav{position:relative}nav.foldable-nav#td-section-nav label{margin-bottom:0;width:100%}nav.foldable-nav .td-sidebar-nav__section,nav.foldable-nav .with-child ul{list-style:none;padding:0;margin:0}nav.foldable-nav .ul-1>li{padding-left:1.5em}nav.foldable-nav ul.foldable{display:none}nav.foldable-nav input:checked~ul.foldable{display:block}nav.foldable-nav input[type=checkbox]{display:none}nav.foldable-nav .with-child,nav.foldable-nav .without-child{position:relative;padding-left:1.5em}nav.foldable-nav .ul-1 .with-child>label:before{display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;-webkit-font-smoothing:antialiased;font-family:"font awesome 6 free";font-weight:900;content:"\f0da";position:absolute;left:.1em;padding-left:.4em;padding-right:.4em;font-size:1em;color:var(--bs-secondary-color);transition:all .5s}nav.foldable-nav .ul-1 .with-child>label:before:hover{transform:rotate(90deg)}nav.foldable-nav .ul-1 .with-child>input:checked~label:before{color:var(--bs-secondary-color);transform:rotate(90deg);transition:transform .5s}nav.foldable-nav .with-child ul{margin-top:.1em}@media(hover:hover) and (pointer:fine){nav.foldable-nav .ul-1 .with-child>label:hover:before{color:var(--bs-link-color);transition:color .3s}nav.foldable-nav .ul-1 .with-child>input:checked~label:hover:before{color:var(--bs-link-color);transition:color .3s}}.td-sidebar-nav{padding-right:.5rem;margin-right:-15px;margin-left:-15px}@media(min-width:768px){@supports(position:sticky){.td-sidebar-nav{max-height:calc(100vh - 8.5rem);overflow-y:auto}}}@media(min-width:992px){.td-sidebar-nav.td-sidebar-nav--search-disabled{padding-top:1rem}@supports(position:sticky){.td-sidebar-nav.td-sidebar-nav--search-disabled{max-height:calc(calc(100vh - 8.5rem) + 4.5rem)}}}@media(min-width:768px){.td-sidebar-nav{display:block!important}}.td-sidebar-nav__section{padding-left:0}.td-sidebar-nav__section li{list-style:none}.td-sidebar-nav__section.ul-0,.td-sidebar-nav__section ul{padding:0;margin:0}@media(min-width:768px){.td-sidebar-nav__section .ul-1 ul{padding-left:1.5em}}.td-sidebar-nav__section-title{display:block;font-weight:500}.td-sidebar-nav__section-title .active{font-weight:700}.td-sidebar-nav__section-title a{color:var(--bs-secondary-color)}.td-sidebar-nav .td-sidebar-link{display:block;padding-bottom:.375rem}.td-sidebar-nav .td-sidebar-link__page{color:var(--bs-body-color);font-weight:300}.td-sidebar-nav a:focus,.td-sidebar-nav a:hover{color:var(--bs-link-color)}.td-sidebar-nav a.active{font-weight:700}.td-sidebar-nav .dropdown a{color:var(--bs-tertiary-color)}.td-sidebar-nav .dropdown .nav-link{padding:0 0 1rem}.td-sidebar-nav>.td-sidebar-nav__section{padding-left:1.5rem}.td-sidebar-nav li i{padding-right:.5em}.td-sidebar-nav li i:before{display:inline-block;text-align:center;min-width:1em}.td-sidebar-nav .td-sidebar-link.tree-root{font-weight:700;border-bottom:1px solid var(--bs-tertiary-color);margin-bottom:1rem}.td-sidebar{padding-bottom:1rem}.td-sidebar a{text-decoration:none}.td-sidebar a:focus,.td-sidebar a:hover{text-decoration:initial}.td-sidebar .btn-link{text-decoration:none}@media(min-width:768px){.td-sidebar{padding-top:4rem;background-color:var(--bs-body-tertiary-bg);padding-right:1rem;border-right:1px solid var(--bs-border-color)}}.td-sidebar__toggle{line-height:1;color:var(--bs-body-color);margin:1rem}.td-sidebar__search{padding:1rem 0}.td-sidebar__inner{order:0}@media(min-width:768px){@supports(position:sticky){.td-sidebar__inner{position:sticky;top:4rem;z-index:10;height:calc(100vh - 5rem)}}}@media(min-width:1200px){.td-sidebar__inner{flex:0 1 320px}}.td-sidebar__inner .td-search-box{width:100%}.td-sidebar #content-desktop{display:block}.td-sidebar #content-mobile{display:none}@media(max-width:991.98px){.td-sidebar #content-desktop{display:none}.td-sidebar #content-mobile{display:block}}.td-sidebar-toc{border-left:1px solid var(--bs-border-color);order:2;padding-top:.75rem;padding-bottom:1.5rem;vertical-align:top}.td-sidebar-toc a{text-decoration:none}.td-sidebar-toc a:focus,.td-sidebar-toc a:hover{text-decoration:initial}.td-sidebar-toc .btn-link{text-decoration:none}@supports(position:sticky){.td-sidebar-toc{position:sticky;top:4rem;height:calc(100vh - 4rem);overflow-y:auto}}.td-sidebar-toc .td-page-meta a{display:block;font-weight:500}.td-toc a{display:block;font-weight:300;padding-bottom:.25rem}.td-toc li{list-style:none;display:block}.td-toc li li{margin-left:.5rem}.td-toc #TableOfContents a{color:var(--bs-secondary-color)}.td-toc #TableOfContents a:focus,.td-toc #TableOfContents a:hover{color:initial}.td-toc ul{padding-left:0}@media print{.td-breadcrumbs{display:none!important}}.td-breadcrumbs .breadcrumb{background:inherit;padding-left:0;padding-top:0}.alert{font-weight:500;background:#fff;color:inherit;border-radius:0}.alert-primary{border-style:solid;border-color:#5f7681;border-width:0 0 0 4px}.alert-primary .alert-heading{color:#5f7681}.alert-secondary{border-style:solid;border-color:#f58301;border-width:0 0 0 4px}.alert-secondary .alert-heading{color:#f58301}.alert-success{border-style:solid;border-color:#3772ff;border-width:0 0 0 4px}.alert-success .alert-heading{color:#3772ff}.alert-info{border-style:solid;border-color:#007ac0;border-width:0 0 0 4px}.alert-info .alert-heading{color:#007ac0}.alert-warning{border-style:solid;border-color:#e33030;border-width:0 0 0 4px}.alert-warning .alert-heading{color:#e33030}.alert-danger{border-style:solid;border-color:#ed6a5a;border-width:0 0 0 4px}.alert-danger .alert-heading{color:#ed6a5a}.alert-light{border-style:solid;border-color:#d3f3ee;border-width:0 0 0 4px}.alert-light .alert-heading{color:#d3f3ee}.alert-dark{border-style:solid;border-color:#333f47;border-width:0 0 0 4px}.alert-dark .alert-heading{color:#333f47}.td-content{order:1}.td-content p,.td-content li,.td-content td{font-weight:400}.td-content>h1,.td-content>.h1{font-weight:700;margin-bottom:1rem}.td-content>h2,.td-content>.h2{margin-bottom:1rem}.td-content>h2:not(:first-child),.td-content>.h2:not(:first-child){margin-top:3rem}.td-content>h2+h3,.td-content>.h2+h3,.td-content>h2+.h3,.td-content>h2+.td-footer__links-item,.td-content>.h2+.h3,.td-content>.h2+.td-footer__links-item{margin-top:1rem}.td-content>h3,.td-content>.h3,.td-content>.td-footer__links-item,.td-content>h4,.td-content>.h4,.td-content>h5,.td-content>.h5,.td-content>h6,.td-content>.h6{margin-bottom:1rem;margin-top:2rem}.td-content blockquote{padding:0 0 0 1rem;margin-bottom:1rem;color:var(--bs-secondary-color);border-left:6px solid var(--bs-primary)}.td-content ul li,.td-content ol li{margin-bottom:.25rem}.td-content strong{font-weight:700}.td-content .alert:not(:first-child){margin-top:2rem;margin-bottom:2rem}.td-content .lead{margin-bottom:1.5rem}.td-title{margin-top:1rem;margin-bottom:.5rem}@media(min-width:576px){.td-title{font-size:3rem}}.td-heading-self-link{font-size:90%;padding-left:.25em;text-decoration:none;visibility:hidden}.td-heading-self-link:before{content:'#'}@media(hover:none) and (pointer:coarse),(max-width:576px){.td-heading-self-link{visibility:visible}}h1:hover>.td-heading-self-link,.h1:hover>.td-heading-self-link{visibility:visible}h2:hover>.td-heading-self-link,.h2:hover>.td-heading-self-link{visibility:visible}h3:hover>.td-heading-self-link,.h3:hover>.td-heading-self-link,.td-footer__links-item:hover>.td-heading-self-link{visibility:visible}h4:hover>.td-heading-self-link,.h4:hover>.td-heading-self-link{visibility:visible}h5:hover>.td-heading-self-link,.h5:hover>.td-heading-self-link{visibility:visible}h6:hover>.td-heading-self-link,.h6:hover>.td-heading-self-link{visibility:visible}.td-search{background:0 0;position:relative;width:100%}.td-search__icon{display:flex;align-items:center;height:100%;position:absolute;left:.75em;pointer-events:none}.td-search__icon:before{content:"\f002"}.td-navbar .td-search__icon{color:inherit}.td-search__input{width:100%;text-indent:1.25em}.td-search__input:not(:focus){background:0 0}.td-search__input.form-control:focus{border-color:#f7f8f9;box-shadow:0 0 0 2px #9fadb3;color:var(--bs-body-color)}.td-navbar .td-search__input{border:none;color:inherit}.td-navbar .td-search__input::-webkit-input-placeholder{color:inherit}.td-navbar .td-search__input:-moz-placeholder{color:inherit}.td-navbar .td-search__input::-moz-placeholder{color:inherit}.td-navbar .td-search__input:-ms-input-placeholder{color:inherit}.td-search:focus-within .td-search__icon{display:none}.td-search:focus-within .td-search-input{text-indent:0}.td-search:not(:focus-within){color:var(--bs-secondary-color)}.td-sidebar .td-search--algolia{display:block;padding:0 .5rem}.td-sidebar .td-search--algolia>button{margin:0;width:100%}.td-search--offline:focus-within .td-search__icon{display:flex;color:var(--bs-secondary-color)}.td-offline-search-results{max-width:90%}.td-offline-search-results .card{margin-bottom:.5rem}.td-offline-search-results .card .card-header{font-weight:700}.td-offline-search-results__close-button{float:right}.td-offline-search-results__close-button:after{content:"\f00d"}.td-outer{display:flex;flex-direction:column;min-height:100vh}@media(min-width:768px){.td-default main>section:first-of-type{padding-top:8rem}}.td-main{flex-grow:1}.td-404 main,.td-main main{padding-top:1.5rem;padding-bottom:2rem}@media(min-width:768px){.td-404 main,.td-main main{padding-top:5.5rem}}.td-cover-block--height-min{min-height:300px}.td-cover-block--height-med{min-height:400px}.td-cover-block--height-max{min-height:500px}.td-cover-block--height-full{min-height:100vh}@media(min-width:768px){.td-cover-block--height-min{min-height:450px}.td-cover-block--height-med{min-height:500px}.td-cover-block--height-max{min-height:650px}}.td-cover-logo{margin-right:.5em}.td-cover-block{position:relative;padding-top:5rem;padding-bottom:5rem;background-repeat:no-repeat;background-position:50% 0;background-size:cover}.td-cover-block>.byline{position:absolute;bottom:2px;right:4px}.td-bg-arrow-wrapper{position:relative}.section-index .entry{padding:.75rem}.section-index h5,.section-index .h5{margin-bottom:0}.section-index h5 a,.section-index .h5 a{font-weight:700}.section-index p{margin-top:0}.pageinfo{font-weight:500;background:#f8f9fa;color:inherit;border-radius:0;margin:2rem;padding:1.5rem;padding-bottom:.5rem}.pageinfo-primary{border-style:solid;border-color:#5f7681}.pageinfo-secondary{border-style:solid;border-color:#5f7681}.pageinfo-success{border-style:solid;border-color:#5f7681}.pageinfo-info{border-style:solid;border-color:#5f7681}.pageinfo-warning{border-style:solid;border-color:#5f7681}.pageinfo-danger{border-style:solid;border-color:#5f7681}.pageinfo-light{border-style:solid;border-color:#5f7681}.pageinfo-dark{border-style:solid;border-color:#5f7681}.td-page-meta__lastmod{margin-top:3rem!important;padding-top:1rem!important}.taxonomy-terms-article{width:100%;clear:both;font-size:.8rem}.taxonomy-terms-article .taxonomy-title{display:inline;font-size:1.25em;height:1em;line-height:1em;margin-right:.5em;padding:0}.taxonomy-terms-cloud{width:100%;clear:both;font-size:.8rem}.taxonomy-terms-cloud .taxonomy-title{display:inline-block;width:100%;font-size:1rem;font-weight:700;color:var(--bs-primary-text-emphasis);border-bottom:1px solid var(--bs-tertiary-color);margin-bottom:1em;padding-bottom:.375rem;margin-top:1em}.taxonomy-terms-page{max-width:800px;margin:auto}.taxonomy-terms-page h1,.taxonomy-terms-page .h1{margin-bottom:1em}.taxonomy-terms-page .taxonomy-terms-cloud{font-size:1em}.taxonomy-terms-page .taxonomy-terms-cloud li{display:block}.taxonomy-terms-page .taxo-text-tags li+li::before{content:none}.taxonomy-terms-page .taxo-fruits .taxonomy-count,.taxonomy-terms-page .taxo-fruits .taxonomy-label{display:inherit;font-size:1rem;margin:0;padding:0;padding-right:.5em}.taxonomy-terms-page .taxo-fruits .taxonomy-count::before{content:"("}.taxonomy-terms-page .taxo-fruits .taxonomy-count::after{content:")"}.taxonomy-terms{list-style:none;margin:0;overflow:hidden;padding:0;display:inline}.taxonomy-terms li{display:inline;overflow-wrap:break-word;word-wrap:break-word;-ms-word-break:break-all;word-break:break-all;word-break:break-word;-webkit-hyphens:auto;hyphens:auto}.taxonomy-count{font-size:.8em;line-height:1.25em;display:inline-block;padding-left:.6em;padding-right:.6em;margin-left:.6em;text-align:center;border-radius:1em;background-color:var(--bs-body-bg)}.taxonomy-term{background:var(--bs-secondary-bg);border-width:0;border-radius:0 3px 3px 0;color:var(--bs-body-color);display:inline-block;font-size:1em;line-height:1.5em;min-height:1.5em;max-width:100%;padding:0 .5em 0 1em;position:relative;margin:0 .5em .2em 0;text-decoration:none;-webkit-transition:color .2s;-webkit-clip-path:polygon(100% 0,100% 100%,.8em 100%,0 50%,.8em 0);clip-path:polygon(100% 0,100% 100%,.8em 100%,0 50%,.8em 0)}.taxonomy-term:hover{background-color:var(--bs-primary-bg-subtle);color:var(--bs-body-color-dark)}.taxonomy-term:hover .taxonomy-count{color:var(--bs-body-color-dark)}.taxonomy-term:hover::before{background:#5f7681}.taxo-text-tags .taxonomy-term{background:0 0;border-width:0;border-radius:0;color:#6c757d;font-size:1em;line-height:1.5em;min-height:1.5em;max-width:100%;padding:0;position:relative;margin:0;text-decoration:none;-webkit-clip-path:none;clip-path:none}.taxo-text-tags .taxonomy-term:hover{background:0 0;color:#0d6efd}.taxo-text-tags .taxonomy-term:hover .taxonomy-count{color:#333f47!important}.taxo-text-tags .taxonomy-term:hover::before{background:0 0}.taxo-text-tags li+li::before{content:"|";color:#6c757d;margin-right:.2em}.taxo-text-tags .taxonomy-count{font-size:1em;line-height:1.25em;display:inline-block;padding:0;margin:0;text-align:center;border-radius:0;background:0 0;vertical-align:super;font-size:.75em}.taxo-text-tags .taxonomy-term:hover .taxonomy-count{color:#0d6efd!important}.taxo-fruits .taxonomy-term[data-taxonomy-term]::before{font-style:normal;font-variant:normal;text-rendering:auto;-webkit-font-smoothing:antialiased;font-family:"font awesome 6 free";padding-right:.5em;font-size:2em;min-width:1.5em;display:inline-block}.taxo-fruits .taxonomy-term[data-taxonomy-term=apple]::before{content:"\f5d1";color:red}.taxo-fruits .taxonomy-term[data-taxonomy-term=carrot]::before{content:"\f787";color:orange}.taxo-fruits .taxonomy-term[data-taxonomy-term=lemon]::before{content:"\f094";color:#32cd32}.taxo-fruits .taxonomy-term[data-taxonomy-term=pepper]::before{content:"\f816";color:darkred}.taxo-fruits .taxonomy-term{background:0 0;border-width:0;border-radius:0;color:#6c757d;font-size:1em;line-height:2.5em;max-width:100%;padding:0;position:relative;margin:0;text-decoration:none;-webkit-clip-path:none;clip-path:none}.taxo-fruits .taxonomy-term:hover{background:0 0;color:#0d6efd}.taxo-fruits .taxonomy-term:hover .taxonomy-count{color:#333f47!important}.taxo-fruits .taxonomy-term:hover::before{background:0 0;text-shadow:0 0 3px #212529}.taxo-fruits .taxonomy-count,.taxo-fruits .taxonomy-label{display:none}.taxo-fruits.taxonomy-terms-article{margin-bottom:1rem}.taxo-fruits.taxonomy-terms-article .taxonomy-title{display:none}.taxonomy-taxonomy-page{max-width:800px;margin:auto}.taxonomy-taxonomy-page h1,.taxonomy-taxonomy-page .h1{margin-bottom:1em}.article-meta{margin-bottom:1.5rem}.article-teaser.article-type-docs h3 a:before,.article-teaser.article-type-docs .h3 a:before,.article-teaser.article-type-docs .td-footer__links-item a:before{display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;-webkit-font-smoothing:antialiased;font-family:"font awesome 6 free";content:"\f02d";padding-right:.5em}.article-teaser.article-type-blog h3 a:before,.article-teaser.article-type-blog .h3 a:before,.article-teaser.article-type-blog .td-footer__links-item a:before{display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;-webkit-font-smoothing:antialiased;font-family:"font awesome 6 free";content:"\f781";padding-right:.5em}.all-taxonomy-terms{font-weight:500;line-height:1.2;font-size:1.5rem}.all-taxonomy-terms:before{display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;-webkit-font-smoothing:antialiased;font-family:"font awesome 6 free";content:"\f122";padding-right:.5em}.article-teaser.card{padding:1em;margin-bottom:1.5em}.article-teaser .breadcrumb{margin-bottom:0;font-size:.85rem}.article-teaser .article-meta{margin-bottom:0}div.drawio{display:inline-block;position:relative}div.drawio button{position:absolute;bottom:5px;right:5px;padding:.4em .5em;font-size:.8em;display:none}div.drawio:hover button{display:inline}div.drawioframe{position:fixed;height:100%;width:100%;top:0;left:0;z-index:1000;background:#000b;border:0}div.drawioframe iframe{position:absolute;height:90%;width:90%;top:5%;left:5%;z-index:1010}.tab-content .tab-pane{margin-top:0;margin-bottom:1.5rem;border-left:var(--bs-border-width)solid var(--bs-border-color);border-right:var(--bs-border-width)solid var(--bs-border-color);border-bottom:var(--bs-border-width)solid var(--bs-border-color)}.tab-content .tab-pane .highlight{margin:0;border:none;max-width:100%}.tab-body{font-weight:500;background:var(--td-pre-bg);color:var(--bs-body-color);border-radius:0;padding:1.5rem}.tab-body>:last-child{margin-bottom:0}.tab-body>.highlight:only-child{margin:-1.5rem;max-width:calc(100% + 3rem)}.tab-body-primary{border-style:solid;border-color:#5f7681}.tab-body-secondary{border-style:solid;border-color:#f58301}.tab-body-success{border-style:solid;border-color:#3772ff}.tab-body-info{border-style:solid;border-color:#007ac0}.tab-body-warning{border-style:solid;border-color:#e33030}.tab-body-danger{border-style:solid;border-color:#ed6a5a}.tab-body-light{border-style:solid;border-color:#d3f3ee}.tab-body-dark{border-style:solid;border-color:#333f47}.td-card.card .highlight{border:none;margin:0}.td-card .card-header.code{background-color:var(--bs-body-bg)}.td-card .card-body.code{background-color:var(--bs-body-bg);padding:0 0 0 1ex}.td-card .card-body pre{margin:0;padding:0 1rem 1rem}.swagger-ui .info .title small pre,.swagger-ui .info .title .small pre,.swagger-ui .info .title .td-footer__center pre,.swagger-ui .info .title .td-cover-block>.byline pre{background:#7d8492}.td-footer{min-height:150px;padding-top:3rem}@media(max-width:991.98px){.td-footer{min-height:200px}}.td-footer__center{text-align:center}.td-footer__right{text-align:right}.td-footer__about{font-size:initial}.td-footer__links-list{margin-bottom:0}.td-footer__links-item a{color:inherit!important}.td-footer__authors,.td-footer__all_rights_reserved{padding-left:.25rem}.td-footer__all_rights_reserved{padding-left:.25rem}@media(min-width:768px){.td-offset-anchor:target{display:block;position:relative;top:-4rem;visibility:hidden}h2[id]:before,[id].h2:before,h3[id]:before,[id].h3:before,[id].td-footer__links-item:before,h4[id]:before,[id].h4:before,h5[id]:before,[id].h5:before{display:block;content:" ";margin-top:-5rem;height:5rem;visibility:hidden}} \ No newline at end of file diff --git a/scss/main.min.5a3e601dd3c8e7f5fc116334f63ebc60aa2e1e5503c0ad767fc8c4cb8c4e3b91.css b/scss/main.min.5a3e601dd3c8e7f5fc116334f63ebc60aa2e1e5503c0ad767fc8c4cb8c4e3b91.css deleted file mode 100644 index fcbc773eca..0000000000 --- a/scss/main.min.5a3e601dd3c8e7f5fc116334f63ebc60aa2e1e5503c0ad767fc8c4cb8c4e3b91.css +++ /dev/null @@ -1,10 +0,0 @@ -@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,700,700i&display=swap";h4[id]:before,[id].h4:before,h5[id]:before,[id].h5:before{margin-top:0!important;height:0!important}.right{float:right}.left{float:left}.partner-logos img{width:15%;min-width:120px;padding:.2em}h2,.h2,h3,.h3,.td-footer__links-item{clear:both}.one-liner{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.meta-links a{display:block}.alert{clear:both}#td-sidebar-menu .td-search-input{max-width:none}#td-sidebar-menu .td-sidebar__search{padding-bottom:0}.td-sidebar{padding-bottom:0!important}#td-section-nav .td-sidebar-nav__section label{padding-left:.7rem}#td-section-nav .td-sidebar-nav__section .without-child,#td-section-nav .td-sidebar-nav__section .with-child{padding-left:.5rem}#td-section-nav .td-sidebar-link__page{color:#222;font-weight:400}#td-section-nav .td-sidebar-link__page.active{color:#f58301;font-weight:700}#td-section-nav .td-sidebar-link__section.active{color:#5f7681}#td-section-nav .td-sidebar-link__section.selected{color:#f58301}.workflow{font-size:90%}.workflow .fas,.workflow .td-offline-search-results__close-button:after{font-size:2em;color:#007ac0}.workflow>.row:nth-child(n+2){border-top:1px solid #eee;padding:.5rem 0}.workflow>.row>div.text{padding-bottom:.5rem}.schedule+table th{width:3em}.schedule+table td:not(:first-child):not(:empty){vertical-align:middle;background-image:url(/circle.svg);background-repeat:no-repeat;background-position:50%;text-align:center;color:#78b159;font-size:1pt}.fa-rotate-45{transform:rotate(45deg)}.highlight table td:first-child code span{-webkit-user-select:none;-moz-user-select:none;user-select:none}.mermaid{padding-bottom:30px}.td-navbar .td-navbar-nav-scroll .navbar-nav{padding-bottom:0!important}.td-navbar .td-navbar-nav-scroll{height:0!important}/*!* Bootstrap v5.2.3 (https://getbootstrap.com/) -* Copyright 2011-2022 The Bootstrap Authors -* Copyright 2011-2022 Twitter, Inc. -* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)*/:root{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-black:#000;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#5f7681;--bs-secondary:#F58301;--bs-success:#3772ff;--bs-info:#007AC0;--bs-warning:#E33030;--bs-danger:#ed6a5a;--bs-light:#d3f3ee;--bs-dark:#333F47;--bs-primary-rgb:95, 118, 129;--bs-secondary-rgb:245, 131, 1;--bs-success-rgb:55, 114, 255;--bs-info-rgb:0, 122, 192;--bs-warning-rgb:227, 48, 48;--bs-danger-rgb:237, 106, 90;--bs-light-rgb:211, 243, 238;--bs-dark-rgb:51, 63, 71;--bs-white-rgb:255, 255, 255;--bs-black-rgb:0, 0, 0;--bs-body-color-rgb:33, 37, 41;--bs-body-bg-rgb:255, 255, 255;--bs-font-sans-serif:"Open Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";--bs-font-monospace:SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family:Open Sans, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-bg:#fff;--bs-border-width:1px;--bs-border-style:solid;--bs-border-color:#dee2e6;--bs-border-color-translucent:rgba(0, 0, 0, 0.175);--bs-border-radius:0.375rem;--bs-border-radius-sm:0.25rem;--bs-border-radius-lg:0.5rem;--bs-border-radius-xl:1rem;--bs-border-radius-2xl:2rem;--bs-border-radius-pill:50rem;--bs-link-color:#0d6efd;--bs-link-hover-color:#094db1;--bs-code-color:#99641d;--bs-highlight-bg:#fff3cd}*,*::before,*::after{box-sizing:border-box}@media(prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;border:0;border-top:1px solid;opacity:.25}h6,.h6,h5,.h5,h4,.h4,h3,.h3,.td-footer__links-item,h2,.h2,h1,.h1{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}h1,.h1{font-size:calc(1.375rem + 1.5vw)}@media(min-width:1200px){h1,.h1{font-size:2.5rem}}h2,.h2{font-size:calc(1.325rem + .9vw)}@media(min-width:1200px){h2,.h2{font-size:2rem}}h3,.h3,.td-footer__links-item{font-size:calc(1.275rem + .3vw)}@media(min-width:1200px){h3,.h3,.td-footer__links-item{font-size:1.5rem}}h4,.h4{font-size:calc(1.26rem + .12vw)}@media(min-width:1200px){h4,.h4{font-size:1.35rem}}h5,.h5{font-size:1.15rem}h6,.h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}ol,ul,dl{margin-top:0;margin-bottom:1rem}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small,.small,.td-footer__copyright-etc,.td-cover-block>.byline{font-size:.875em}mark,.mark{padding:.1875em;background-color:var(--bs-highlight-bg)}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:var(--bs-link-color);text-decoration:none}a:hover{color:var(--bs-link-hover-color)}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}pre,code,kbd,samp{font-family:var(--bs-font-monospace);font-size:1em}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:var(--bs-code-color);word-wrap:break-word}a>code{color:inherit}kbd{padding:.1875rem .375rem;font-size:.875em;color:var(--bs-body-bg);background-color:var(--bs-body-color);border-radius:.25rem}kbd kbd{padding:0;font-size:1em}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}thead,tbody,tfoot,tr,td,th{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}input,button,select,optgroup,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator{display:none!important}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}button:not(:disabled),[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media(min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-text,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:calc(1.625rem + 4.5vw);font-weight:300;line-height:1.2}@media(min-width:1200px){.display-1{font-size:5rem}}.display-2{font-size:calc(1.575rem + 3.9vw);font-weight:300;line-height:1.2}@media(min-width:1200px){.display-2{font-size:4.5rem}}.display-3{font-size:calc(1.525rem + 3.3vw);font-weight:300;line-height:1.2}@media(min-width:1200px){.display-3{font-size:4rem}}.display-4{font-size:calc(1.475rem + 2.7vw);font-weight:300;line-height:1.2}@media(min-width:1200px){.display-4{font-size:3.5rem}}.display-5{font-size:calc(1.425rem + 2.1vw);font-weight:300;line-height:1.2}@media(min-width:1200px){.display-5{font-size:3rem}}.display-6{font-size:calc(1.375rem + 1.5vw);font-weight:300;line-height:1.2}@media(min-width:1200px){.display-6{font-size:2.5rem}}.list-unstyled{padding-left:0;list-style:none}.list-inline,.td-footer__links-list{padding-left:0;list-style:none}.list-inline-item,.td-footer__links-item{display:inline-block}.list-inline-item:not(:last-child),.td-footer__links-item:not(:last-child){margin-right:1rem}.initialism{font-size:.875em;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:.875em;color:#6c757d}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid,.td-content img{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid var(--bs-border-color);border-radius:.375rem;box-shadow:0 .125rem .25rem rgba(0,0,0,.075);max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:.875em;color:#6c757d}.container,.container-fluid,.container-xxl,.container-xl,.container-lg,.container-md,.container-sm{--bs-gutter-x:1.5rem;--bs-gutter-y:0;width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-right:auto;margin-left:auto}@media(min-width:576px){.container-sm,.container{max-width:540px}}@media(min-width:768px){.container-md,.container-sm,.container{max-width:720px}}@media(min-width:992px){.container-lg,.container-md,.container-sm,.container{max-width:960px}}@media(min-width:1200px){.container-xl,.container-lg,.container-md,.container-sm,.container{max-width:1140px}}@media(min-width:1400px){.container-xxl,.container-xl,.container-lg,.container-md,.container-sm,.container{max-width:1320px}}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(-1 * var(--bs-gutter-y));margin-right:calc(-.5 * var(--bs-gutter-x));margin-left:calc(-.5 * var(--bs-gutter-x))}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-top:var(--bs-gutter-y)}.col{flex:1 0}.row-cols-auto>*{flex:none;width:auto}.row-cols-1>*{flex:none;width:100%}.row-cols-2>*{flex:none;width:50%}.row-cols-3>*{flex:none;width:33.33333333%}.row-cols-4>*{flex:none;width:25%}.row-cols-5>*{flex:none;width:20%}.row-cols-6>*{flex:none;width:16.66666667%}.col-auto{flex:none;width:auto}.col-1{flex:none;width:8.33333333%}.col-2{flex:none;width:16.66666667%}.col-3{flex:none;width:25%}.col-4{flex:none;width:33.33333333%}.col-5{flex:none;width:41.66666667%}.col-6{flex:none;width:50%}.col-7{flex:none;width:58.33333333%}.col-8{flex:none;width:66.66666667%}.col-9{flex:none;width:75%}.col-10{flex:none;width:83.33333333%}.col-11{flex:none;width:91.66666667%}.col-12{flex:none;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.25rem}.g-1,.gy-1{--bs-gutter-y:0.25rem}.g-2,.gx-2{--bs-gutter-x:0.5rem}.g-2,.gy-2{--bs-gutter-y:0.5rem}.g-3,.gx-3{--bs-gutter-x:1rem}.g-3,.gy-3{--bs-gutter-y:1rem}.g-4,.gx-4{--bs-gutter-x:1.5rem}.g-4,.gy-4{--bs-gutter-y:1.5rem}.g-5,.gx-5{--bs-gutter-x:3rem}.g-5,.gy-5{--bs-gutter-y:3rem}@media(min-width:576px){.col-sm{flex:1 0}.row-cols-sm-auto>*{flex:none;width:auto}.row-cols-sm-1>*{flex:none;width:100%}.row-cols-sm-2>*{flex:none;width:50%}.row-cols-sm-3>*{flex:none;width:33.33333333%}.row-cols-sm-4>*{flex:none;width:25%}.row-cols-sm-5>*{flex:none;width:20%}.row-cols-sm-6>*{flex:none;width:16.66666667%}.col-sm-auto{flex:none;width:auto}.col-sm-1{flex:none;width:8.33333333%}.col-sm-2{flex:none;width:16.66666667%}.col-sm-3{flex:none;width:25%}.col-sm-4{flex:none;width:33.33333333%}.col-sm-5{flex:none;width:41.66666667%}.col-sm-6{flex:none;width:50%}.col-sm-7{flex:none;width:58.33333333%}.col-sm-8{flex:none;width:66.66666667%}.col-sm-9{flex:none;width:75%}.col-sm-10{flex:none;width:83.33333333%}.col-sm-11{flex:none;width:91.66666667%}.col-sm-12{flex:none;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.25rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.25rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:0.5rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:0.5rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem}}@media(min-width:768px){.col-md{flex:1 0}.row-cols-md-auto>*{flex:none;width:auto}.row-cols-md-1>*{flex:none;width:100%}.row-cols-md-2>*{flex:none;width:50%}.row-cols-md-3>*{flex:none;width:33.33333333%}.row-cols-md-4>*{flex:none;width:25%}.row-cols-md-5>*{flex:none;width:20%}.row-cols-md-6>*{flex:none;width:16.66666667%}.col-md-auto{flex:none;width:auto}.col-md-1{flex:none;width:8.33333333%}.col-md-2{flex:none;width:16.66666667%}.col-md-3{flex:none;width:25%}.col-md-4{flex:none;width:33.33333333%}.col-md-5{flex:none;width:41.66666667%}.col-md-6{flex:none;width:50%}.col-md-7{flex:none;width:58.33333333%}.col-md-8{flex:none;width:66.66666667%}.col-md-9{flex:none;width:75%}.col-md-10{flex:none;width:83.33333333%}.col-md-11{flex:none;width:91.66666667%}.col-md-12{flex:none;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.25rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.25rem}.g-md-2,.gx-md-2{--bs-gutter-x:0.5rem}.g-md-2,.gy-md-2{--bs-gutter-y:0.5rem}.g-md-3,.gx-md-3{--bs-gutter-x:1rem}.g-md-3,.gy-md-3{--bs-gutter-y:1rem}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem}.g-md-5,.gx-md-5{--bs-gutter-x:3rem}.g-md-5,.gy-md-5{--bs-gutter-y:3rem}}@media(min-width:992px){.col-lg{flex:1 0}.row-cols-lg-auto>*{flex:none;width:auto}.row-cols-lg-1>*{flex:none;width:100%}.row-cols-lg-2>*{flex:none;width:50%}.row-cols-lg-3>*{flex:none;width:33.33333333%}.row-cols-lg-4>*{flex:none;width:25%}.row-cols-lg-5>*{flex:none;width:20%}.row-cols-lg-6>*{flex:none;width:16.66666667%}.col-lg-auto{flex:none;width:auto}.col-lg-1{flex:none;width:8.33333333%}.col-lg-2{flex:none;width:16.66666667%}.col-lg-3{flex:none;width:25%}.col-lg-4{flex:none;width:33.33333333%}.col-lg-5{flex:none;width:41.66666667%}.col-lg-6{flex:none;width:50%}.col-lg-7{flex:none;width:58.33333333%}.col-lg-8{flex:none;width:66.66666667%}.col-lg-9{flex:none;width:75%}.col-lg-10{flex:none;width:83.33333333%}.col-lg-11{flex:none;width:91.66666667%}.col-lg-12{flex:none;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.25rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.25rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:0.5rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:0.5rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem}}@media(min-width:1200px){.col-xl{flex:1 0}.row-cols-xl-auto>*{flex:none;width:auto}.row-cols-xl-1>*{flex:none;width:100%}.row-cols-xl-2>*{flex:none;width:50%}.row-cols-xl-3>*{flex:none;width:33.33333333%}.row-cols-xl-4>*{flex:none;width:25%}.row-cols-xl-5>*{flex:none;width:20%}.row-cols-xl-6>*{flex:none;width:16.66666667%}.col-xl-auto{flex:none;width:auto}.col-xl-1{flex:none;width:8.33333333%}.col-xl-2{flex:none;width:16.66666667%}.col-xl-3{flex:none;width:25%}.col-xl-4{flex:none;width:33.33333333%}.col-xl-5{flex:none;width:41.66666667%}.col-xl-6{flex:none;width:50%}.col-xl-7{flex:none;width:58.33333333%}.col-xl-8{flex:none;width:66.66666667%}.col-xl-9{flex:none;width:75%}.col-xl-10{flex:none;width:83.33333333%}.col-xl-11{flex:none;width:91.66666667%}.col-xl-12{flex:none;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.25rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.25rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:0.5rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:0.5rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem}}@media(min-width:1400px){.col-xxl{flex:1 0}.row-cols-xxl-auto>*{flex:none;width:auto}.row-cols-xxl-1>*{flex:none;width:100%}.row-cols-xxl-2>*{flex:none;width:50%}.row-cols-xxl-3>*{flex:none;width:33.33333333%}.row-cols-xxl-4>*{flex:none;width:25%}.row-cols-xxl-5>*{flex:none;width:20%}.row-cols-xxl-6>*{flex:none;width:16.66666667%}.col-xxl-auto{flex:none;width:auto}.col-xxl-1{flex:none;width:8.33333333%}.col-xxl-2{flex:none;width:16.66666667%}.col-xxl-3{flex:none;width:25%}.col-xxl-4{flex:none;width:33.33333333%}.col-xxl-5{flex:none;width:41.66666667%}.col-xxl-6{flex:none;width:50%}.col-xxl-7{flex:none;width:58.33333333%}.col-xxl-8{flex:none;width:66.66666667%}.col-xxl-9{flex:none;width:75%}.col-xxl-10{flex:none;width:83.33333333%}.col-xxl-11{flex:none;width:91.66666667%}.col-xxl-12{flex:none;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.25rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.25rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:0.5rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:0.5rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem}}.table,.td-table:not(.td-initial),.td-content table:not(.td-initial),.td-box table:not(.td-initial){--bs-table-color:var(--bs-body-color);--bs-table-bg:transparent;--bs-table-border-color:var(--bs-border-color);--bs-table-accent-bg:transparent;--bs-table-striped-color:var(--bs-body-color);--bs-table-striped-bg:rgba(0, 0, 0, 0.05);--bs-table-active-color:var(--bs-body-color);--bs-table-active-bg:rgba(0, 0, 0, 0.1);--bs-table-hover-color:var(--bs-body-color);--bs-table-hover-bg:rgba(0, 0, 0, 0.075);width:100%;margin-bottom:1rem;color:var(--bs-table-color);vertical-align:top;border-color:var(--bs-table-border-color)}.table>:not(caption)>*>*,.td-table:not(.td-initial)>:not(caption)>*>*,.td-content table:not(.td-initial)>:not(caption)>*>*,.td-box table:not(.td-initial)>:not(caption)>*>*{padding:.5rem;background-color:var(--bs-table-bg);border-bottom-width:1px;box-shadow:inset 0 0 0 9999px var(--bs-table-accent-bg)}.table>tbody,.td-table:not(.td-initial)>tbody,.td-content table:not(.td-initial)>tbody,.td-box table:not(.td-initial)>tbody{vertical-align:inherit}.table>thead,.td-table:not(.td-initial)>thead,.td-content table:not(.td-initial)>thead,.td-box table:not(.td-initial)>thead{vertical-align:bottom}.table-group-divider{border-top:2px solid}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem}.table-bordered>:not(caption)>*{border-width:1px 0}.table-bordered>:not(caption)>*>*{border-width:0 1px}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-borderless>:not(:first-child){border-top-width:0}.table-striped>tbody>tr:nth-of-type(odd)>*,.td-table:not(.td-initial)>tbody>tr:nth-of-type(odd)>*,.td-content table:not(.td-initial)>tbody>tr:nth-of-type(odd)>*,.td-box table:not(.td-initial)>tbody>tr:nth-of-type(odd)>*{--bs-table-accent-bg:var(--bs-table-striped-bg);color:var(--bs-table-striped-color)}.table-striped-columns>:not(caption)>tr>:nth-child(even){--bs-table-accent-bg:var(--bs-table-striped-bg);color:var(--bs-table-striped-color)}.table-active{--bs-table-accent-bg:var(--bs-table-active-bg);color:var(--bs-table-active-color)}.table-hover>tbody>tr:hover>*{--bs-table-accent-bg:var(--bs-table-hover-bg);color:var(--bs-table-hover-color)}.table-primary{--bs-table-color:#000;--bs-table-bg:#dfe4e6;--bs-table-border-color:#c9cdcf;--bs-table-striped-bg:#d4d9db;--bs-table-striped-color:#000;--bs-table-active-bg:#c9cdcf;--bs-table-active-color:#000;--bs-table-hover-bg:#ced3d5;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-secondary{--bs-table-color:#000;--bs-table-bg:#fde6cc;--bs-table-border-color:#e4cfb8;--bs-table-striped-bg:#f0dbc2;--bs-table-striped-color:#000;--bs-table-active-bg:#e4cfb8;--bs-table-active-color:#000;--bs-table-hover-bg:#ead5bd;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-success{--bs-table-color:#000;--bs-table-bg:#d7e3ff;--bs-table-border-color:#c2cce6;--bs-table-striped-bg:#ccd8f2;--bs-table-striped-color:#000;--bs-table-active-bg:#c2cce6;--bs-table-active-color:#000;--bs-table-hover-bg:#c7d2ec;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-info{--bs-table-color:#000;--bs-table-bg:#cce4f2;--bs-table-border-color:#b8cdda;--bs-table-striped-bg:#c2d9e6;--bs-table-striped-color:#000;--bs-table-active-bg:#b8cdda;--bs-table-active-color:#000;--bs-table-hover-bg:#bdd3e0;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-warning{--bs-table-color:#000;--bs-table-bg:#f9d6d6;--bs-table-border-color:#e0c1c1;--bs-table-striped-bg:#edcbcb;--bs-table-striped-color:#000;--bs-table-active-bg:#e0c1c1;--bs-table-active-color:#000;--bs-table-hover-bg:#e6c6c6;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-danger{--bs-table-color:#000;--bs-table-bg:#fbe1de;--bs-table-border-color:#e2cbc8;--bs-table-striped-bg:#eed6d3;--bs-table-striped-color:#000;--bs-table-active-bg:#e2cbc8;--bs-table-active-color:#000;--bs-table-hover-bg:#e8d0cd;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-light{--bs-table-color:#000;--bs-table-bg:#d3f3ee;--bs-table-border-color:#bedbd6;--bs-table-striped-bg:#c8e7e2;--bs-table-striped-color:#000;--bs-table-active-bg:#bedbd6;--bs-table-active-color:#000;--bs-table-hover-bg:#c3e1dc;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-dark{--bs-table-color:#fff;--bs-table-bg:#333F47;--bs-table-border-color:#475259;--bs-table-striped-bg:#3d4950;--bs-table-striped-color:#fff;--bs-table-active-bg:#475259;--bs-table-active-color:#fff;--bs-table-hover-bg:#424d55;--bs-table-hover-color:#fff;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-responsive,.td-table:not(.td-initial),.td-content table:not(.td-initial),.td-box table:not(.td-initial){overflow-x:auto;-webkit-overflow-scrolling:touch}@media(max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label{margin-bottom:.5rem}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem}.form-text{margin-top:.25rem;font-size:.875em;color:#6c757d}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.375rem;box-shadow:inset 0 1px 2px rgba(0,0,0,.075);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:#212529;background-color:#fff;border-color:#afbbc0;outline:0;box-shadow:inset 0 1px 2px rgba(0,0,0,.075),0 0 0 .25rem rgba(95,118,129,.25)}.form-control::-webkit-date-and-time-value{height:1.5em}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled{background-color:#e9ecef;opacity:1}.form-control::file-selector-button{padding:.375rem .75rem;margin:-.375rem -.75rem;margin-inline-end:.75rem;color:#212529;background-color:#e9ecef;background-image:var(--bs-gradient);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:#dde0e3}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext:focus{outline:0}.form-control-plaintext.form-control-sm,.form-control-plaintext.form-control-lg{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;border-radius:.25rem}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-.25rem -.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;border-radius:.5rem}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-.5rem -1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + .75rem + 2px)}textarea.form-control-sm{min-height:calc(1.5em + .5rem + 2px)}textarea.form-control-lg{min-height:calc(1.5em + 1rem + 2px)}.form-control-color{width:3rem;height:calc(1.5em + .75rem + 2px);padding:.375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{border:0!important;border-radius:.375rem}.form-control-color::-webkit-color-swatch{border-radius:.375rem}.form-control-color.form-control-sm{height:calc(1.5em + .5rem + 2px)}.form-control-color.form-control-lg{height:calc(1.5em + 1rem + 2px)}.form-select{display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;-moz-padding-start:calc(.75rem - 3px);font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:1px solid #ced4da;border-radius:.375rem;box-shadow:inset 0 1px 2px rgba(0,0,0,.075);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;-moz-appearance:none;appearance:none}@media(prefers-reduced-motion:reduce){.form-select{transition:none}}.form-select:focus{border-color:#afbbc0;outline:0;box-shadow:inset 0 1px 2px rgba(0,0,0,.075),0 0 0 .25rem rgba(95,118,129,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none}.form-select:disabled{background-color:#e9ecef}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #212529}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem;border-radius:.25rem}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem;border-radius:.5rem}.form-check{display:block;min-height:1.5rem;padding-left:1.5em;margin-bottom:.125rem}.form-check .form-check-input{float:left;margin-left:-1.5em}.form-check-reverse{padding-right:1.5em;padding-left:0;text-align:right}.form-check-reverse .form-check-input{float:right;margin-right:-1.5em;margin-left:0}.form-check-input{width:1em;height:1em;margin-top:.25em;vertical-align:top;background-color:#fff;background-repeat:no-repeat;background-position:50%;background-size:contain;border:1px solid rgba(0,0,0,.25);-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-print-color-adjust:exact;print-color-adjust:exact}.form-check-input[type=checkbox]{border-radius:.25em}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:#afbbc0;outline:0;box-shadow:0 0 0 .25rem rgba(95,118,129,.25)}.form-check-input:checked{background-color:#5f7681;border-color:#5f7681}.form-check-input:checked[type=checkbox]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e"),var(--bs-gradient)}.form-check-input:checked[type=radio]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e"),var(--bs-gradient)}.form-check-input[type=checkbox]:indeterminate{background-color:#5f7681;border-color:#5f7681;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e"),var(--bs-gradient)}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input[disabled]~.form-check-label,.form-check-input:disabled~.form-check-label{cursor:default;opacity:.5}.form-switch{padding-left:2.5em}.form-switch .form-check-input{width:2em;margin-left:-2.5em;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");background-position:0;border-radius:2em;transition:background-position .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23afbbc0'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:100%;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"),var(--bs-gradient)}.form-switch.form-check-reverse{padding-right:2.5em;padding-left:0}.form-switch.form-check-reverse .form-check-input{margin-right:-2.5em;margin-left:0}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.btn-check[disabled]+.btn,div.drawio .btn-check[disabled]+button,.td-blog .btn-check[disabled]+.td-rss-button,.btn-check:disabled+.btn,div.drawio .btn-check:disabled+button,.td-blog .btn-check:disabled+.td-rss-button{pointer-events:none;filter:none;opacity:.65}.form-range{width:100%;height:1.5rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(95,118,129,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(95,118,129,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#5f7681;background-image:var(--bs-gradient);border:0;border-radius:1rem;box-shadow:0 .1rem .25rem rgba(0,0,0,.1);-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media(prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#cfd6d9;background-image:var(--bs-gradient)}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem;box-shadow:inset 0 1px 2px rgba(0,0,0,.075)}.form-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#5f7681;background-image:var(--bs-gradient);border:0;border-radius:1rem;box-shadow:0 .1rem .25rem rgba(0,0,0,.1);-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media(prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{-moz-transition:none;transition:none}}.form-range::-moz-range-thumb:active{background-color:#cfd6d9;background-image:var(--bs-gradient)}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem;box-shadow:inset 0 1px 2px rgba(0,0,0,.075)}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.form-range:disabled::-moz-range-thumb{background-color:#adb5bd}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-control-plaintext,.form-floating>.form-select{height:calc(3.5rem + 2px);line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;width:100%;height:100%;padding:1rem .75rem;overflow:hidden;text-align:start;text-overflow:ellipsis;white-space:nowrap;pointer-events:none;border:1px solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media(prefers-reduced-motion:reduce){.form-floating>label{transition:none}}.form-floating>.form-control,.form-floating>.form-control-plaintext{padding:1rem .75rem}.form-floating>.form-control::-moz-placeholder,.form-floating>.form-control-plaintext::-moz-placeholder{color:transparent}.form-floating>.form-control::placeholder,.form-floating>.form-control-plaintext::placeholder{color:transparent}.form-floating>.form-control:not(:-moz-placeholder-shown),.form-floating>.form-control-plaintext:not(:-moz-placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown),.form-floating>.form-control-plaintext:focus,.form-floating>.form-control-plaintext:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:-webkit-autofill,.form-floating>.form-control-plaintext:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:not(:-moz-placeholder-shown)~label{opacity:.65;transform:scale(.85)translateY(-.5rem)translateX(.15rem)}.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-control-plaintext~label,.form-floating>.form-select~label{opacity:.65;transform:scale(.85)translateY(-.5rem)translateX(.15rem)}.form-floating>.form-control:-webkit-autofill~label{opacity:.65;transform:scale(.85)translateY(-.5rem)translateX(.15rem)}.form-floating>.form-control-plaintext~label{border-width:1px 0}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-select,.input-group>.form-floating{position:relative;flex:auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-select:focus,.input-group>.form-floating:focus-within{z-index:5}.input-group .btn,.input-group div.drawio button,div.drawio .input-group button,.input-group .td-blog .td-rss-button,.td-blog .input-group .td-rss-button{position:relative;z-index:2}.input-group .btn:focus,.input-group div.drawio button:focus,div.drawio .input-group button:focus,.input-group .td-blog .td-rss-button:focus,.td-blog .input-group .td-rss-button:focus{z-index:5}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.375rem}.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text,.input-group-lg>.btn,div.drawio .input-group-lg>button,.td-blog .input-group-lg>.td-rss-button{padding:.5rem 1rem;font-size:1.25rem;border-radius:.5rem}.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text,.input-group-sm>.btn,div.drawio .input-group-sm>button,.td-blog .input-group-sm>.td-rss-button{padding:.25rem .5rem;font-size:.875rem;border-radius:.25rem}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating),.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-control,.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-select{border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating),.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-control,.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-select{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:-1px;border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.form-floating:not(:first-child)>.form-control,.input-group>.form-floating:not(:first-child)>.form-select{border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#3772ff}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#000;background-color:rgba(55,114,255,.9);border-radius:.375rem}.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip,.is-valid~.valid-feedback,.is-valid~.valid-tooltip{display:block}.was-validated .form-control:valid,.form-control.is-valid{border-color:#3772ff;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%233772ff' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem)center;background-size:calc(.75em + .375rem)calc(.75em + .375rem)}.was-validated .form-control:valid:focus,.form-control.is-valid:focus{border-color:#3772ff;box-shadow:0 0 0 .25rem rgba(55,114,255,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem)right calc(.375em + .1875rem)}.was-validated .form-select:valid,.form-select.is-valid{border-color:#3772ff}.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"],.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"]{padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%233772ff' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem)calc(.75em + .375rem)}.was-validated .form-select:valid:focus,.form-select.is-valid:focus{border-color:#3772ff;box-shadow:0 0 0 .25rem rgba(55,114,255,.25)}.was-validated .form-control-color:valid,.form-control-color.is-valid{width:calc(3rem + calc(1.5em + .75rem))}.was-validated .form-check-input:valid,.form-check-input.is-valid{border-color:#3772ff}.was-validated .form-check-input:valid:checked,.form-check-input.is-valid:checked{background-color:#3772ff}.was-validated .form-check-input:valid:focus,.form-check-input.is-valid:focus{box-shadow:0 0 0 .25rem rgba(55,114,255,.25)}.was-validated .form-check-input:valid~.form-check-label,.form-check-input.is-valid~.form-check-label{color:#3772ff}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.was-validated .input-group>.form-control:not(:focus):valid,.input-group>.form-control:not(:focus).is-valid,.was-validated .input-group>.form-select:not(:focus):valid,.input-group>.form-select:not(:focus).is-valid,.was-validated .input-group>.form-floating:not(:focus-within):valid,.input-group>.form-floating:not(:focus-within).is-valid{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#ed6a5a}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#000;background-color:rgba(237,106,90,.9);border-radius:.375rem}.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip,.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip{display:block}.was-validated .form-control:invalid,.form-control.is-invalid{border-color:#ed6a5a;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23ed6a5a'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23ed6a5a' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem)center;background-size:calc(.75em + .375rem)calc(.75em + .375rem)}.was-validated .form-control:invalid:focus,.form-control.is-invalid:focus{border-color:#ed6a5a;box-shadow:0 0 0 .25rem rgba(237,106,90,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem)right calc(.375em + .1875rem)}.was-validated .form-select:invalid,.form-select.is-invalid{border-color:#ed6a5a}.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"],.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"]{padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23ed6a5a'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23ed6a5a' stroke='none'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem)calc(.75em + .375rem)}.was-validated .form-select:invalid:focus,.form-select.is-invalid:focus{border-color:#ed6a5a;box-shadow:0 0 0 .25rem rgba(237,106,90,.25)}.was-validated .form-control-color:invalid,.form-control-color.is-invalid{width:calc(3rem + calc(1.5em + .75rem))}.was-validated .form-check-input:invalid,.form-check-input.is-invalid{border-color:#ed6a5a}.was-validated .form-check-input:invalid:checked,.form-check-input.is-invalid:checked{background-color:#ed6a5a}.was-validated .form-check-input:invalid:focus,.form-check-input.is-invalid:focus{box-shadow:0 0 0 .25rem rgba(237,106,90,.25)}.was-validated .form-check-input:invalid~.form-check-label,.form-check-input.is-invalid~.form-check-label{color:#ed6a5a}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.was-validated .input-group>.form-control:not(:focus):invalid,.input-group>.form-control:not(:focus).is-invalid,.was-validated .input-group>.form-select:not(:focus):invalid,.input-group>.form-select:not(:focus).is-invalid,.was-validated .input-group>.form-floating:not(:focus-within):invalid,.input-group>.form-floating:not(:focus-within).is-invalid{z-index:4}.btn,div.drawio button,.td-blog .td-rss-button{--bs-btn-padding-x:0.75rem;--bs-btn-padding-y:0.375rem;--bs-btn-font-family: ;--bs-btn-font-size:1rem;--bs-btn-font-weight:400;--bs-btn-line-height:1.5;--bs-btn-color:#212529;--bs-btn-bg:transparent;--bs-btn-border-width:1px;--bs-btn-border-color:transparent;--bs-btn-border-radius:0.375rem;--bs-btn-hover-border-color:transparent;--bs-btn-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);--bs-btn-disabled-opacity:0.65;--bs-btn-focus-box-shadow:0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5);display:inline-block;padding:var(--bs-btn-padding-y)var(--bs-btn-padding-x);font-family:var(--bs-btn-font-family);font-size:var(--bs-btn-font-size);font-weight:var(--bs-btn-font-weight);line-height:var(--bs-btn-line-height);color:var(--bs-btn-color);text-align:center;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;border:var(--bs-btn-border-width)solid var(--bs-btn-border-color);border-radius:var(--bs-btn-border-radius);background-color:var(--bs-btn-bg);background-image:var(--bs-gradient);box-shadow:var(--bs-btn-box-shadow);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.btn,div.drawio button,.td-blog .td-rss-button{transition:none}}.btn:hover,div.drawio button:hover,.td-blog .td-rss-button:hover{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color)}.btn-check+.btn:hover,div.drawio .btn-check+button:hover,.td-blog .btn-check+.td-rss-button:hover{color:var(--bs-btn-color);background-color:var(--bs-btn-bg);border-color:var(--bs-btn-border-color)}.btn:focus-visible,div.drawio button:focus-visible,.td-blog .td-rss-button:focus-visible{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);background-image:var(--bs-gradient);border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-box-shadow),var(--bs-btn-focus-box-shadow)}.btn-check:focus-visible+.btn,div.drawio .btn-check:focus-visible+button,.td-blog .btn-check:focus-visible+.td-rss-button{border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-box-shadow),var(--bs-btn-focus-box-shadow)}.btn-check:checked+.btn,div.drawio .btn-check:checked+button,.td-blog .btn-check:checked+.td-rss-button,:not(.btn-check)+.btn:active,div.drawio :not(.btn-check)+button:active,.td-blog :not(.btn-check)+.td-rss-button:active,.btn:first-child:active,div.drawio button:first-child:active,.td-blog .td-rss-button:first-child:active,.btn.active,div.drawio button.active,.td-blog .active.td-rss-button,.btn.show,div.drawio button.show,.td-blog .show.td-rss-button{color:var(--bs-btn-active-color);background-color:var(--bs-btn-active-bg);background-image:none;border-color:var(--bs-btn-active-border-color);box-shadow:var(--bs-btn-active-shadow)}.btn-check:checked+.btn:focus-visible,div.drawio .btn-check:checked+button:focus-visible,.td-blog .btn-check:checked+.td-rss-button:focus-visible,:not(.btn-check)+.btn:active:focus-visible,div.drawio :not(.btn-check)+button:active:focus-visible,.td-blog :not(.btn-check)+.td-rss-button:active:focus-visible,.btn:first-child:active:focus-visible,div.drawio button:first-child:active:focus-visible,.td-blog .td-rss-button:first-child:active:focus-visible,.btn.active:focus-visible,div.drawio button.active:focus-visible,.td-blog .active.td-rss-button:focus-visible,.btn.show:focus-visible,div.drawio button.show:focus-visible,.td-blog .show.td-rss-button:focus-visible{box-shadow:var(--bs-btn-active-shadow),var(--bs-btn-focus-box-shadow)}.btn:disabled,div.drawio button:disabled,.td-blog .td-rss-button:disabled,.btn.disabled,div.drawio button.disabled,.td-blog .disabled.td-rss-button,fieldset:disabled .btn,fieldset:disabled div.drawio button,div.drawio fieldset:disabled button,fieldset:disabled .td-blog .td-rss-button,.td-blog fieldset:disabled .td-rss-button{color:var(--bs-btn-disabled-color);pointer-events:none;background-color:var(--bs-btn-disabled-bg);background-image:none;border-color:var(--bs-btn-disabled-border-color);opacity:var(--bs-btn-disabled-opacity);box-shadow:none}.btn-primary{--bs-btn-color:#fff;--bs-btn-bg:#5f7681;--bs-btn-border-color:#5f7681;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#51646e;--bs-btn-hover-border-color:#4c5e67;--bs-btn-focus-shadow-rgb:119, 139, 148;--bs-btn-active-color:#fff;--bs-btn-active-bg:#4c5e67;--bs-btn-active-border-color:#475961;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#5f7681;--bs-btn-disabled-border-color:#5f7681}.btn-secondary{--bs-btn-color:#000;--bs-btn-bg:#F58301;--bs-btn-border-color:#F58301;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#f79627;--bs-btn-hover-border-color:#f68f1a;--bs-btn-focus-shadow-rgb:208, 111, 1;--bs-btn-active-color:#000;--bs-btn-active-bg:#f79c34;--bs-btn-active-border-color:#f68f1a;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#F58301;--bs-btn-disabled-border-color:#F58301}.btn-success{--bs-btn-color:#000;--bs-btn-bg:#3772ff;--bs-btn-border-color:#3772ff;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#5587ff;--bs-btn-hover-border-color:#4b80ff;--bs-btn-focus-shadow-rgb:47, 97, 217;--bs-btn-active-color:#000;--bs-btn-active-bg:#5f8eff;--bs-btn-active-border-color:#4b80ff;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#3772ff;--bs-btn-disabled-border-color:#3772ff}.btn-info,.td-blog .td-rss-button{--bs-btn-color:#fff;--bs-btn-bg:#007AC0;--bs-btn-border-color:#007AC0;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#0068a3;--bs-btn-hover-border-color:#00629a;--bs-btn-focus-shadow-rgb:38, 142, 201;--bs-btn-active-color:#fff;--bs-btn-active-bg:#00629a;--bs-btn-active-border-color:#005c90;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#007AC0;--bs-btn-disabled-border-color:#007AC0}.btn-warning{--bs-btn-color:#000;--bs-btn-bg:#E33030;--bs-btn-border-color:#E33030;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#e74f4f;--bs-btn-hover-border-color:#e64545;--bs-btn-focus-shadow-rgb:193, 41, 41;--bs-btn-active-color:#000;--bs-btn-active-bg:#e95959;--bs-btn-active-border-color:#e64545;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#E33030;--bs-btn-disabled-border-color:#E33030}.btn-danger{--bs-btn-color:#000;--bs-btn-bg:#ed6a5a;--bs-btn-border-color:#ed6a5a;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#f08073;--bs-btn-hover-border-color:#ef796b;--bs-btn-focus-shadow-rgb:201, 90, 77;--bs-btn-active-color:#000;--bs-btn-active-bg:#f1887b;--bs-btn-active-border-color:#ef796b;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#ed6a5a;--bs-btn-disabled-border-color:#ed6a5a}.btn-light{--bs-btn-color:#000;--bs-btn-bg:#d3f3ee;--bs-btn-border-color:#d3f3ee;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#b3cfca;--bs-btn-hover-border-color:#a9c2be;--bs-btn-focus-shadow-rgb:179, 207, 202;--bs-btn-active-color:#000;--bs-btn-active-bg:#a9c2be;--bs-btn-active-border-color:#9eb6b3;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#d3f3ee;--bs-btn-disabled-border-color:#d3f3ee}.btn-dark{--bs-btn-color:#fff;--bs-btn-bg:#333F47;--bs-btn-border-color:#333F47;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#525c63;--bs-btn-hover-border-color:#475259;--bs-btn-focus-shadow-rgb:82, 92, 99;--bs-btn-active-color:#fff;--bs-btn-active-bg:#5c656c;--bs-btn-active-border-color:#475259;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#333F47;--bs-btn-disabled-border-color:#333F47}.btn-outline-primary,div.drawio button{--bs-btn-color:#5f7681;--bs-btn-border-color:#5f7681;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#5f7681;--bs-btn-hover-border-color:#5f7681;--bs-btn-focus-shadow-rgb:95, 118, 129;--bs-btn-active-color:#fff;--bs-btn-active-bg:#5f7681;--bs-btn-active-border-color:#5f7681;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#5f7681;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#5f7681;--bs-gradient:none}.btn-outline-secondary{--bs-btn-color:#F58301;--bs-btn-border-color:#F58301;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#F58301;--bs-btn-hover-border-color:#F58301;--bs-btn-focus-shadow-rgb:245, 131, 1;--bs-btn-active-color:#000;--bs-btn-active-bg:#F58301;--bs-btn-active-border-color:#F58301;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#F58301;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#F58301;--bs-gradient:none}.btn-outline-success{--bs-btn-color:#3772ff;--bs-btn-border-color:#3772ff;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#3772ff;--bs-btn-hover-border-color:#3772ff;--bs-btn-focus-shadow-rgb:55, 114, 255;--bs-btn-active-color:#000;--bs-btn-active-bg:#3772ff;--bs-btn-active-border-color:#3772ff;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#3772ff;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#3772ff;--bs-gradient:none}.btn-outline-info{--bs-btn-color:#007AC0;--bs-btn-border-color:#007AC0;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#007AC0;--bs-btn-hover-border-color:#007AC0;--bs-btn-focus-shadow-rgb:0, 122, 192;--bs-btn-active-color:#fff;--bs-btn-active-bg:#007AC0;--bs-btn-active-border-color:#007AC0;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#007AC0;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#007AC0;--bs-gradient:none}.btn-outline-warning{--bs-btn-color:#E33030;--bs-btn-border-color:#E33030;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#E33030;--bs-btn-hover-border-color:#E33030;--bs-btn-focus-shadow-rgb:227, 48, 48;--bs-btn-active-color:#000;--bs-btn-active-bg:#E33030;--bs-btn-active-border-color:#E33030;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#E33030;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#E33030;--bs-gradient:none}.btn-outline-danger{--bs-btn-color:#ed6a5a;--bs-btn-border-color:#ed6a5a;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#ed6a5a;--bs-btn-hover-border-color:#ed6a5a;--bs-btn-focus-shadow-rgb:237, 106, 90;--bs-btn-active-color:#000;--bs-btn-active-bg:#ed6a5a;--bs-btn-active-border-color:#ed6a5a;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#ed6a5a;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#ed6a5a;--bs-gradient:none}.btn-outline-light{--bs-btn-color:#d3f3ee;--bs-btn-border-color:#d3f3ee;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#d3f3ee;--bs-btn-hover-border-color:#d3f3ee;--bs-btn-focus-shadow-rgb:211, 243, 238;--bs-btn-active-color:#000;--bs-btn-active-bg:#d3f3ee;--bs-btn-active-border-color:#d3f3ee;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#d3f3ee;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#d3f3ee;--bs-gradient:none}.btn-outline-dark{--bs-btn-color:#333F47;--bs-btn-border-color:#333F47;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#333F47;--bs-btn-hover-border-color:#333F47;--bs-btn-focus-shadow-rgb:51, 63, 71;--bs-btn-active-color:#fff;--bs-btn-active-bg:#333F47;--bs-btn-active-border-color:#333F47;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#333F47;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#333F47;--bs-gradient:none}.btn-link{--bs-btn-font-weight:400;--bs-btn-color:var(--bs-link-color);--bs-btn-bg:transparent;--bs-btn-border-color:transparent;--bs-btn-hover-color:var(--bs-link-hover-color);--bs-btn-hover-border-color:transparent;--bs-btn-active-color:var(--bs-link-hover-color);--bs-btn-active-border-color:transparent;--bs-btn-disabled-color:#6c757d;--bs-btn-disabled-border-color:transparent;--bs-btn-box-shadow:none;--bs-btn-focus-shadow-rgb:119, 139, 148;text-decoration:none;background-image:none}.btn-link:focus-visible{color:var(--bs-btn-color)}.btn-link:hover{color:var(--bs-btn-hover-color)}.btn-lg,.td-blog .td-rss-button,.btn-group-lg>.btn,div.drawio .btn-group-lg>button{--bs-btn-padding-y:0.5rem;--bs-btn-padding-x:1rem;--bs-btn-font-size:1.25rem;--bs-btn-border-radius:0.5rem}.btn-sm,.btn-group-sm>.btn,div.drawio .btn-group-sm>button,.td-blog .btn-group-sm>.td-rss-button{--bs-btn-padding-y:0.25rem;--bs-btn-padding-x:0.5rem;--bs-btn-font-size:0.875rem;--bs-btn-border-radius:0.25rem}.fade{transition:opacity .15s linear}@media(prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media(prefers-reduced-motion:reduce){.collapsing{transition:none}}.collapsing.collapse-horizontal{width:0;height:auto;transition:width .35s ease}@media(prefers-reduced-motion:reduce){.collapsing.collapse-horizontal{transition:none}}.dropup,.dropend,.dropdown,.dropstart,.dropup-center,.dropdown-center{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{--bs-dropdown-zindex:1000;--bs-dropdown-min-width:10rem;--bs-dropdown-padding-x:0;--bs-dropdown-padding-y:0.5rem;--bs-dropdown-spacer:0.125rem;--bs-dropdown-font-size:1rem;--bs-dropdown-color:#212529;--bs-dropdown-bg:#fff;--bs-dropdown-border-color:var(--bs-border-color-translucent);--bs-dropdown-border-radius:0.375rem;--bs-dropdown-border-width:1px;--bs-dropdown-inner-border-radius:calc(0.375rem - 1px);--bs-dropdown-divider-bg:var(--bs-border-color-translucent);--bs-dropdown-divider-margin-y:0.5rem;--bs-dropdown-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-dropdown-link-color:#212529;--bs-dropdown-link-hover-color:#1e2125;--bs-dropdown-link-hover-bg:#e9ecef;--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#5f7681;--bs-dropdown-link-disabled-color:#adb5bd;--bs-dropdown-item-padding-x:1rem;--bs-dropdown-item-padding-y:0.25rem;--bs-dropdown-header-color:#6c757d;--bs-dropdown-header-padding-x:1rem;--bs-dropdown-header-padding-y:0.5rem;position:absolute;z-index:var(--bs-dropdown-zindex);display:none;min-width:var(--bs-dropdown-min-width);padding:var(--bs-dropdown-padding-y)var(--bs-dropdown-padding-x);margin:0;font-size:var(--bs-dropdown-font-size);color:var(--bs-dropdown-color);text-align:left;list-style:none;background-color:var(--bs-dropdown-bg);background-clip:padding-box;border:var(--bs-dropdown-border-width)solid var(--bs-dropdown-border-color);border-radius:var(--bs-dropdown-border-radius);box-shadow:var(--bs-dropdown-box-shadow)}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:var(--bs-dropdown-spacer)}.dropdown-menu-start{--bs-position:start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position:end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media(min-width:576px){.dropdown-menu-sm-start{--bs-position:start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position:end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media(min-width:768px){.dropdown-menu-md-start{--bs-position:start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position:end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media(min-width:992px){.dropdown-menu-lg-start{--bs-position:start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position:end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media(min-width:1200px){.dropdown-menu-xl-start{--bs-position:start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position:end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media(min-width:1400px){.dropdown-menu-xxl-start{--bs-position:start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position:end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:var(--bs-dropdown-spacer)}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:var(--bs-dropdown-spacer)}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropend .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-toggle::after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:var(--bs-dropdown-spacer)}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropstart .dropdown-toggle::after{display:none}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:var(--bs-dropdown-divider-margin-y)0;overflow:hidden;border-top:1px solid var(--bs-dropdown-divider-bg);opacity:1}.dropdown-item{display:block;width:100%;padding:var(--bs-dropdown-item-padding-y)var(--bs-dropdown-item-padding-x);clear:both;font-weight:400;color:var(--bs-dropdown-link-color);text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:hover,.dropdown-item:focus{color:var(--bs-dropdown-link-hover-color);background-color:var(--bs-dropdown-link-hover-bg);background-image:var(--bs-gradient)}.dropdown-item.active,.dropdown-item:active{color:var(--bs-dropdown-link-active-color);text-decoration:none;background-color:var(--bs-dropdown-link-active-bg);background-image:var(--bs-gradient)}.dropdown-item.disabled,.dropdown-item:disabled{color:var(--bs-dropdown-link-disabled-color);pointer-events:none;background-color:transparent;background-image:none}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:var(--bs-dropdown-header-padding-y)var(--bs-dropdown-header-padding-x);margin-bottom:0;font-size:.875rem;color:var(--bs-dropdown-header-color);white-space:nowrap}.dropdown-item-text{display:block;padding:var(--bs-dropdown-item-padding-y)var(--bs-dropdown-item-padding-x);color:var(--bs-dropdown-link-color)}.dropdown-menu-dark{--bs-dropdown-color:#dee2e6;--bs-dropdown-bg:#343a40;--bs-dropdown-border-color:var(--bs-border-color-translucent);--bs-dropdown-box-shadow: ;--bs-dropdown-link-color:#dee2e6;--bs-dropdown-link-hover-color:#fff;--bs-dropdown-divider-bg:var(--bs-border-color-translucent);--bs-dropdown-link-hover-bg:rgba(255, 255, 255, 0.15);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#5f7681;--bs-dropdown-link-disabled-color:#adb5bd;--bs-dropdown-header-color:#adb5bd}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group>.btn,div.drawio .btn-group>button,.td-blog .btn-group>.td-rss-button,.btn-group-vertical>.btn,div.drawio .btn-group-vertical>button,.td-blog .btn-group-vertical>.td-rss-button{position:relative;flex:auto}.btn-group>.btn-check:checked+.btn,div.drawio .btn-group>.btn-check:checked+button,.td-blog .btn-group>.btn-check:checked+.td-rss-button,.btn-group>.btn-check:focus+.btn,div.drawio .btn-group>.btn-check:focus+button,.td-blog .btn-group>.btn-check:focus+.td-rss-button,.btn-group>.btn:hover,div.drawio .btn-group>button:hover,.td-blog .btn-group>.td-rss-button:hover,.btn-group>.btn:focus,div.drawio .btn-group>button:focus,.td-blog .btn-group>.td-rss-button:focus,.btn-group>.btn:active,div.drawio .btn-group>button:active,.td-blog .btn-group>.td-rss-button:active,.btn-group>.btn.active,div.drawio .btn-group>button.active,.td-blog .btn-group>.active.td-rss-button,.btn-group-vertical>.btn-check:checked+.btn,div.drawio .btn-group-vertical>.btn-check:checked+button,.td-blog .btn-group-vertical>.btn-check:checked+.td-rss-button,.btn-group-vertical>.btn-check:focus+.btn,div.drawio .btn-group-vertical>.btn-check:focus+button,.td-blog .btn-group-vertical>.btn-check:focus+.td-rss-button,.btn-group-vertical>.btn:hover,div.drawio .btn-group-vertical>button:hover,.td-blog .btn-group-vertical>.td-rss-button:hover,.btn-group-vertical>.btn:focus,div.drawio .btn-group-vertical>button:focus,.td-blog .btn-group-vertical>.td-rss-button:focus,.btn-group-vertical>.btn:active,div.drawio .btn-group-vertical>button:active,.td-blog .btn-group-vertical>.td-rss-button:active,.btn-group-vertical>.btn.active,div.drawio .btn-group-vertical>button.active,.td-blog .btn-group-vertical>.active.td-rss-button{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group{border-radius:.375rem}.btn-group>:not(.btn-check:first-child)+.btn,div.drawio .btn-group>:not(.btn-check:first-child)+button,.td-blog .btn-group>:not(.btn-check:first-child)+.td-rss-button,.btn-group>.btn-group:not(:first-child){margin-left:-1px}.btn-group>.btn:not(:last-child):not(.dropdown-toggle),div.drawio .btn-group>button:not(:last-child):not(.dropdown-toggle),.td-blog .btn-group>.td-rss-button:not(:last-child):not(.dropdown-toggle),.btn-group>.btn.dropdown-toggle-split:first-child,div.drawio .btn-group>button.dropdown-toggle-split:first-child,.td-blog .btn-group>.dropdown-toggle-split.td-rss-button:first-child,.btn-group>.btn-group:not(:last-child)>.btn,div.drawio .btn-group>.btn-group:not(:last-child)>button,.td-blog .btn-group>.btn-group:not(:last-child)>.td-rss-button{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:nth-child(n+3),div.drawio .btn-group>button:nth-child(n+3),.td-blog .btn-group>.td-rss-button:nth-child(n+3),.btn-group>:not(.btn-check)+.btn,div.drawio .btn-group>:not(.btn-check)+button,.td-blog .btn-group>:not(.btn-check)+.td-rss-button,.btn-group>.btn-group:not(:first-child)>.btn,div.drawio .btn-group>.btn-group:not(:first-child)>button,.td-blog .btn-group>.btn-group:not(:first-child)>.td-rss-button{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after{margin-left:0}.dropstart .dropdown-toggle-split::before{margin-right:0}.btn-sm+.dropdown-toggle-split,.btn-group-sm>.btn+.dropdown-toggle-split,div.drawio .btn-group-sm>button+.dropdown-toggle-split,.td-blog .btn-group-sm>.td-rss-button+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-lg+.dropdown-toggle-split,.td-blog .td-rss-button+.dropdown-toggle-split,.btn-group-lg>.btn+.dropdown-toggle-split,div.drawio .btn-group-lg>button+.dropdown-toggle-split,.td-blog .btn-group-lg>.td-rss-button+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group.show .dropdown-toggle{box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.show .dropdown-toggle.btn-link{box-shadow:none}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,div.drawio .btn-group-vertical>button,.td-blog .btn-group-vertical>.td-rss-button,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn:not(:first-child),div.drawio .btn-group-vertical>button:not(:first-child),.td-blog .btn-group-vertical>.td-rss-button:not(:first-child),.btn-group-vertical>.btn-group:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle),div.drawio .btn-group-vertical>button:not(:last-child):not(.dropdown-toggle),.td-blog .btn-group-vertical>.td-rss-button:not(:last-child):not(.dropdown-toggle),.btn-group-vertical>.btn-group:not(:last-child)>.btn,div.drawio .btn-group-vertical>.btn-group:not(:last-child)>button,.td-blog .btn-group-vertical>.btn-group:not(:last-child)>.td-rss-button{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn~.btn,div.drawio .btn-group-vertical>button~.btn,div.drawio .btn-group-vertical>.btn~button,div.drawio .btn-group-vertical>button~button,.td-blog .btn-group-vertical>.td-rss-button~.btn,.td-blog div.drawio .btn-group-vertical>.td-rss-button~button,div.drawio .td-blog .btn-group-vertical>.td-rss-button~button,.td-blog .btn-group-vertical>.btn~.td-rss-button,.td-blog div.drawio .btn-group-vertical>button~.td-rss-button,div.drawio .td-blog .btn-group-vertical>button~.td-rss-button,.td-blog .btn-group-vertical>.td-rss-button~.td-rss-button,.btn-group-vertical>.btn-group:not(:first-child)>.btn,div.drawio .btn-group-vertical>.btn-group:not(:first-child)>button,.td-blog .btn-group-vertical>.btn-group:not(:first-child)>.td-rss-button{border-top-left-radius:0;border-top-right-radius:0}.nav{--bs-nav-link-padding-x:1rem;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-link-color);--bs-nav-link-hover-color:var(--bs-link-hover-color);--bs-nav-link-disabled-color:#6c757d;display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:var(--bs-nav-link-padding-y)var(--bs-nav-link-padding-x);font-size:var(--bs-nav-link-font-size);font-weight:var(--bs-nav-link-font-weight);color:var(--bs-nav-link-color);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}@media(prefers-reduced-motion:reduce){.nav-link{transition:none}}.nav-link:hover,.nav-link:focus{color:var(--bs-nav-link-hover-color)}.nav-link.disabled{color:var(--bs-nav-link-disabled-color);pointer-events:none;cursor:default}.nav-tabs{--bs-nav-tabs-border-width:1px;--bs-nav-tabs-border-color:#dee2e6;--bs-nav-tabs-border-radius:0.375rem;--bs-nav-tabs-link-hover-border-color:#e9ecef #e9ecef #dee2e6;--bs-nav-tabs-link-active-color:#495057;--bs-nav-tabs-link-active-bg:#fff;--bs-nav-tabs-link-active-border-color:#dee2e6 #dee2e6 #fff;border-bottom:var(--bs-nav-tabs-border-width)solid var(--bs-nav-tabs-border-color)}.nav-tabs .nav-link{margin-bottom:calc(-1 * var(--bs-nav-tabs-border-width));background:0 0;border:var(--bs-nav-tabs-border-width)solid transparent;border-top-left-radius:var(--bs-nav-tabs-border-radius);border-top-right-radius:var(--bs-nav-tabs-border-radius)}.nav-tabs .nav-link:hover,.nav-tabs .nav-link:focus{isolation:isolate;border-color:var(--bs-nav-tabs-link-hover-border-color)}.nav-tabs .nav-link.disabled,.nav-tabs .nav-link:disabled{color:var(--bs-nav-link-disabled-color);background-color:transparent;border-color:transparent}.nav-tabs .nav-link.active,.nav-tabs .nav-item.show .nav-link{color:var(--bs-nav-tabs-link-active-color);background-color:var(--bs-nav-tabs-link-active-bg);border-color:var(--bs-nav-tabs-link-active-border-color)}.nav-tabs .dropdown-menu{margin-top:calc(-1 * var(--bs-nav-tabs-border-width));border-top-left-radius:0;border-top-right-radius:0}.nav-pills{--bs-nav-pills-border-radius:0.375rem;--bs-nav-pills-link-active-color:#fff;--bs-nav-pills-link-active-bg:#5f7681}.nav-pills .nav-link{background:0 0;border:0;border-radius:var(--bs-nav-pills-border-radius)}.nav-pills .nav-link:disabled{color:var(--bs-nav-link-disabled-color);background-color:transparent;border-color:transparent}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:var(--bs-nav-pills-link-active-color);background-color:var(--bs-nav-pills-link-active-bg);background-image:var(--bs-gradient)}.nav-fill>.nav-link,.nav-fill .nav-item{flex:auto;text-align:center}.nav-justified>.nav-link,.nav-justified .nav-item{flex-basis:0;flex-grow:1;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar,.td-navbar{--bs-navbar-padding-x:0;--bs-navbar-padding-y:0.5rem;--bs-navbar-color:rgba(0, 0, 0, 0.55);--bs-navbar-hover-color:rgba(0, 0, 0, 0.7);--bs-navbar-disabled-color:rgba(0, 0, 0, 0.3);--bs-navbar-active-color:rgba(0, 0, 0, 0.9);--bs-navbar-brand-padding-y:0.3125rem;--bs-navbar-brand-margin-end:1rem;--bs-navbar-brand-font-size:1.25rem;--bs-navbar-brand-color:rgba(0, 0, 0, 0.9);--bs-navbar-brand-hover-color:rgba(0, 0, 0, 0.9);--bs-navbar-nav-link-padding-x:0.5rem;--bs-navbar-toggler-padding-y:0.25rem;--bs-navbar-toggler-padding-x:0.75rem;--bs-navbar-toggler-font-size:1.25rem;--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");--bs-navbar-toggler-border-color:rgba(0, 0, 0, 0.1);--bs-navbar-toggler-border-radius:0.375rem;--bs-navbar-toggler-focus-width:0.25rem;--bs-navbar-toggler-transition:box-shadow 0.15s ease-in-out;position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding:var(--bs-navbar-padding-y)var(--bs-navbar-padding-x);background-image:var(--bs-gradient)}.navbar>.container,.td-navbar>.container,.navbar>.container-fluid,.td-navbar>.container-fluid,.navbar>.container-sm,.td-navbar>.container-sm,.navbar>.container-md,.td-navbar>.container-md,.navbar>.container-lg,.td-navbar>.container-lg,.navbar>.container-xl,.td-navbar>.container-xl,.navbar>.container-xxl,.td-navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:var(--bs-navbar-brand-padding-y);padding-bottom:var(--bs-navbar-brand-padding-y);margin-right:var(--bs-navbar-brand-margin-end);font-size:var(--bs-navbar-brand-font-size);color:var(--bs-navbar-brand-color);white-space:nowrap}.navbar-brand:hover,.navbar-brand:focus{color:var(--bs-navbar-brand-hover-color)}.navbar-nav{--bs-nav-link-padding-x:0;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-navbar-color);--bs-nav-link-hover-color:var(--bs-navbar-hover-color);--bs-nav-link-disabled-color:var(--bs-navbar-disabled-color);display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .show>.nav-link,.navbar-nav .nav-link.active{color:var(--bs-navbar-active-color)}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-navbar-color)}.navbar-text a,.navbar-text a:hover,.navbar-text a:focus{color:var(--bs-navbar-active-color)}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:var(--bs-navbar-toggler-padding-y)var(--bs-navbar-toggler-padding-x);font-size:var(--bs-navbar-toggler-font-size);line-height:1;color:var(--bs-navbar-color);background-color:transparent;border:var(--bs-border-width)solid var(--bs-navbar-toggler-border-color);border-radius:var(--bs-navbar-toggler-border-radius);transition:var(--bs-navbar-toggler-transition)}@media(prefers-reduced-motion:reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 var(--bs-navbar-toggler-focus-width)}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-image:var(--bs-navbar-toggler-icon-bg);background-repeat:no-repeat;background-position:50%;background-size:100%}.navbar-nav-scroll{max-height:var(--bs-scroll-height,75vh);overflow-y:auto}@media(min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}.navbar-expand-sm .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;box-shadow:none;transition:none}.navbar-expand-sm .offcanvas .offcanvas-header{display:none}.navbar-expand-sm .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}.navbar-expand-md .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;box-shadow:none;transition:none}.navbar-expand-md .offcanvas .offcanvas-header{display:none}.navbar-expand-md .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}.navbar-expand-lg .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;box-shadow:none;transition:none}.navbar-expand-lg .offcanvas .offcanvas-header{display:none}.navbar-expand-lg .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}.navbar-expand-xl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;box-shadow:none;transition:none}.navbar-expand-xl .offcanvas .offcanvas-header{display:none}.navbar-expand-xl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}.navbar-expand-xxl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;box-shadow:none;transition:none}.navbar-expand-xxl .offcanvas .offcanvas-header{display:none}.navbar-expand-xxl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}.navbar-expand,.td-navbar{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav,.td-navbar .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu,.td-navbar .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link,.td-navbar .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand .navbar-nav-scroll,.td-navbar .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse,.td-navbar .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler,.td-navbar .navbar-toggler{display:none}.navbar-expand .offcanvas,.td-navbar .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;box-shadow:none;transition:none}.navbar-expand .offcanvas .offcanvas-header,.td-navbar .offcanvas .offcanvas-header{display:none}.navbar-expand .offcanvas .offcanvas-body,.td-navbar .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}.navbar-dark{--bs-navbar-color:rgba(255, 255, 255, 0.75);--bs-navbar-hover-color:rgba(255, 255, 255, 0.5);--bs-navbar-disabled-color:rgba(255, 255, 255, 0.25);--bs-navbar-active-color:#fff;--bs-navbar-brand-color:#fff;--bs-navbar-brand-hover-color:#fff;--bs-navbar-toggler-border-color:rgba(255, 255, 255, 0.1);--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.card,.td-content .highlight{--bs-card-spacer-y:1rem;--bs-card-spacer-x:1rem;--bs-card-title-spacer-y:0.5rem;--bs-card-border-width:1px;--bs-card-border-color:var(--bs-border-color-translucent);--bs-card-border-radius:0.375rem;--bs-card-box-shadow: ;--bs-card-inner-border-radius:calc(0.375rem - 1px);--bs-card-cap-padding-y:0.5rem;--bs-card-cap-padding-x:1rem;--bs-card-cap-bg:rgba(0, 0, 0, 0.03);--bs-card-cap-color: ;--bs-card-height: ;--bs-card-color: ;--bs-card-bg:#fff;--bs-card-img-overlay-padding:1rem;--bs-card-group-margin:0.75rem;position:relative;display:flex;flex-direction:column;min-width:0;height:var(--bs-card-height);word-wrap:break-word;background-color:var(--bs-card-bg);background-clip:border-box;border:var(--bs-card-border-width)solid var(--bs-card-border-color);border-radius:var(--bs-card-border-radius);box-shadow:var(--bs-card-box-shadow)}.card>hr,.td-content .highlight>hr{margin-right:0;margin-left:0}.card>.list-group,.td-content .highlight>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child,.td-content .highlight>.list-group:first-child{border-top-width:0;border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card>.list-group:last-child,.td-content .highlight>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card>.card-header+.list-group,.td-content .highlight>.card-header+.list-group,.card>.list-group+.card-footer,.td-content .highlight>.list-group+.card-footer{border-top:0}.card-body{flex:auto;padding:var(--bs-card-spacer-y)var(--bs-card-spacer-x);color:var(--bs-card-color)}.card-title{margin-bottom:var(--bs-card-title-spacer-y)}.card-subtitle{margin-top:calc(-.5 * var(--bs-card-title-spacer-y));margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link+.card-link{margin-left:var(--bs-card-spacer-x)}.card-header{padding:var(--bs-card-cap-padding-y)var(--bs-card-cap-padding-x);margin-bottom:0;color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-bottom:var(--bs-card-border-width)solid var(--bs-card-border-color)}.card-header:first-child{border-radius:var(--bs-card-inner-border-radius)var(--bs-card-inner-border-radius)0 0}.card-footer{padding:var(--bs-card-cap-padding-y)var(--bs-card-cap-padding-x);color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-top:var(--bs-card-border-width)solid var(--bs-card-border-color)}.card-footer:last-child{border-radius:0 0 var(--bs-card-inner-border-radius)var(--bs-card-inner-border-radius)}.card-header-tabs{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-bottom:calc(-1 * var(--bs-card-cap-padding-y));margin-left:calc(-.5 * var(--bs-card-cap-padding-x));border-bottom:0}.card-header-tabs .nav-link.active{background-color:var(--bs-card-bg);border-bottom-color:var(--bs-card-bg)}.card-header-pills{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-left:calc(-.5 * var(--bs-card-cap-padding-x))}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:var(--bs-card-img-overlay-padding);border-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-top,.card-img-bottom{width:100%}.card-img,.card-img-top{border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom{border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card-group>.card,.td-content .card-group>.highlight{margin-bottom:var(--bs-card-group-margin)}@media(min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card,.td-content .card-group>.highlight{flex:1 0;margin-bottom:0}.card-group>.card+.card,.td-content .card-group>.highlight+.card,.td-content .card-group>.card+.highlight,.td-content .card-group>.highlight+.highlight{margin-left:0;border-left:0}.card-group>.card:not(:last-child),.td-content .card-group>.highlight:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-img-top,.td-content .card-group>.highlight:not(:last-child) .card-img-top,.card-group>.card:not(:last-child) .card-header,.td-content .card-group>.highlight:not(:last-child) .card-header{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-img-bottom,.td-content .card-group>.highlight:not(:last-child) .card-img-bottom,.card-group>.card:not(:last-child) .card-footer,.td-content .card-group>.highlight:not(:last-child) .card-footer{border-bottom-right-radius:0}.card-group>.card:not(:first-child),.td-content .card-group>.highlight:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-img-top,.td-content .card-group>.highlight:not(:first-child) .card-img-top,.card-group>.card:not(:first-child) .card-header,.td-content .card-group>.highlight:not(:first-child) .card-header{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-img-bottom,.td-content .card-group>.highlight:not(:first-child) .card-img-bottom,.card-group>.card:not(:first-child) .card-footer,.td-content .card-group>.highlight:not(:first-child) .card-footer{border-bottom-left-radius:0}}.accordion{--bs-accordion-color:#212529;--bs-accordion-bg:#fff;--bs-accordion-transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, border-radius 0.15s ease;--bs-accordion-border-color:var(--bs-border-color);--bs-accordion-border-width:1px;--bs-accordion-border-radius:0.375rem;--bs-accordion-inner-border-radius:calc(0.375rem - 1px);--bs-accordion-btn-padding-x:1.25rem;--bs-accordion-btn-padding-y:1rem;--bs-accordion-btn-color:#212529;--bs-accordion-btn-bg:var(--bs-accordion-bg);--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");--bs-accordion-btn-icon-width:1.25rem;--bs-accordion-btn-icon-transform:rotate(-180deg);--bs-accordion-btn-icon-transition:transform 0.2s ease-in-out;--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23566a74'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");--bs-accordion-btn-focus-border-color:#afbbc0;--bs-accordion-btn-focus-box-shadow:0 0 0 0.25rem rgba(95, 118, 129, 0.25);--bs-accordion-body-padding-x:1.25rem;--bs-accordion-body-padding-y:1rem;--bs-accordion-active-color:#566a74;--bs-accordion-active-bg:#eff1f2}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:var(--bs-accordion-btn-padding-y)var(--bs-accordion-btn-padding-x);font-size:1rem;color:var(--bs-accordion-btn-color);text-align:left;background-color:var(--bs-accordion-btn-bg);border:0;border-radius:0;overflow-anchor:none;transition:var(--bs-accordion-transition)}@media(prefers-reduced-motion:reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:var(--bs-accordion-active-color);background-color:var(--bs-accordion-active-bg);box-shadow:inset 0 calc(-1 * var(--bs-accordion-border-width))0 var(--bs-accordion-border-color)}.accordion-button:not(.collapsed)::after{background-image:var(--bs-accordion-btn-active-icon);transform:var(--bs-accordion-btn-icon-transform)}.accordion-button::after{flex-shrink:0;width:var(--bs-accordion-btn-icon-width);height:var(--bs-accordion-btn-icon-width);margin-left:auto;content:"";background-image:var(--bs-accordion-btn-icon);background-repeat:no-repeat;background-size:var(--bs-accordion-btn-icon-width);transition:var(--bs-accordion-btn-icon-transition)}@media(prefers-reduced-motion:reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;border-color:var(--bs-accordion-btn-focus-border-color);outline:0;box-shadow:var(--bs-accordion-btn-focus-box-shadow)}.accordion-header{margin-bottom:0}.accordion-item{color:var(--bs-accordion-color);background-color:var(--bs-accordion-bg);border:var(--bs-accordion-border-width)solid var(--bs-accordion-border-color)}.accordion-item:first-of-type{border-top-left-radius:var(--bs-accordion-border-radius);border-top-right-radius:var(--bs-accordion-border-radius)}.accordion-item:first-of-type .accordion-button{border-top-left-radius:var(--bs-accordion-inner-border-radius);border-top-right-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-item:last-of-type .accordion-button.collapsed{border-bottom-right-radius:var(--bs-accordion-inner-border-radius);border-bottom-left-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:last-of-type .accordion-collapse{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-body{padding:var(--bs-accordion-body-padding-y)var(--bs-accordion-body-padding-x)}.accordion-flush .accordion-collapse{border-width:0}.accordion-flush .accordion-item{border-right:0;border-left:0;border-radius:0}.accordion-flush .accordion-item:first-child{border-top:0}.accordion-flush .accordion-item:last-child{border-bottom:0}.accordion-flush .accordion-item .accordion-button,.accordion-flush .accordion-item .accordion-button.collapsed{border-radius:0}.breadcrumb{--bs-breadcrumb-padding-x:0;--bs-breadcrumb-padding-y:0;--bs-breadcrumb-margin-bottom:1rem;--bs-breadcrumb-bg: ;--bs-breadcrumb-border-radius: ;--bs-breadcrumb-divider-color:#6c757d;--bs-breadcrumb-item-padding-x:0.5rem;--bs-breadcrumb-item-active-color:#6c757d;display:flex;flex-wrap:wrap;padding:var(--bs-breadcrumb-padding-y)var(--bs-breadcrumb-padding-x);margin-bottom:var(--bs-breadcrumb-margin-bottom);font-size:var(--bs-breadcrumb-font-size);list-style:none;background-color:var(--bs-breadcrumb-bg);border-radius:var(--bs-breadcrumb-border-radius)}.breadcrumb-item+.breadcrumb-item{padding-left:var(--bs-breadcrumb-item-padding-x)}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:var(--bs-breadcrumb-item-padding-x);color:var(--bs-breadcrumb-divider-color);content:var(--bs-breadcrumb-divider,"/")}.breadcrumb-item.active{color:var(--bs-breadcrumb-item-active-color)}.pagination{--bs-pagination-padding-x:0.75rem;--bs-pagination-padding-y:0.375rem;--bs-pagination-font-size:1rem;--bs-pagination-color:#6c757d;--bs-pagination-bg:#fff;--bs-pagination-border-width:1px;--bs-pagination-border-color:#dee2e6;--bs-pagination-border-radius:0.375rem;--bs-pagination-hover-color:var(--bs-link-hover-color);--bs-pagination-hover-bg:#e9ecef;--bs-pagination-hover-border-color:#dee2e6;--bs-pagination-focus-color:var(--bs-link-hover-color);--bs-pagination-focus-bg:#e9ecef;--bs-pagination-focus-box-shadow:0 0 0 0.25rem rgba(95, 118, 129, 0.25);--bs-pagination-active-color:#fff;--bs-pagination-active-bg:#5f7681;--bs-pagination-active-border-color:#5f7681;--bs-pagination-disabled-color:#dee2e6;--bs-pagination-disabled-bg:#fff;--bs-pagination-disabled-border-color:#dee2e6;display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;padding:var(--bs-pagination-padding-y)var(--bs-pagination-padding-x);font-size:var(--bs-pagination-font-size);color:var(--bs-pagination-color);background-color:var(--bs-pagination-bg);border:var(--bs-pagination-border-width)solid var(--bs-pagination-border-color);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:var(--bs-pagination-hover-color);background-color:var(--bs-pagination-hover-bg);border-color:var(--bs-pagination-hover-border-color)}.page-link:focus{z-index:3;color:var(--bs-pagination-focus-color);background-color:var(--bs-pagination-focus-bg);outline:0;box-shadow:var(--bs-pagination-focus-box-shadow)}.page-link.active,.active>.page-link{z-index:3;color:var(--bs-pagination-active-color);background-color:var(--bs-pagination-active-bg);background-image:var(--bs-gradient);border-color:var(--bs-pagination-active-border-color)}.page-link.disabled,.disabled>.page-link{color:var(--bs-pagination-disabled-color);pointer-events:none;background-color:var(--bs-pagination-disabled-bg);border-color:var(--bs-pagination-disabled-border-color)}.page-item:not(:first-child) .page-link{margin-left:-1px}.page-item:first-child .page-link{border-top-left-radius:var(--bs-pagination-border-radius);border-bottom-left-radius:var(--bs-pagination-border-radius)}.page-item:last-child .page-link{border-top-right-radius:var(--bs-pagination-border-radius);border-bottom-right-radius:var(--bs-pagination-border-radius)}.pagination-lg{--bs-pagination-padding-x:1.5rem;--bs-pagination-padding-y:0.75rem;--bs-pagination-font-size:1.25rem;--bs-pagination-border-radius:0.5rem}.pagination-sm{--bs-pagination-padding-x:0.5rem;--bs-pagination-padding-y:0.25rem;--bs-pagination-font-size:0.875rem;--bs-pagination-border-radius:0.25rem}.badge{--bs-badge-padding-x:0.65em;--bs-badge-padding-y:0.35em;--bs-badge-font-size:0.75em;--bs-badge-font-weight:700;--bs-badge-color:#fff;--bs-badge-border-radius:0.375rem;display:inline-block;padding:var(--bs-badge-padding-y)var(--bs-badge-padding-x);font-size:var(--bs-badge-font-size);font-weight:var(--bs-badge-font-weight);line-height:1;color:var(--bs-badge-color);text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:var(--bs-badge-border-radius);background-image:var(--bs-gradient)}.badge:empty{display:none}.btn .badge,div.drawio button .badge,.td-blog .td-rss-button .badge{position:relative;top:-1px}.alert{--bs-alert-bg:transparent;--bs-alert-padding-x:1rem;--bs-alert-padding-y:1rem;--bs-alert-margin-bottom:1rem;--bs-alert-color:inherit;--bs-alert-border-color:transparent;--bs-alert-border:1px solid var(--bs-alert-border-color);--bs-alert-border-radius:0.375rem;position:relative;padding:var(--bs-alert-padding-y)var(--bs-alert-padding-x);margin-bottom:var(--bs-alert-margin-bottom);color:var(--bs-alert-color);background-color:var(--bs-alert-bg);border:var(--bs-alert-border);border-radius:var(--bs-alert-border-radius)}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem}.alert-primary{--bs-alert-color:#39474d;--bs-alert-bg:#dfe4e6;--bs-alert-border-color:#cfd6d9;background-image:var(--bs-gradient)}.alert-primary .alert-link{color:#2e393e}.alert-secondary{--bs-alert-color:#934f01;--bs-alert-bg:#fde6cc;--bs-alert-border-color:#fcdab3;background-image:var(--bs-gradient)}.alert-secondary .alert-link{color:#763f01}.alert-success{--bs-alert-color:#214499;--bs-alert-bg:#d7e3ff;--bs-alert-border-color:#c3d5ff;background-image:var(--bs-gradient)}.alert-success .alert-link{color:#1a367a}.alert-info{--bs-alert-color:#004973;--bs-alert-bg:#cce4f2;--bs-alert-border-color:#b3d7ec;background-image:var(--bs-gradient)}.alert-info .alert-link{color:#003a5c}.alert-warning{--bs-alert-color:#881d1d;--bs-alert-bg:#f9d6d6;--bs-alert-border-color:#f7c1c1;background-image:var(--bs-gradient)}.alert-warning .alert-link{color:#6d1717}.alert-danger{--bs-alert-color:#8e4036;--bs-alert-bg:#fbe1de;--bs-alert-border-color:#fad2ce;background-image:var(--bs-gradient)}.alert-danger .alert-link{color:#72332b}.alert-light{--bs-alert-color:#54615f;--bs-alert-bg:#f6fdfc;--bs-alert-border-color:#f2fbfa;background-image:var(--bs-gradient)}.alert-light .alert-link{color:#434e4c}.alert-dark{--bs-alert-color:#1f262b;--bs-alert-bg:#d6d9da;--bs-alert-border-color:#c2c5c8;background-image:var(--bs-gradient)}.alert-dark .alert-link{color:#191e22}@keyframes progress-bar-stripes{0%{background-position-x:1rem}}.progress{--bs-progress-height:1rem;--bs-progress-font-size:0.75rem;--bs-progress-bg:#e9ecef;--bs-progress-border-radius:0.375rem;--bs-progress-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.075);--bs-progress-bar-color:#fff;--bs-progress-bar-bg:#5f7681;--bs-progress-bar-transition:width 0.6s ease;display:flex;height:var(--bs-progress-height);overflow:hidden;font-size:var(--bs-progress-font-size);background-color:var(--bs-progress-bg);border-radius:var(--bs-progress-border-radius);box-shadow:var(--bs-progress-box-shadow)}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:var(--bs-progress-bar-color);text-align:center;white-space:nowrap;background-color:var(--bs-progress-bar-bg);transition:var(--bs-progress-bar-transition)}@media(prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:var(--bs-progress-height)var(--bs-progress-height)}.progress-bar-animated{animation:1s linear infinite progress-bar-stripes}@media(prefers-reduced-motion:reduce){.progress-bar-animated{animation:none}}.list-group{--bs-list-group-color:#212529;--bs-list-group-bg:#fff;--bs-list-group-border-color:rgba(0, 0, 0, 0.125);--bs-list-group-border-width:1px;--bs-list-group-border-radius:0.375rem;--bs-list-group-item-padding-x:1rem;--bs-list-group-item-padding-y:0.5rem;--bs-list-group-action-color:#495057;--bs-list-group-action-hover-color:#495057;--bs-list-group-action-hover-bg:#f8f9fa;--bs-list-group-action-active-color:#212529;--bs-list-group-action-active-bg:#e9ecef;--bs-list-group-disabled-color:#6c757d;--bs-list-group-disabled-bg:#fff;--bs-list-group-active-color:#fff;--bs-list-group-active-bg:#5f7681;--bs-list-group-active-border-color:#5f7681;display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:var(--bs-list-group-border-radius)}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>.list-group-item::before{content:counters(section,".")". ";counter-increment:section}.list-group-item-action{width:100%;color:var(--bs-list-group-action-color);text-align:inherit}.list-group-item-action:hover,.list-group-item-action:focus{z-index:1;color:var(--bs-list-group-action-hover-color);text-decoration:none;background-color:var(--bs-list-group-action-hover-bg)}.list-group-item-action:active{color:var(--bs-list-group-action-active-color);background-color:var(--bs-list-group-action-active-bg)}.list-group-item{position:relative;display:block;padding:var(--bs-list-group-item-padding-y)var(--bs-list-group-item-padding-x);color:var(--bs-list-group-color);background-color:var(--bs-list-group-bg);border:var(--bs-list-group-border-width)solid var(--bs-list-group-border-color)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:var(--bs-list-group-disabled-color);pointer-events:none;background-color:var(--bs-list-group-disabled-bg)}.list-group-item.active{z-index:2;color:var(--bs-list-group-active-color);background-color:var(--bs-list-group-active-bg);border-color:var(--bs-list-group-active-border-color)}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:calc(-1 * var(--bs-list-group-border-width));border-top-width:var(--bs-list-group-border-width)}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}@media(min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 var(--bs-list-group-border-width)}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#39474d;background-color:#dfe4e6}.list-group-item-primary.list-group-item-action:hover,.list-group-item-primary.list-group-item-action:focus{color:#39474d;background-color:#c9cdcf}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#39474d;border-color:#39474d}.list-group-item-secondary{color:#934f01;background-color:#fde6cc}.list-group-item-secondary.list-group-item-action:hover,.list-group-item-secondary.list-group-item-action:focus{color:#934f01;background-color:#e4cfb8}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#934f01;border-color:#934f01}.list-group-item-success{color:#214499;background-color:#d7e3ff}.list-group-item-success.list-group-item-action:hover,.list-group-item-success.list-group-item-action:focus{color:#214499;background-color:#c2cce6}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#214499;border-color:#214499}.list-group-item-info{color:#004973;background-color:#cce4f2}.list-group-item-info.list-group-item-action:hover,.list-group-item-info.list-group-item-action:focus{color:#004973;background-color:#b8cdda}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#004973;border-color:#004973}.list-group-item-warning{color:#881d1d;background-color:#f9d6d6}.list-group-item-warning.list-group-item-action:hover,.list-group-item-warning.list-group-item-action:focus{color:#881d1d;background-color:#e0c1c1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#881d1d;border-color:#881d1d}.list-group-item-danger{color:#8e4036;background-color:#fbe1de}.list-group-item-danger.list-group-item-action:hover,.list-group-item-danger.list-group-item-action:focus{color:#8e4036;background-color:#e2cbc8}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#8e4036;border-color:#8e4036}.list-group-item-light{color:#54615f;background-color:#f6fdfc}.list-group-item-light.list-group-item-action:hover,.list-group-item-light.list-group-item-action:focus{color:#54615f;background-color:#dde4e3}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#54615f;border-color:#54615f}.list-group-item-dark{color:#1f262b;background-color:#d6d9da}.list-group-item-dark.list-group-item-action:hover,.list-group-item-dark.list-group-item-action:focus{color:#1f262b;background-color:#c1c3c4}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1f262b;border-color:#1f262b}.btn-close{box-sizing:content-box;width:1em;height:1em;padding:.25em;color:#000;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e")50%/1em no-repeat;border:0;border-radius:.375rem;opacity:.5}.btn-close:hover{color:#000;text-decoration:none;opacity:.75}.btn-close:focus{outline:0;box-shadow:0 0 0 .25rem rgba(95,118,129,.25);opacity:1}.btn-close:disabled,.btn-close.disabled{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;opacity:.25}.btn-close-white{filter:invert(1)grayscale(100%)brightness(200%)}.toast{--bs-toast-zindex:1090;--bs-toast-padding-x:0.75rem;--bs-toast-padding-y:0.5rem;--bs-toast-spacing:1.5rem;--bs-toast-max-width:350px;--bs-toast-font-size:0.875rem;--bs-toast-color: ;--bs-toast-bg:rgba(255, 255, 255, 0.85);--bs-toast-border-width:1px;--bs-toast-border-color:var(--bs-border-color-translucent);--bs-toast-border-radius:0.375rem;--bs-toast-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-toast-header-color:#6c757d;--bs-toast-header-bg:rgba(255, 255, 255, 0.85);--bs-toast-header-border-color:rgba(0, 0, 0, 0.05);width:var(--bs-toast-max-width);max-width:100%;font-size:var(--bs-toast-font-size);color:var(--bs-toast-color);pointer-events:auto;background-color:var(--bs-toast-bg);background-clip:padding-box;border:var(--bs-toast-border-width)solid var(--bs-toast-border-color);box-shadow:var(--bs-toast-box-shadow);border-radius:var(--bs-toast-border-radius)}.toast.showing{opacity:0}.toast:not(.show){display:none}.toast-container{--bs-toast-zindex:1090;position:absolute;z-index:var(--bs-toast-zindex);width:-moz-max-content;width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:var(--bs-toast-spacing)}.toast-header{display:flex;align-items:center;padding:var(--bs-toast-padding-y)var(--bs-toast-padding-x);color:var(--bs-toast-header-color);background-color:var(--bs-toast-header-bg);background-clip:padding-box;border-bottom:var(--bs-toast-border-width)solid var(--bs-toast-header-border-color);border-top-left-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width));border-top-right-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width))}.toast-header .btn-close{margin-right:calc(-.5 * var(--bs-toast-padding-x));margin-left:var(--bs-toast-padding-x)}.toast-body{padding:var(--bs-toast-padding-x);word-wrap:break-word}.modal{--bs-modal-zindex:1055;--bs-modal-width:500px;--bs-modal-padding:1rem;--bs-modal-margin:0.5rem;--bs-modal-color: ;--bs-modal-bg:#fff;--bs-modal-border-color:var(--bs-border-color-translucent);--bs-modal-border-width:1px;--bs-modal-border-radius:0.5rem;--bs-modal-box-shadow:0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-modal-inner-border-radius:calc(0.5rem - 1px);--bs-modal-header-padding-x:1rem;--bs-modal-header-padding-y:1rem;--bs-modal-header-padding:1rem 1rem;--bs-modal-header-border-color:var(--bs-border-color);--bs-modal-header-border-width:1px;--bs-modal-title-line-height:1.5;--bs-modal-footer-gap:0.5rem;--bs-modal-footer-bg: ;--bs-modal-footer-border-color:var(--bs-border-color);--bs-modal-footer-border-width:1px;position:fixed;top:0;left:0;z-index:var(--bs-modal-zindex);display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:var(--bs-modal-margin);pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-50px)}@media(prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - var(--bs-modal-margin) * 2)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - var(--bs-modal-margin) * 2)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;color:var(--bs-modal-color);pointer-events:auto;background-color:var(--bs-modal-bg);background-clip:padding-box;border:var(--bs-modal-border-width)solid var(--bs-modal-border-color);border-radius:var(--bs-modal-border-radius);box-shadow:var(--bs-modal-box-shadow);outline:0}.modal-backdrop{--bs-backdrop-zindex:1050;--bs-backdrop-bg:#000;--bs-backdrop-opacity:0.5;position:fixed;top:0;left:0;z-index:var(--bs-backdrop-zindex);width:100vw;height:100vh;background-color:var(--bs-backdrop-bg)}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:var(--bs-backdrop-opacity)}.modal-header{display:flex;flex-shrink:0;align-items:center;justify-content:space-between;padding:var(--bs-modal-header-padding);border-bottom:var(--bs-modal-header-border-width)solid var(--bs-modal-header-border-color);border-top-left-radius:var(--bs-modal-inner-border-radius);border-top-right-radius:var(--bs-modal-inner-border-radius)}.modal-header .btn-close{padding:calc(var(--bs-modal-header-padding-y) * .5);margin:calc(-.5 * var(--bs-modal-header-padding-y))calc(-.5 * var(--bs-modal-header-padding-x))calc(-.5 * var(--bs-modal-header-padding-y))auto}.modal-title{margin-bottom:0;line-height:var(--bs-modal-title-line-height)}.modal-body{position:relative;flex:auto;padding:var(--bs-modal-padding)}.modal-footer{display:flex;flex-shrink:0;flex-wrap:wrap;align-items:center;justify-content:flex-end;padding:calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap) * .5);background-color:var(--bs-modal-footer-bg);border-top:var(--bs-modal-footer-border-width)solid var(--bs-modal-footer-border-color);border-bottom-right-radius:var(--bs-modal-inner-border-radius);border-bottom-left-radius:var(--bs-modal-inner-border-radius)}.modal-footer>*{margin:calc(var(--bs-modal-footer-gap) * .5)}@media(min-width:576px){.modal{--bs-modal-margin:1.75rem;--bs-modal-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15)}.modal-dialog{max-width:var(--bs-modal-width);margin-right:auto;margin-left:auto}.modal-sm{--bs-modal-width:300px}}@media(min-width:992px){.modal-lg,.modal-xl{--bs-modal-width:800px}}@media(min-width:1200px){.modal-xl{--bs-modal-width:1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen .modal-header,.modal-fullscreen .modal-footer{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}@media(max-width:575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-sm-down .modal-header,.modal-fullscreen-sm-down .modal-footer{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}}@media(max-width:767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-md-down .modal-header,.modal-fullscreen-md-down .modal-footer{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}}@media(max-width:991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-lg-down .modal-header,.modal-fullscreen-lg-down .modal-footer{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}}@media(max-width:1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xl-down .modal-header,.modal-fullscreen-xl-down .modal-footer{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}}@media(max-width:1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xxl-down .modal-header,.modal-fullscreen-xxl-down .modal-footer{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}}.tooltip{--bs-tooltip-zindex:1080;--bs-tooltip-max-width:200px;--bs-tooltip-padding-x:0.5rem;--bs-tooltip-padding-y:0.25rem;--bs-tooltip-margin: ;--bs-tooltip-font-size:0.875rem;--bs-tooltip-color:#fff;--bs-tooltip-bg:#000;--bs-tooltip-border-radius:0.375rem;--bs-tooltip-opacity:0.9;--bs-tooltip-arrow-width:0.8rem;--bs-tooltip-arrow-height:0.4rem;z-index:var(--bs-tooltip-zindex);display:block;padding:var(--bs-tooltip-arrow-height);margin:var(--bs-tooltip-margin);font-family:open sans,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-tooltip-font-size);word-wrap:break-word;opacity:0}.tooltip.show{opacity:var(--bs-tooltip-opacity)}.tooltip .tooltip-arrow{display:block;width:var(--bs-tooltip-arrow-width);height:var(--bs-tooltip-arrow-height)}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-top .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow{bottom:0}.bs-tooltip-top .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before{top:-1px;border-width:var(--bs-tooltip-arrow-height)calc(var(--bs-tooltip-arrow-width) * .5)0;border-top-color:var(--bs-tooltip-bg)}.bs-tooltip-end .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow{left:0;width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-end .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before{right:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5)var(--bs-tooltip-arrow-height)calc(var(--bs-tooltip-arrow-width) * .5)0;border-right-color:var(--bs-tooltip-bg)}.bs-tooltip-bottom .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow{top:0}.bs-tooltip-bottom .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before{bottom:-1px;border-width:0 calc(var(--bs-tooltip-arrow-width) * .5)var(--bs-tooltip-arrow-height);border-bottom-color:var(--bs-tooltip-bg)}.bs-tooltip-start .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow{right:0;width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-start .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before{left:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5)0 calc(var(--bs-tooltip-arrow-width) * .5)var(--bs-tooltip-arrow-height);border-left-color:var(--bs-tooltip-bg)}.tooltip-inner{max-width:var(--bs-tooltip-max-width);padding:var(--bs-tooltip-padding-y)var(--bs-tooltip-padding-x);color:var(--bs-tooltip-color);text-align:center;background-color:var(--bs-tooltip-bg);border-radius:var(--bs-tooltip-border-radius)}.popover{--bs-popover-zindex:1070;--bs-popover-max-width:276px;--bs-popover-font-size:0.875rem;--bs-popover-bg:#fff;--bs-popover-border-width:1px;--bs-popover-border-color:var(--bs-border-color-translucent);--bs-popover-border-radius:0.5rem;--bs-popover-inner-border-radius:calc(0.5rem - 1px);--bs-popover-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-popover-header-padding-x:1rem;--bs-popover-header-padding-y:0.5rem;--bs-popover-header-font-size:1rem;--bs-popover-header-color: ;--bs-popover-header-bg:#f0f0f0;--bs-popover-body-padding-x:1rem;--bs-popover-body-padding-y:1rem;--bs-popover-body-color:#212529;--bs-popover-arrow-width:1rem;--bs-popover-arrow-height:0.5rem;--bs-popover-arrow-border:var(--bs-popover-border-color);z-index:var(--bs-popover-zindex);display:block;max-width:var(--bs-popover-max-width);font-family:open sans,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-popover-font-size);word-wrap:break-word;background-color:var(--bs-popover-bg);background-clip:padding-box;border:var(--bs-popover-border-width)solid var(--bs-popover-border-color);border-radius:var(--bs-popover-border-radius);box-shadow:var(--bs-popover-box-shadow)}.popover .popover-arrow{display:block;width:var(--bs-popover-arrow-width);height:var(--bs-popover-arrow-height)}.popover .popover-arrow::before,.popover .popover-arrow::after{position:absolute;display:block;content:"";border-color:transparent;border-style:solid;border-width:0}.bs-popover-top>.popover-arrow,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow{bottom:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-top>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after{border-width:var(--bs-popover-arrow-height)calc(var(--bs-popover-arrow-width) * .5)0}.bs-popover-top>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before{bottom:0;border-top-color:var(--bs-popover-arrow-border)}.bs-popover-top>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after{bottom:var(--bs-popover-border-width);border-top-color:var(--bs-popover-bg)}.bs-popover-end>.popover-arrow,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow{left:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-end>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after{border-width:calc(var(--bs-popover-arrow-width) * .5)var(--bs-popover-arrow-height)calc(var(--bs-popover-arrow-width) * .5)0}.bs-popover-end>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before{left:0;border-right-color:var(--bs-popover-arrow-border)}.bs-popover-end>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after{left:var(--bs-popover-border-width);border-right-color:var(--bs-popover-bg)}.bs-popover-bottom>.popover-arrow,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow{top:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-bottom>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after{border-width:0 calc(var(--bs-popover-arrow-width) * .5)var(--bs-popover-arrow-height)}.bs-popover-bottom>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before{top:0;border-bottom-color:var(--bs-popover-arrow-border)}.bs-popover-bottom>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after{top:var(--bs-popover-border-width);border-bottom-color:var(--bs-popover-bg)}.bs-popover-bottom .popover-header::before,.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before{position:absolute;top:0;left:50%;display:block;width:var(--bs-popover-arrow-width);margin-left:calc(-.5 * var(--bs-popover-arrow-width));content:"";border-bottom:var(--bs-popover-border-width)solid var(--bs-popover-header-bg)}.bs-popover-start>.popover-arrow,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow{right:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-start>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after{border-width:calc(var(--bs-popover-arrow-width) * .5)0 calc(var(--bs-popover-arrow-width) * .5)var(--bs-popover-arrow-height)}.bs-popover-start>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before{right:0;border-left-color:var(--bs-popover-arrow-border)}.bs-popover-start>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after{right:var(--bs-popover-border-width);border-left-color:var(--bs-popover-bg)}.popover-header{padding:var(--bs-popover-header-padding-y)var(--bs-popover-header-padding-x);margin-bottom:0;font-size:var(--bs-popover-header-font-size);color:var(--bs-popover-header-color);background-color:var(--bs-popover-header-bg);border-bottom:var(--bs-popover-border-width)solid var(--bs-popover-border-color);border-top-left-radius:var(--bs-popover-inner-border-radius);border-top-right-radius:var(--bs-popover-inner-border-radius)}.popover-header:empty{display:none}.popover-body{padding:var(--bs-popover-body-padding-y)var(--bs-popover-body-padding-x);color:var(--bs-popover-body-color)}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;backface-visibility:hidden;transition:transform .6s ease-in-out}@media(prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item.active,.carousel-item-next,.carousel-item-prev{display:block}.carousel-item-next:not(.carousel-item-start),.active.carousel-item-end{transform:translateX(100%)}.carousel-item-prev:not(.carousel-item-end),.active.carousel-item-start{transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item.active,.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end{z-index:1;opacity:1}.carousel-fade .active.carousel-item-start,.carousel-fade .active.carousel-item-end{z-index:0;opacity:0;transition:opacity 0s .6s}@media(prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-start,.carousel-fade .active.carousel-item-end{transition:none}}.carousel-control-prev,.carousel-control-next{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:0 0;border:0;opacity:.5;transition:opacity .15s ease}@media(prefers-reduced-motion:reduce){.carousel-control-prev,.carousel-control-next{transition:none}}.carousel-control-prev:hover,.carousel-control-prev:focus,.carousel-control-next:hover,.carousel-control-next:focus{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0;background-image:linear-gradient(90deg,rgba(0,0,0,.25),rgba(0,0,0,.1%))}.carousel-control-next{right:0;background-image:linear-gradient(270deg,rgba(0,0,0,.25),rgba(0,0,0,.1%))}.carousel-control-prev-icon,.carousel-control-next-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%;list-style:none}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:initial;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media(prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:#fff;text-align:center}.carousel-dark .carousel-control-prev-icon,.carousel-dark .carousel-control-next-icon{filter:invert(1)grayscale(100)}.carousel-dark .carousel-indicators [data-bs-target]{background-color:#000}.carousel-dark .carousel-caption{color:#000}.spinner-grow,.spinner-border{display:inline-block;width:var(--bs-spinner-width);height:var(--bs-spinner-height);vertical-align:var(--bs-spinner-vertical-align);border-radius:50%;animation:var(--bs-spinner-animation-speed)linear infinite var(--bs-spinner-animation-name)}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-border-width:0.25em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-border;border:var(--bs-spinner-border-width)solid;border-right-color:transparent}.spinner-border-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem;--bs-spinner-border-width:0.2em}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-grow;background-color:currentcolor;opacity:0}.spinner-grow-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem}@media(prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{--bs-spinner-animation-speed:1.5s}}.offcanvas,.offcanvas-xxl,.offcanvas-xl,.offcanvas-lg,.offcanvas-md,.offcanvas-sm{--bs-offcanvas-zindex:1045;--bs-offcanvas-width:400px;--bs-offcanvas-height:30vh;--bs-offcanvas-padding-x:1rem;--bs-offcanvas-padding-y:1rem;--bs-offcanvas-color: ;--bs-offcanvas-bg:#fff;--bs-offcanvas-border-width:1px;--bs-offcanvas-border-color:var(--bs-border-color-translucent);--bs-offcanvas-box-shadow:0 0.125rem 0.25rem rgba(0, 0, 0, 0.075)}@media(max-width:575.98px){.offcanvas-sm{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;box-shadow:var(--bs-offcanvas-box-shadow);transition:transform .3s ease-in-out}}@media(max-width:575.98px) and (prefers-reduced-motion:reduce){.offcanvas-sm{transition:none}}@media(max-width:575.98px){.offcanvas-sm.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-sm.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-sm.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-sm.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-sm.showing,.offcanvas-sm.show:not(.hiding){transform:none}.offcanvas-sm.showing,.offcanvas-sm.hiding,.offcanvas-sm.show{visibility:visible}}@media(min-width:576px){.offcanvas-sm{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-sm .offcanvas-header{display:none}.offcanvas-sm .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media(max-width:767.98px){.offcanvas-md{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;box-shadow:var(--bs-offcanvas-box-shadow);transition:transform .3s ease-in-out}}@media(max-width:767.98px) and (prefers-reduced-motion:reduce){.offcanvas-md{transition:none}}@media(max-width:767.98px){.offcanvas-md.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-md.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-md.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-md.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-md.showing,.offcanvas-md.show:not(.hiding){transform:none}.offcanvas-md.showing,.offcanvas-md.hiding,.offcanvas-md.show{visibility:visible}}@media(min-width:768px){.offcanvas-md{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-md .offcanvas-header{display:none}.offcanvas-md .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media(max-width:991.98px){.offcanvas-lg{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;box-shadow:var(--bs-offcanvas-box-shadow);transition:transform .3s ease-in-out}}@media(max-width:991.98px) and (prefers-reduced-motion:reduce){.offcanvas-lg{transition:none}}@media(max-width:991.98px){.offcanvas-lg.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-lg.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-lg.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-lg.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-lg.showing,.offcanvas-lg.show:not(.hiding){transform:none}.offcanvas-lg.showing,.offcanvas-lg.hiding,.offcanvas-lg.show{visibility:visible}}@media(min-width:992px){.offcanvas-lg{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-lg .offcanvas-header{display:none}.offcanvas-lg .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media(max-width:1199.98px){.offcanvas-xl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;box-shadow:var(--bs-offcanvas-box-shadow);transition:transform .3s ease-in-out}}@media(max-width:1199.98px) and (prefers-reduced-motion:reduce){.offcanvas-xl{transition:none}}@media(max-width:1199.98px){.offcanvas-xl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xl.showing,.offcanvas-xl.show:not(.hiding){transform:none}.offcanvas-xl.showing,.offcanvas-xl.hiding,.offcanvas-xl.show{visibility:visible}}@media(min-width:1200px){.offcanvas-xl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-xl .offcanvas-header{display:none}.offcanvas-xl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media(max-width:1399.98px){.offcanvas-xxl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;box-shadow:var(--bs-offcanvas-box-shadow);transition:transform .3s ease-in-out}}@media(max-width:1399.98px) and (prefers-reduced-motion:reduce){.offcanvas-xxl{transition:none}}@media(max-width:1399.98px){.offcanvas-xxl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xxl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xxl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xxl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xxl.showing,.offcanvas-xxl.show:not(.hiding){transform:none}.offcanvas-xxl.showing,.offcanvas-xxl.hiding,.offcanvas-xxl.show{visibility:visible}}@media(min-width:1400px){.offcanvas-xxl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-xxl .offcanvas-header{display:none}.offcanvas-xxl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}.offcanvas{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;box-shadow:var(--bs-offcanvas-box-shadow);transition:transform .3s ease-in-out}@media(prefers-reduced-motion:reduce){.offcanvas{transition:none}}.offcanvas.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas.showing,.offcanvas.show:not(.hiding){transform:none}.offcanvas.showing,.offcanvas.hiding,.offcanvas.show{visibility:visible}.offcanvas-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.offcanvas-backdrop.fade{opacity:0}.offcanvas-backdrop.show{opacity:.5}.offcanvas-header{display:flex;align-items:center;justify-content:space-between;padding:var(--bs-offcanvas-padding-y)var(--bs-offcanvas-padding-x)}.offcanvas-header .btn-close{padding:calc(var(--bs-offcanvas-padding-y) * .5);margin-top:calc(-.5 * var(--bs-offcanvas-padding-y));margin-right:calc(-.5 * var(--bs-offcanvas-padding-x));margin-bottom:calc(-.5 * var(--bs-offcanvas-padding-y))}.offcanvas-title{margin-bottom:0;line-height:1.5}.offcanvas-body{flex-grow:1;padding:var(--bs-offcanvas-padding-y)var(--bs-offcanvas-padding-x);overflow-y:auto}.placeholder{display:inline-block;min-height:1em;vertical-align:middle;cursor:wait;background-color:currentcolor;opacity:.5}.placeholder.btn::before,div.drawio button.placeholder::before,.td-blog .placeholder.td-rss-button::before{display:inline-block;content:""}.placeholder-xs{min-height:.6em}.placeholder-sm{min-height:.8em}.placeholder-lg{min-height:1.2em}.placeholder-glow .placeholder{animation:placeholder-glow 2s ease-in-out infinite}@keyframes placeholder-glow{50%{opacity:.2}}.placeholder-wave{-webkit-mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,.8) 75%,#000 95%);mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,.8) 75%,#000 95%);-webkit-mask-size:200% 100%;mask-size:200% 100%;animation:placeholder-wave 2s linear infinite}@keyframes placeholder-wave{100%{-webkit-mask-position:-200% 0%;mask-position:-200% 0%}}.clearfix::after{display:block;clear:both;content:""}.text-bg-primary{color:#fff!important;background-color:RGBA(95,118,129,var(--bs-bg-opacity,1))!important}.text-bg-secondary{color:#000!important;background-color:RGBA(245,131,1,var(--bs-bg-opacity,1))!important}.text-bg-success{color:#000!important;background-color:RGBA(55,114,255,var(--bs-bg-opacity,1))!important}.text-bg-info{color:#fff!important;background-color:RGBA(0,122,192,var(--bs-bg-opacity,1))!important}.text-bg-warning{color:#000!important;background-color:RGBA(227,48,48,var(--bs-bg-opacity,1))!important}.text-bg-danger{color:#000!important;background-color:RGBA(237,106,90,var(--bs-bg-opacity,1))!important}.text-bg-light{color:#000!important;background-color:RGBA(211,243,238,var(--bs-bg-opacity,1))!important}.text-bg-dark{color:#fff!important;background-color:RGBA(51,63,71,var(--bs-bg-opacity,1))!important}.link-primary{color:#5f7681!important}.link-primary:hover,.link-primary:focus{color:#43535a!important}.link-secondary{color:#f58301!important}.link-secondary:hover,.link-secondary:focus{color:#f8a84d!important}.link-success{color:#3772ff!important}.link-success:hover,.link-success:focus{color:#739cff!important}.link-info{color:#007ac0!important}.link-info:hover,.link-info:focus{color:#005586!important}.link-warning{color:#e33030!important}.link-warning:hover,.link-warning:focus{color:#eb6e6e!important}.link-danger{color:#ed6a5a!important}.link-danger:hover,.link-danger:focus{color:#f2978c!important}.link-light{color:#d3f3ee!important}.link-light:hover,.link-light:focus{color:#e0f7f3!important}.link-dark{color:#333f47!important}.link-dark:hover,.link-dark:focus{color:#242c32!important}.ratio{position:relative;width:100%}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.ratio-1x1{--bs-aspect-ratio:100%}.ratio-4x3{--bs-aspect-ratio:calc(3 / 4 * 100%)}.ratio-16x9{--bs-aspect-ratio:calc(9 / 16 * 100%)}.ratio-21x9{--bs-aspect-ratio:calc(9 / 21 * 100%)}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:sticky;top:0;z-index:1020}.sticky-bottom{position:sticky;bottom:0;z-index:1020}@media(min-width:576px){.sticky-sm-top{position:sticky;top:0;z-index:1020}.sticky-sm-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width:768px){.sticky-md-top{position:sticky;top:0;z-index:1020}.sticky-md-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width:992px){.sticky-lg-top{position:sticky;top:0;z-index:1020}.sticky-lg-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width:1200px){.sticky-xl-top{position:sticky;top:0;z-index:1020}.sticky-xl-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width:1400px){.sticky-xxl-top{position:sticky;top:0;z-index:1020}.sticky-xxl-bottom{position:sticky;bottom:0;z-index:1020}}.hstack{display:flex;flex-direction:row;align-items:center;align-self:stretch}.vstack{display:flex;flex:auto;flex-direction:column;align-self:stretch}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vr{display:inline-block;align-self:stretch;width:1px;min-height:1em;background-color:currentcolor;opacity:.25}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.float-start{float:left!important}.float-end{float:right!important}.float-none{float:none!important}.opacity-0{opacity:0!important}.opacity-25{opacity:.25!important}.opacity-50{opacity:.5!important}.opacity-75{opacity:.75!important}.opacity-100{opacity:1!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:sticky!important}.top-0{top:0!important}.top-50{top:50%!important}.top-100{top:100%!important}.bottom-0{bottom:0!important}.bottom-50{bottom:50%!important}.bottom-100{bottom:100%!important}.start-0{left:0!important}.start-50{left:50%!important}.start-100{left:100%!important}.end-0{right:0!important}.end-50{right:50%!important}.end-100{right:100%!important}.translate-middle{transform:translate(-50%,-50%)!important}.translate-middle-x{transform:translateX(-50%)!important}.translate-middle-y{transform:translateY(-50%)!important}.border{border:var(--bs-border-width)var(--bs-border-style)var(--bs-border-color)!important}.border-0{border:0!important}.border-top{border-top:var(--bs-border-width)var(--bs-border-style)var(--bs-border-color)!important}.border-top-0{border-top:0!important}.border-end{border-right:var(--bs-border-width)var(--bs-border-style)var(--bs-border-color)!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:var(--bs-border-width)var(--bs-border-style)var(--bs-border-color)!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:var(--bs-border-width)var(--bs-border-style)var(--bs-border-color)!important}.border-start-0{border-left:0!important}.border-primary{--bs-border-opacity:1;border-color:rgba(var(--bs-primary-rgb),var(--bs-border-opacity))!important}.border-secondary{--bs-border-opacity:1;border-color:rgba(var(--bs-secondary-rgb),var(--bs-border-opacity))!important}.border-success{--bs-border-opacity:1;border-color:rgba(var(--bs-success-rgb),var(--bs-border-opacity))!important}.border-info{--bs-border-opacity:1;border-color:rgba(var(--bs-info-rgb),var(--bs-border-opacity))!important}.border-warning{--bs-border-opacity:1;border-color:rgba(var(--bs-warning-rgb),var(--bs-border-opacity))!important}.border-danger{--bs-border-opacity:1;border-color:rgba(var(--bs-danger-rgb),var(--bs-border-opacity))!important}.border-light{--bs-border-opacity:1;border-color:rgba(var(--bs-light-rgb),var(--bs-border-opacity))!important}.border-dark{--bs-border-opacity:1;border-color:rgba(var(--bs-dark-rgb),var(--bs-border-opacity))!important}.border-white{--bs-border-opacity:1;border-color:rgba(var(--bs-white-rgb),var(--bs-border-opacity))!important}.border-1{--bs-border-width:1px}.border-2{--bs-border-width:2px}.border-3{--bs-border-width:3px}.border-4{--bs-border-width:4px}.border-5{--bs-border-width:5px}.border-opacity-10{--bs-border-opacity:0.1}.border-opacity-25{--bs-border-opacity:0.25}.border-opacity-50{--bs-border-opacity:0.5}.border-opacity-75{--bs-border-opacity:0.75}.border-opacity-100{--bs-border-opacity:1}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mh-100{max-height:100%!important}.vh-100{height:100vh!important}.min-vh-100{min-height:100vh!important}.flex-fill{flex:auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:3rem!important}.m-auto{margin:auto!important}.mx-0{margin-right:0!important;margin-left:0!important}.mx-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-3{margin-right:1rem!important;margin-left:1rem!important}.mx-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-5{margin-right:3rem!important;margin-left:3rem!important}.mx-auto{margin-right:auto!important;margin-left:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:3rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.25rem!important}.me-2{margin-right:.5rem!important}.me-3{margin-right:1rem!important}.me-4{margin-right:1.5rem!important}.me-5{margin-right:3rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:3rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.25rem!important}.ms-2{margin-left:.5rem!important}.ms-3{margin-left:1rem!important}.ms-4{margin-left:1.5rem!important}.ms-5{margin-left:3rem!important}.ms-auto{margin-left:auto!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:3rem!important}.px-0{padding-right:0!important;padding-left:0!important}.px-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-3{padding-right:1rem!important;padding-left:1rem!important}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-5{padding-right:3rem!important;padding-left:3rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:3rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.25rem!important}.pe-2{padding-right:.5rem!important}.pe-3{padding-right:1rem!important}.pe-4{padding-right:1.5rem!important}.pe-5{padding-right:3rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:3rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.25rem!important}.ps-2{padding-left:.5rem!important}.ps-3{padding-left:1rem!important}.ps-4{padding-left:1.5rem!important}.ps-5{padding-left:3rem!important}.gap-0{gap:0!important}.gap-1{gap:.25rem!important}.gap-2{gap:.5rem!important}.gap-3{gap:1rem!important}.gap-4{gap:1.5rem!important}.gap-5{gap:3rem!important}.font-monospace{font-family:var(--bs-font-monospace)!important}.fs-1{font-size:calc(1.375rem + 1.5vw)!important}.fs-2{font-size:calc(1.325rem + .9vw)!important}.fs-3{font-size:calc(1.275rem + .3vw)!important}.fs-4{font-size:calc(1.26rem + .12vw)!important}.fs-5{font-size:1.15rem!important}.fs-6{font-size:1rem!important}.fst-italic{font-style:italic!important}.fst-normal{font-style:normal!important}.fw-light{font-weight:300!important}.fw-lighter{font-weight:lighter!important}.fw-normal{font-weight:400!important}.fw-bold{font-weight:700!important}.fw-semibold{font-weight:600!important}.fw-bolder{font-weight:bolder!important}.lh-1{line-height:1!important}.lh-sm{line-height:1.25!important}.lh-base{line-height:1.5!important}.lh-lg{line-height:2!important}.text-start{text-align:left!important}.text-end{text-align:right!important}.text-center{text-align:center!important}.text-decoration-none{text-decoration:none!important}.text-decoration-underline{text-decoration:underline!important}.text-decoration-line-through{text-decoration:line-through!important}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-break{word-wrap:break-word!important;word-break:break-word!important}.text-primary{--bs-text-opacity:1;color:rgba(var(--bs-primary-rgb),var(--bs-text-opacity))!important}.text-secondary{--bs-text-opacity:1;color:rgba(var(--bs-secondary-rgb),var(--bs-text-opacity))!important}.text-success{--bs-text-opacity:1;color:rgba(var(--bs-success-rgb),var(--bs-text-opacity))!important}.text-info{--bs-text-opacity:1;color:rgba(var(--bs-info-rgb),var(--bs-text-opacity))!important}.text-warning{--bs-text-opacity:1;color:rgba(var(--bs-warning-rgb),var(--bs-text-opacity))!important}.text-danger{--bs-text-opacity:1;color:rgba(var(--bs-danger-rgb),var(--bs-text-opacity))!important}.text-light{--bs-text-opacity:1;color:rgba(var(--bs-light-rgb),var(--bs-text-opacity))!important}.text-dark{--bs-text-opacity:1;color:rgba(var(--bs-dark-rgb),var(--bs-text-opacity))!important}.text-black{--bs-text-opacity:1;color:rgba(var(--bs-black-rgb),var(--bs-text-opacity))!important}.text-white{--bs-text-opacity:1;color:rgba(var(--bs-white-rgb),var(--bs-text-opacity))!important}.text-body{--bs-text-opacity:1;color:rgba(var(--bs-body-color-rgb),var(--bs-text-opacity))!important}.text-muted{--bs-text-opacity:1;color:#6c757d!important}.text-black-50{--bs-text-opacity:1;color:rgba(0,0,0,.5)!important}.text-white-50{--bs-text-opacity:1;color:rgba(255,255,255,.5)!important}.text-reset{--bs-text-opacity:1;color:inherit!important}.text-opacity-25{--bs-text-opacity:0.25}.text-opacity-50{--bs-text-opacity:0.5}.text-opacity-75{--bs-text-opacity:0.75}.text-opacity-100{--bs-text-opacity:1}.bg-primary{--bs-bg-opacity:1;background-color:rgba(var(--bs-primary-rgb),var(--bs-bg-opacity))!important}.bg-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-rgb),var(--bs-bg-opacity))!important}.bg-success{--bs-bg-opacity:1;background-color:rgba(var(--bs-success-rgb),var(--bs-bg-opacity))!important}.bg-info{--bs-bg-opacity:1;background-color:rgba(var(--bs-info-rgb),var(--bs-bg-opacity))!important}.bg-warning{--bs-bg-opacity:1;background-color:rgba(var(--bs-warning-rgb),var(--bs-bg-opacity))!important}.bg-danger{--bs-bg-opacity:1;background-color:rgba(var(--bs-danger-rgb),var(--bs-bg-opacity))!important}.bg-light{--bs-bg-opacity:1;background-color:rgba(var(--bs-light-rgb),var(--bs-bg-opacity))!important}.bg-dark{--bs-bg-opacity:1;background-color:rgba(var(--bs-dark-rgb),var(--bs-bg-opacity))!important}.bg-black{--bs-bg-opacity:1;background-color:rgba(var(--bs-black-rgb),var(--bs-bg-opacity))!important}.bg-white{--bs-bg-opacity:1;background-color:rgba(var(--bs-white-rgb),var(--bs-bg-opacity))!important}.bg-body{--bs-bg-opacity:1;background-color:rgba(var(--bs-body-bg-rgb),var(--bs-bg-opacity))!important}.bg-transparent{--bs-bg-opacity:1;background-color:transparent!important}.bg-opacity-10{--bs-bg-opacity:0.1}.bg-opacity-25{--bs-bg-opacity:0.25}.bg-opacity-50{--bs-bg-opacity:0.5}.bg-opacity-75{--bs-bg-opacity:0.75}.bg-opacity-100{--bs-bg-opacity:1}.bg-gradient{background-image:var(--bs-gradient)!important}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}.pe-none{pointer-events:none!important}.pe-auto{pointer-events:auto!important}.rounded{border-radius:var(--bs-border-radius)!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:var(--bs-border-radius-sm)!important}.rounded-2{border-radius:var(--bs-border-radius)!important}.rounded-3{border-radius:var(--bs-border-radius-lg)!important}.rounded-4{border-radius:var(--bs-border-radius-xl)!important}.rounded-5{border-radius:var(--bs-border-radius-2xl)!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:var(--bs-border-radius-pill)!important}.rounded-top{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important}.rounded-end{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important}.rounded-bottom{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}.rounded-start{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media(min-width:576px){.float-sm-start{float:left!important}.float-sm-end{float:right!important}.float-sm-none{float:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:3rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-right:0!important;margin-left:0!important}.mx-sm-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-sm-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-sm-3{margin-right:1rem!important;margin-left:1rem!important}.mx-sm-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-sm-5{margin-right:3rem!important;margin-left:3rem!important}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-sm-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:3rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.25rem!important}.me-sm-2{margin-right:.5rem!important}.me-sm-3{margin-right:1rem!important}.me-sm-4{margin-right:1.5rem!important}.me-sm-5{margin-right:3rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:3rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.25rem!important}.ms-sm-2{margin-left:.5rem!important}.ms-sm-3{margin-left:1rem!important}.ms-sm-4{margin-left:1.5rem!important}.ms-sm-5{margin-left:3rem!important}.ms-sm-auto{margin-left:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:3rem!important}.px-sm-0{padding-right:0!important;padding-left:0!important}.px-sm-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-sm-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-sm-3{padding-right:1rem!important;padding-left:1rem!important}.px-sm-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-sm-5{padding-right:3rem!important;padding-left:3rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-sm-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:3rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.25rem!important}.pe-sm-2{padding-right:.5rem!important}.pe-sm-3{padding-right:1rem!important}.pe-sm-4{padding-right:1.5rem!important}.pe-sm-5{padding-right:3rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:3rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.25rem!important}.ps-sm-2{padding-left:.5rem!important}.ps-sm-3{padding-left:1rem!important}.ps-sm-4{padding-left:1.5rem!important}.ps-sm-5{padding-left:3rem!important}.gap-sm-0{gap:0!important}.gap-sm-1{gap:.25rem!important}.gap-sm-2{gap:.5rem!important}.gap-sm-3{gap:1rem!important}.gap-sm-4{gap:1.5rem!important}.gap-sm-5{gap:3rem!important}.text-sm-start{text-align:left!important}.text-sm-end{text-align:right!important}.text-sm-center{text-align:center!important}}@media(min-width:768px){.float-md-start{float:left!important}.float-md-end{float:right!important}.float-md-none{float:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:3rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-right:0!important;margin-left:0!important}.mx-md-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-md-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-md-3{margin-right:1rem!important;margin-left:1rem!important}.mx-md-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-md-5{margin-right:3rem!important;margin-left:3rem!important}.mx-md-auto{margin-right:auto!important;margin-left:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-md-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:3rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.25rem!important}.me-md-2{margin-right:.5rem!important}.me-md-3{margin-right:1rem!important}.me-md-4{margin-right:1.5rem!important}.me-md-5{margin-right:3rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:3rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.25rem!important}.ms-md-2{margin-left:.5rem!important}.ms-md-3{margin-left:1rem!important}.ms-md-4{margin-left:1.5rem!important}.ms-md-5{margin-left:3rem!important}.ms-md-auto{margin-left:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:3rem!important}.px-md-0{padding-right:0!important;padding-left:0!important}.px-md-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-md-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-md-3{padding-right:1rem!important;padding-left:1rem!important}.px-md-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-md-5{padding-right:3rem!important;padding-left:3rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-md-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:3rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.25rem!important}.pe-md-2{padding-right:.5rem!important}.pe-md-3{padding-right:1rem!important}.pe-md-4{padding-right:1.5rem!important}.pe-md-5{padding-right:3rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:3rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.25rem!important}.ps-md-2{padding-left:.5rem!important}.ps-md-3{padding-left:1rem!important}.ps-md-4{padding-left:1.5rem!important}.ps-md-5{padding-left:3rem!important}.gap-md-0{gap:0!important}.gap-md-1{gap:.25rem!important}.gap-md-2{gap:.5rem!important}.gap-md-3{gap:1rem!important}.gap-md-4{gap:1.5rem!important}.gap-md-5{gap:3rem!important}.text-md-start{text-align:left!important}.text-md-end{text-align:right!important}.text-md-center{text-align:center!important}}@media(min-width:992px){.float-lg-start{float:left!important}.float-lg-end{float:right!important}.float-lg-none{float:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block,.td-blog .td-rss-button{display:block!important}.d-lg-grid{display:grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:3rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-right:0!important;margin-left:0!important}.mx-lg-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-lg-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-lg-3{margin-right:1rem!important;margin-left:1rem!important}.mx-lg-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-lg-5{margin-right:3rem!important;margin-left:3rem!important}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-lg-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:3rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.25rem!important}.me-lg-2{margin-right:.5rem!important}.me-lg-3{margin-right:1rem!important}.me-lg-4{margin-right:1.5rem!important}.me-lg-5{margin-right:3rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:3rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.25rem!important}.ms-lg-2{margin-left:.5rem!important}.ms-lg-3{margin-left:1rem!important}.ms-lg-4{margin-left:1.5rem!important}.ms-lg-5{margin-left:3rem!important}.ms-lg-auto{margin-left:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:3rem!important}.px-lg-0{padding-right:0!important;padding-left:0!important}.px-lg-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-lg-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-lg-3{padding-right:1rem!important;padding-left:1rem!important}.px-lg-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-lg-5{padding-right:3rem!important;padding-left:3rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-lg-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:3rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.25rem!important}.pe-lg-2{padding-right:.5rem!important}.pe-lg-3{padding-right:1rem!important}.pe-lg-4{padding-right:1.5rem!important}.pe-lg-5{padding-right:3rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:3rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.25rem!important}.ps-lg-2{padding-left:.5rem!important}.ps-lg-3{padding-left:1rem!important}.ps-lg-4{padding-left:1.5rem!important}.ps-lg-5{padding-left:3rem!important}.gap-lg-0{gap:0!important}.gap-lg-1{gap:.25rem!important}.gap-lg-2{gap:.5rem!important}.gap-lg-3{gap:1rem!important}.gap-lg-4{gap:1.5rem!important}.gap-lg-5{gap:3rem!important}.text-lg-start{text-align:left!important}.text-lg-end{text-align:right!important}.text-lg-center{text-align:center!important}}@media(min-width:1200px){.float-xl-start{float:left!important}.float-xl-end{float:right!important}.float-xl-none{float:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:3rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-right:0!important;margin-left:0!important}.mx-xl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:3rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.25rem!important}.me-xl-2{margin-right:.5rem!important}.me-xl-3{margin-right:1rem!important}.me-xl-4{margin-right:1.5rem!important}.me-xl-5{margin-right:3rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:3rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.25rem!important}.ms-xl-2{margin-left:.5rem!important}.ms-xl-3{margin-left:1rem!important}.ms-xl-4{margin-left:1.5rem!important}.ms-xl-5{margin-left:3rem!important}.ms-xl-auto{margin-left:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:3rem!important}.px-xl-0{padding-right:0!important;padding-left:0!important}.px-xl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:3rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.25rem!important}.pe-xl-2{padding-right:.5rem!important}.pe-xl-3{padding-right:1rem!important}.pe-xl-4{padding-right:1.5rem!important}.pe-xl-5{padding-right:3rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:3rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.25rem!important}.ps-xl-2{padding-left:.5rem!important}.ps-xl-3{padding-left:1rem!important}.ps-xl-4{padding-left:1.5rem!important}.ps-xl-5{padding-left:3rem!important}.gap-xl-0{gap:0!important}.gap-xl-1{gap:.25rem!important}.gap-xl-2{gap:.5rem!important}.gap-xl-3{gap:1rem!important}.gap-xl-4{gap:1.5rem!important}.gap-xl-5{gap:3rem!important}.text-xl-start{text-align:left!important}.text-xl-end{text-align:right!important}.text-xl-center{text-align:center!important}}@media(min-width:1400px){.float-xxl-start{float:left!important}.float-xxl-end{float:right!important}.float-xxl-none{float:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:3rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-right:0!important;margin-left:0!important}.mx-xxl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xxl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xxl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xxl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xxl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xxl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:3rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.25rem!important}.me-xxl-2{margin-right:.5rem!important}.me-xxl-3{margin-right:1rem!important}.me-xxl-4{margin-right:1.5rem!important}.me-xxl-5{margin-right:3rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:3rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.25rem!important}.ms-xxl-2{margin-left:.5rem!important}.ms-xxl-3{margin-left:1rem!important}.ms-xxl-4{margin-left:1.5rem!important}.ms-xxl-5{margin-left:3rem!important}.ms-xxl-auto{margin-left:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:3rem!important}.px-xxl-0{padding-right:0!important;padding-left:0!important}.px-xxl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xxl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xxl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xxl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xxl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xxl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:3rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.25rem!important}.pe-xxl-2{padding-right:.5rem!important}.pe-xxl-3{padding-right:1rem!important}.pe-xxl-4{padding-right:1.5rem!important}.pe-xxl-5{padding-right:3rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:3rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.25rem!important}.ps-xxl-2{padding-left:.5rem!important}.ps-xxl-3{padding-left:1rem!important}.ps-xxl-4{padding-left:1.5rem!important}.ps-xxl-5{padding-left:3rem!important}.gap-xxl-0{gap:0!important}.gap-xxl-1{gap:.25rem!important}.gap-xxl-2{gap:.5rem!important}.gap-xxl-3{gap:1rem!important}.gap-xxl-4{gap:1.5rem!important}.gap-xxl-5{gap:3rem!important}.text-xxl-start{text-align:left!important}.text-xxl-end{text-align:right!important}.text-xxl-center{text-align:center!important}}@media(min-width:1200px){.fs-1{font-size:2.5rem!important}.fs-2{font-size:2rem!important}.fs-3{font-size:1.5rem!important}.fs-4{font-size:1.35rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}}/*!* Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com -* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) -* Copyright 2023 Fonticons, Inc.*/.fa,.td-search__icon:before{font-family:var(--fa-style-family,"Font Awesome 6 Free");font-weight:var(--fa-style,900)}.fa,.td-search__icon:before,.fa-classic,.fa-sharp,.fas,.td-offline-search-results__close-button:after,.fa-solid,.far,.fa-regular,.fab,.fa-brands{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:var(--fa-display,inline-block);font-style:normal;font-variant:normal;line-height:1;text-rendering:auto}.fas,.td-offline-search-results__close-button:after,.fa-classic,.fa-solid,.far,.fa-regular{font-family:'font awesome 6 free'}.fab,.fa-brands{font-family:'font awesome 6 brands'}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-2xs{font-size:.625em;line-height:.1em;vertical-align:.225em}.fa-xs{font-size:.75em;line-height:.08333333em;vertical-align:.125em}.fa-sm{font-size:.875em;line-height:.07142857em;vertical-align:.05357143em}.fa-lg{font-size:1.25em;line-height:.05em;vertical-align:-.075em}.fa-xl{font-size:1.5em;line-height:.04166667em;vertical-align:-.125em}.fa-2xl{font-size:2em;line-height:.03125em;vertical-align:-.1875em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:var(--fa-li-margin,2.5em);padding-left:0}.fa-ul>li{position:relative}.fa-li{left:calc(var(--fa-li-width,2em) * -1);position:absolute;text-align:center;width:var(--fa-li-width,2em);line-height:inherit}.fa-border{border-color:var(--fa-border-color,#eee);border-radius:var(--fa-border-radius,.1em);border-style:var(--fa-border-style,solid);border-width:var(--fa-border-width,.08em);padding:var(--fa-border-padding,.2em .25em .15em)}.fa-pull-left{float:left;margin-right:var(--fa-pull-margin,.3em)}.fa-pull-right{float:right;margin-left:var(--fa-pull-margin,.3em)}.fa-beat{animation-name:fa-beat;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-bounce{animation-name:fa-bounce;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1))}.fa-fade{animation-name:fa-fade;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-beat-fade{animation-name:fa-beat-fade;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-flip{animation-name:fa-flip;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-shake{animation-name:fa-shake;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,linear)}.fa-spin{animation-name:fa-spin;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,2s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,linear)}.fa-spin-reverse{--fa-animation-direction:reverse}.fa-pulse,.fa-spin-pulse{animation-name:fa-spin;animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,steps(8))}@media(prefers-reduced-motion:reduce){.fa-beat,.fa-bounce,.fa-fade,.fa-beat-fade,.fa-flip,.fa-pulse,.fa-shake,.fa-spin,.fa-spin-pulse{animation-delay:-1ms;animation-duration:1ms;animation-iteration-count:1;transition-delay:0s;transition-duration:0s}}@keyframes fa-beat{0%,90%{transform:scale(1)}45%{transform:scale(var(--fa-beat-scale,1.25))}}@keyframes fa-bounce{0%{transform:scale(1,1)translateY(0)}10%{transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9))translateY(0)}30%{transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1))translateY(var(--fa-bounce-height,-.5em))}50%{transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95))translateY(0)}57%{transform:scale(1,1)translateY(var(--fa-bounce-rebound,-.125em))}64%{transform:scale(1,1)translateY(0)}100%{transform:scale(1,1)translateY(0)}}@keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@keyframes fa-beat-fade{0%,100%{opacity:var(--fa-beat-fade-opacity,.4);transform:scale(1)}50%{opacity:1;transform:scale(var(--fa-beat-fade-scale,1.125))}}@keyframes fa-flip{50%{transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@keyframes fa-shake{0%{transform:rotate(-15deg)}4%{transform:rotate(15deg)}8%,24%{transform:rotate(-18deg)}12%,28%{transform:rotate(18deg)}16%{transform:rotate(-22deg)}20%{transform:rotate(22deg)}32%{transform:rotate(-12deg)}36%{transform:rotate(12deg)}40%,100%{transform:rotate(0)}}@keyframes fa-spin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}.fa-rotate-90{transform:rotate(90deg)}.fa-rotate-180{transform:rotate(180deg)}.fa-rotate-270{transform:rotate(270deg)}.fa-flip-horizontal{transform:scale(-1,1)}.fa-flip-vertical{transform:scale(1,-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{transform:scale(-1,-1)}.fa-rotate-by{transform:rotate(var(--fa-rotate-angle,none))}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%;z-index:var(--fa-stack-z-index,auto)}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:var(--fa-inverse,#fff)}.fa-0::before{content:"\30"}.fa-1::before{content:"\31"}.fa-2::before{content:"\32"}.fa-3::before{content:"\33"}.fa-4::before{content:"\34"}.fa-5::before{content:"\35"}.fa-6::before{content:"\36"}.fa-7::before{content:"\37"}.fa-8::before{content:"\38"}.fa-9::before{content:"\39"}.fa-fill-drip::before{content:"\f576"}.fa-arrows-to-circle::before{content:"\e4bd"}.fa-circle-chevron-right::before{content:"\f138"}.fa-chevron-circle-right::before{content:"\f138"}.fa-at::before{content:"\40"}.fa-trash-can::before{content:"\f2ed"}.fa-trash-alt::before{content:"\f2ed"}.fa-text-height::before{content:"\f034"}.fa-user-xmark::before{content:"\f235"}.fa-user-times::before{content:"\f235"}.fa-stethoscope::before{content:"\f0f1"}.fa-message::before{content:"\f27a"}.fa-comment-alt::before{content:"\f27a"}.fa-info::before{content:"\f129"}.fa-down-left-and-up-right-to-center::before{content:"\f422"}.fa-compress-alt::before{content:"\f422"}.fa-explosion::before{content:"\e4e9"}.fa-file-lines::before{content:"\f15c"}.fa-file-alt::before{content:"\f15c"}.fa-file-text::before{content:"\f15c"}.fa-wave-square::before{content:"\f83e"}.fa-ring::before{content:"\f70b"}.fa-building-un::before{content:"\e4d9"}.fa-dice-three::before{content:"\f527"}.fa-calendar-days::before{content:"\f073"}.fa-calendar-alt::before{content:"\f073"}.fa-anchor-circle-check::before{content:"\e4aa"}.fa-building-circle-arrow-right::before{content:"\e4d1"}.fa-volleyball::before{content:"\f45f"}.fa-volleyball-ball::before{content:"\f45f"}.fa-arrows-up-to-line::before{content:"\e4c2"}.fa-sort-down::before{content:"\f0dd"}.fa-sort-desc::before{content:"\f0dd"}.fa-circle-minus::before{content:"\f056"}.fa-minus-circle::before{content:"\f056"}.fa-door-open::before{content:"\f52b"}.fa-right-from-bracket::before{content:"\f2f5"}.fa-sign-out-alt::before{content:"\f2f5"}.fa-atom::before{content:"\f5d2"}.fa-soap::before{content:"\e06e"}.fa-icons::before{content:"\f86d"}.fa-heart-music-camera-bolt::before{content:"\f86d"}.fa-microphone-lines-slash::before{content:"\f539"}.fa-microphone-alt-slash::before{content:"\f539"}.fa-bridge-circle-check::before{content:"\e4c9"}.fa-pump-medical::before{content:"\e06a"}.fa-fingerprint::before{content:"\f577"}.fa-hand-point-right::before{content:"\f0a4"}.fa-magnifying-glass-location::before{content:"\f689"}.fa-search-location::before{content:"\f689"}.fa-forward-step::before{content:"\f051"}.fa-step-forward::before{content:"\f051"}.fa-face-smile-beam::before{content:"\f5b8"}.fa-smile-beam::before{content:"\f5b8"}.fa-flag-checkered::before{content:"\f11e"}.fa-football::before{content:"\f44e"}.fa-football-ball::before{content:"\f44e"}.fa-school-circle-exclamation::before{content:"\e56c"}.fa-crop::before{content:"\f125"}.fa-angles-down::before{content:"\f103"}.fa-angle-double-down::before{content:"\f103"}.fa-users-rectangle::before{content:"\e594"}.fa-people-roof::before{content:"\e537"}.fa-people-line::before{content:"\e534"}.fa-beer-mug-empty::before{content:"\f0fc"}.fa-beer::before{content:"\f0fc"}.fa-diagram-predecessor::before{content:"\e477"}.fa-arrow-up-long::before{content:"\f176"}.fa-long-arrow-up::before{content:"\f176"}.fa-fire-flame-simple::before{content:"\f46a"}.fa-burn::before{content:"\f46a"}.fa-person::before{content:"\f183"}.fa-male::before{content:"\f183"}.fa-laptop::before{content:"\f109"}.fa-file-csv::before{content:"\f6dd"}.fa-menorah::before{content:"\f676"}.fa-truck-plane::before{content:"\e58f"}.fa-record-vinyl::before{content:"\f8d9"}.fa-face-grin-stars::before{content:"\f587"}.fa-grin-stars::before{content:"\f587"}.fa-bong::before{content:"\f55c"}.fa-spaghetti-monster-flying::before{content:"\f67b"}.fa-pastafarianism::before{content:"\f67b"}.fa-arrow-down-up-across-line::before{content:"\e4af"}.fa-spoon::before{content:"\f2e5"}.fa-utensil-spoon::before{content:"\f2e5"}.fa-jar-wheat::before{content:"\e517"}.fa-envelopes-bulk::before{content:"\f674"}.fa-mail-bulk::before{content:"\f674"}.fa-file-circle-exclamation::before{content:"\e4eb"}.fa-circle-h::before{content:"\f47e"}.fa-hospital-symbol::before{content:"\f47e"}.fa-pager::before{content:"\f815"}.fa-address-book::before{content:"\f2b9"}.fa-contact-book::before{content:"\f2b9"}.fa-strikethrough::before{content:"\f0cc"}.fa-k::before{content:"\4b"}.fa-landmark-flag::before{content:"\e51c"}.fa-pencil::before{content:"\f303"}.fa-pencil-alt::before{content:"\f303"}.fa-backward::before{content:"\f04a"}.fa-caret-right::before{content:"\f0da"}.fa-comments::before{content:"\f086"}.fa-paste::before{content:"\f0ea"}.fa-file-clipboard::before{content:"\f0ea"}.fa-code-pull-request::before{content:"\e13c"}.fa-clipboard-list::before{content:"\f46d"}.fa-truck-ramp-box::before{content:"\f4de"}.fa-truck-loading::before{content:"\f4de"}.fa-user-check::before{content:"\f4fc"}.fa-vial-virus::before{content:"\e597"}.fa-sheet-plastic::before{content:"\e571"}.fa-blog::before{content:"\f781"}.fa-user-ninja::before{content:"\f504"}.fa-person-arrow-up-from-line::before{content:"\e539"}.fa-scroll-torah::before{content:"\f6a0"}.fa-torah::before{content:"\f6a0"}.fa-broom-ball::before{content:"\f458"}.fa-quidditch::before{content:"\f458"}.fa-quidditch-broom-ball::before{content:"\f458"}.fa-toggle-off::before{content:"\f204"}.fa-box-archive::before{content:"\f187"}.fa-archive::before{content:"\f187"}.fa-person-drowning::before{content:"\e545"}.fa-arrow-down-9-1::before{content:"\f886"}.fa-sort-numeric-desc::before{content:"\f886"}.fa-sort-numeric-down-alt::before{content:"\f886"}.fa-face-grin-tongue-squint::before{content:"\f58a"}.fa-grin-tongue-squint::before{content:"\f58a"}.fa-spray-can::before{content:"\f5bd"}.fa-truck-monster::before{content:"\f63b"}.fa-w::before{content:"\57"}.fa-earth-africa::before{content:"\f57c"}.fa-globe-africa::before{content:"\f57c"}.fa-rainbow::before{content:"\f75b"}.fa-circle-notch::before{content:"\f1ce"}.fa-tablet-screen-button::before{content:"\f3fa"}.fa-tablet-alt::before{content:"\f3fa"}.fa-paw::before{content:"\f1b0"}.fa-cloud::before{content:"\f0c2"}.fa-trowel-bricks::before{content:"\e58a"}.fa-face-flushed::before{content:"\f579"}.fa-flushed::before{content:"\f579"}.fa-hospital-user::before{content:"\f80d"}.fa-tent-arrow-left-right::before{content:"\e57f"}.fa-gavel::before{content:"\f0e3"}.fa-legal::before{content:"\f0e3"}.fa-binoculars::before{content:"\f1e5"}.fa-microphone-slash::before{content:"\f131"}.fa-box-tissue::before{content:"\e05b"}.fa-motorcycle::before{content:"\f21c"}.fa-bell-concierge::before{content:"\f562"}.fa-concierge-bell::before{content:"\f562"}.fa-pen-ruler::before{content:"\f5ae"}.fa-pencil-ruler::before{content:"\f5ae"}.fa-people-arrows::before{content:"\e068"}.fa-people-arrows-left-right::before{content:"\e068"}.fa-mars-and-venus-burst::before{content:"\e523"}.fa-square-caret-right::before{content:"\f152"}.fa-caret-square-right::before{content:"\f152"}.fa-scissors::before{content:"\f0c4"}.fa-cut::before{content:"\f0c4"}.fa-sun-plant-wilt::before{content:"\e57a"}.fa-toilets-portable::before{content:"\e584"}.fa-hockey-puck::before{content:"\f453"}.fa-table::before{content:"\f0ce"}.fa-magnifying-glass-arrow-right::before{content:"\e521"}.fa-tachograph-digital::before{content:"\f566"}.fa-digital-tachograph::before{content:"\f566"}.fa-users-slash::before{content:"\e073"}.fa-clover::before{content:"\e139"}.fa-reply::before{content:"\f3e5"}.fa-mail-reply::before{content:"\f3e5"}.fa-star-and-crescent::before{content:"\f699"}.fa-house-fire::before{content:"\e50c"}.fa-square-minus::before{content:"\f146"}.fa-minus-square::before{content:"\f146"}.fa-helicopter::before{content:"\f533"}.fa-compass::before{content:"\f14e"}.fa-square-caret-down::before{content:"\f150"}.fa-caret-square-down::before{content:"\f150"}.fa-file-circle-question::before{content:"\e4ef"}.fa-laptop-code::before{content:"\f5fc"}.fa-swatchbook::before{content:"\f5c3"}.fa-prescription-bottle::before{content:"\f485"}.fa-bars::before{content:"\f0c9"}.fa-navicon::before{content:"\f0c9"}.fa-people-group::before{content:"\e533"}.fa-hourglass-end::before{content:"\f253"}.fa-hourglass-3::before{content:"\f253"}.fa-heart-crack::before{content:"\f7a9"}.fa-heart-broken::before{content:"\f7a9"}.fa-square-up-right::before{content:"\f360"}.fa-external-link-square-alt::before{content:"\f360"}.fa-face-kiss-beam::before{content:"\f597"}.fa-kiss-beam::before{content:"\f597"}.fa-film::before{content:"\f008"}.fa-ruler-horizontal::before{content:"\f547"}.fa-people-robbery::before{content:"\e536"}.fa-lightbulb::before{content:"\f0eb"}.fa-caret-left::before{content:"\f0d9"}.fa-circle-exclamation::before{content:"\f06a"}.fa-exclamation-circle::before{content:"\f06a"}.fa-school-circle-xmark::before{content:"\e56d"}.fa-arrow-right-from-bracket::before{content:"\f08b"}.fa-sign-out::before{content:"\f08b"}.fa-circle-chevron-down::before{content:"\f13a"}.fa-chevron-circle-down::before{content:"\f13a"}.fa-unlock-keyhole::before{content:"\f13e"}.fa-unlock-alt::before{content:"\f13e"}.fa-cloud-showers-heavy::before{content:"\f740"}.fa-headphones-simple::before{content:"\f58f"}.fa-headphones-alt::before{content:"\f58f"}.fa-sitemap::before{content:"\f0e8"}.fa-circle-dollar-to-slot::before{content:"\f4b9"}.fa-donate::before{content:"\f4b9"}.fa-memory::before{content:"\f538"}.fa-road-spikes::before{content:"\e568"}.fa-fire-burner::before{content:"\e4f1"}.fa-flag::before{content:"\f024"}.fa-hanukiah::before{content:"\f6e6"}.fa-feather::before{content:"\f52d"}.fa-volume-low::before{content:"\f027"}.fa-volume-down::before{content:"\f027"}.fa-comment-slash::before{content:"\f4b3"}.fa-cloud-sun-rain::before{content:"\f743"}.fa-compress::before{content:"\f066"}.fa-wheat-awn::before{content:"\e2cd"}.fa-wheat-alt::before{content:"\e2cd"}.fa-ankh::before{content:"\f644"}.fa-hands-holding-child::before{content:"\e4fa"}.fa-asterisk::before{content:"\2a"}.fa-square-check::before{content:"\f14a"}.fa-check-square::before{content:"\f14a"}.fa-peseta-sign::before{content:"\e221"}.fa-heading::before{content:"\f1dc"}.fa-header::before{content:"\f1dc"}.fa-ghost::before{content:"\f6e2"}.fa-list::before{content:"\f03a"}.fa-list-squares::before{content:"\f03a"}.fa-square-phone-flip::before{content:"\f87b"}.fa-phone-square-alt::before{content:"\f87b"}.fa-cart-plus::before{content:"\f217"}.fa-gamepad::before{content:"\f11b"}.fa-circle-dot::before{content:"\f192"}.fa-dot-circle::before{content:"\f192"}.fa-face-dizzy::before{content:"\f567"}.fa-dizzy::before{content:"\f567"}.fa-egg::before{content:"\f7fb"}.fa-house-medical-circle-xmark::before{content:"\e513"}.fa-campground::before{content:"\f6bb"}.fa-folder-plus::before{content:"\f65e"}.fa-futbol::before{content:"\f1e3"}.fa-futbol-ball::before{content:"\f1e3"}.fa-soccer-ball::before{content:"\f1e3"}.fa-paintbrush::before{content:"\f1fc"}.fa-paint-brush::before{content:"\f1fc"}.fa-lock::before{content:"\f023"}.fa-gas-pump::before{content:"\f52f"}.fa-hot-tub-person::before{content:"\f593"}.fa-hot-tub::before{content:"\f593"}.fa-map-location::before{content:"\f59f"}.fa-map-marked::before{content:"\f59f"}.fa-house-flood-water::before{content:"\e50e"}.fa-tree::before{content:"\f1bb"}.fa-bridge-lock::before{content:"\e4cc"}.fa-sack-dollar::before{content:"\f81d"}.fa-pen-to-square::before{content:"\f044"}.fa-edit::before{content:"\f044"}.fa-car-side::before{content:"\f5e4"}.fa-share-nodes::before{content:"\f1e0"}.fa-share-alt::before{content:"\f1e0"}.fa-heart-circle-minus::before{content:"\e4ff"}.fa-hourglass-half::before{content:"\f252"}.fa-hourglass-2::before{content:"\f252"}.fa-microscope::before{content:"\f610"}.fa-sink::before{content:"\e06d"}.fa-bag-shopping::before{content:"\f290"}.fa-shopping-bag::before{content:"\f290"}.fa-arrow-down-z-a::before{content:"\f881"}.fa-sort-alpha-desc::before{content:"\f881"}.fa-sort-alpha-down-alt::before{content:"\f881"}.fa-mitten::before{content:"\f7b5"}.fa-person-rays::before{content:"\e54d"}.fa-users::before{content:"\f0c0"}.fa-eye-slash::before{content:"\f070"}.fa-flask-vial::before{content:"\e4f3"}.fa-hand::before{content:"\f256"}.fa-hand-paper::before{content:"\f256"}.fa-om::before{content:"\f679"}.fa-worm::before{content:"\e599"}.fa-house-circle-xmark::before{content:"\e50b"}.fa-plug::before{content:"\f1e6"}.fa-chevron-up::before{content:"\f077"}.fa-hand-spock::before{content:"\f259"}.fa-stopwatch::before{content:"\f2f2"}.fa-face-kiss::before{content:"\f596"}.fa-kiss::before{content:"\f596"}.fa-bridge-circle-xmark::before{content:"\e4cb"}.fa-face-grin-tongue::before{content:"\f589"}.fa-grin-tongue::before{content:"\f589"}.fa-chess-bishop::before{content:"\f43a"}.fa-face-grin-wink::before{content:"\f58c"}.fa-grin-wink::before{content:"\f58c"}.fa-ear-deaf::before{content:"\f2a4"}.fa-deaf::before{content:"\f2a4"}.fa-deafness::before{content:"\f2a4"}.fa-hard-of-hearing::before{content:"\f2a4"}.fa-road-circle-check::before{content:"\e564"}.fa-dice-five::before{content:"\f523"}.fa-square-rss::before{content:"\f143"}.fa-rss-square::before{content:"\f143"}.fa-land-mine-on::before{content:"\e51b"}.fa-i-cursor::before{content:"\f246"}.fa-stamp::before{content:"\f5bf"}.fa-stairs::before{content:"\e289"}.fa-i::before{content:"\49"}.fa-hryvnia-sign::before{content:"\f6f2"}.fa-hryvnia::before{content:"\f6f2"}.fa-pills::before{content:"\f484"}.fa-face-grin-wide::before{content:"\f581"}.fa-grin-alt::before{content:"\f581"}.fa-tooth::before{content:"\f5c9"}.fa-v::before{content:"\56"}.fa-bangladeshi-taka-sign::before{content:"\e2e6"}.fa-bicycle::before{content:"\f206"}.fa-staff-snake::before{content:"\e579"}.fa-rod-asclepius::before{content:"\e579"}.fa-rod-snake::before{content:"\e579"}.fa-staff-aesculapius::before{content:"\e579"}.fa-head-side-cough-slash::before{content:"\e062"}.fa-truck-medical::before{content:"\f0f9"}.fa-ambulance::before{content:"\f0f9"}.fa-wheat-awn-circle-exclamation::before{content:"\e598"}.fa-snowman::before{content:"\f7d0"}.fa-mortar-pestle::before{content:"\f5a7"}.fa-road-barrier::before{content:"\e562"}.fa-school::before{content:"\f549"}.fa-igloo::before{content:"\f7ae"}.fa-joint::before{content:"\f595"}.fa-angle-right::before{content:"\f105"}.fa-horse::before{content:"\f6f0"}.fa-q::before{content:"\51"}.fa-g::before{content:"\47"}.fa-notes-medical::before{content:"\f481"}.fa-temperature-half::before{content:"\f2c9"}.fa-temperature-2::before{content:"\f2c9"}.fa-thermometer-2::before{content:"\f2c9"}.fa-thermometer-half::before{content:"\f2c9"}.fa-dong-sign::before{content:"\e169"}.fa-capsules::before{content:"\f46b"}.fa-poo-storm::before{content:"\f75a"}.fa-poo-bolt::before{content:"\f75a"}.fa-face-frown-open::before{content:"\f57a"}.fa-frown-open::before{content:"\f57a"}.fa-hand-point-up::before{content:"\f0a6"}.fa-money-bill::before{content:"\f0d6"}.fa-bookmark::before{content:"\f02e"}.fa-align-justify::before{content:"\f039"}.fa-umbrella-beach::before{content:"\f5ca"}.fa-helmet-un::before{content:"\e503"}.fa-bullseye::before{content:"\f140"}.fa-bacon::before{content:"\f7e5"}.fa-hand-point-down::before{content:"\f0a7"}.fa-arrow-up-from-bracket::before{content:"\e09a"}.fa-folder::before{content:"\f07b"}.fa-folder-blank::before{content:"\f07b"}.fa-file-waveform::before{content:"\f478"}.fa-file-medical-alt::before{content:"\f478"}.fa-radiation::before{content:"\f7b9"}.fa-chart-simple::before{content:"\e473"}.fa-mars-stroke::before{content:"\f229"}.fa-vial::before{content:"\f492"}.fa-gauge::before{content:"\f624"}.fa-dashboard::before{content:"\f624"}.fa-gauge-med::before{content:"\f624"}.fa-tachometer-alt-average::before{content:"\f624"}.fa-wand-magic-sparkles::before{content:"\e2ca"}.fa-magic-wand-sparkles::before{content:"\e2ca"}.fa-e::before{content:"\45"}.fa-pen-clip::before{content:"\f305"}.fa-pen-alt::before{content:"\f305"}.fa-bridge-circle-exclamation::before{content:"\e4ca"}.fa-user::before{content:"\f007"}.fa-school-circle-check::before{content:"\e56b"}.fa-dumpster::before{content:"\f793"}.fa-van-shuttle::before{content:"\f5b6"}.fa-shuttle-van::before{content:"\f5b6"}.fa-building-user::before{content:"\e4da"}.fa-square-caret-left::before{content:"\f191"}.fa-caret-square-left::before{content:"\f191"}.fa-highlighter::before{content:"\f591"}.fa-key::before{content:"\f084"}.fa-bullhorn::before{content:"\f0a1"}.fa-globe::before{content:"\f0ac"}.fa-synagogue::before{content:"\f69b"}.fa-person-half-dress::before{content:"\e548"}.fa-road-bridge::before{content:"\e563"}.fa-location-arrow::before{content:"\f124"}.fa-c::before{content:"\43"}.fa-tablet-button::before{content:"\f10a"}.fa-building-lock::before{content:"\e4d6"}.fa-pizza-slice::before{content:"\f818"}.fa-money-bill-wave::before{content:"\f53a"}.fa-chart-area::before{content:"\f1fe"}.fa-area-chart::before{content:"\f1fe"}.fa-house-flag::before{content:"\e50d"}.fa-person-circle-minus::before{content:"\e540"}.fa-ban::before{content:"\f05e"}.fa-cancel::before{content:"\f05e"}.fa-camera-rotate::before{content:"\e0d8"}.fa-spray-can-sparkles::before{content:"\f5d0"}.fa-air-freshener::before{content:"\f5d0"}.fa-star::before{content:"\f005"}.fa-repeat::before{content:"\f363"}.fa-cross::before{content:"\f654"}.fa-box::before{content:"\f466"}.fa-venus-mars::before{content:"\f228"}.fa-arrow-pointer::before{content:"\f245"}.fa-mouse-pointer::before{content:"\f245"}.fa-maximize::before{content:"\f31e"}.fa-expand-arrows-alt::before{content:"\f31e"}.fa-charging-station::before{content:"\f5e7"}.fa-shapes::before{content:"\f61f"}.fa-triangle-circle-square::before{content:"\f61f"}.fa-shuffle::before{content:"\f074"}.fa-random::before{content:"\f074"}.fa-person-running::before{content:"\f70c"}.fa-running::before{content:"\f70c"}.fa-mobile-retro::before{content:"\e527"}.fa-grip-lines-vertical::before{content:"\f7a5"}.fa-spider::before{content:"\f717"}.fa-hands-bound::before{content:"\e4f9"}.fa-file-invoice-dollar::before{content:"\f571"}.fa-plane-circle-exclamation::before{content:"\e556"}.fa-x-ray::before{content:"\f497"}.fa-spell-check::before{content:"\f891"}.fa-slash::before{content:"\f715"}.fa-computer-mouse::before{content:"\f8cc"}.fa-mouse::before{content:"\f8cc"}.fa-arrow-right-to-bracket::before{content:"\f090"}.fa-sign-in::before{content:"\f090"}.fa-shop-slash::before{content:"\e070"}.fa-store-alt-slash::before{content:"\e070"}.fa-server::before{content:"\f233"}.fa-virus-covid-slash::before{content:"\e4a9"}.fa-shop-lock::before{content:"\e4a5"}.fa-hourglass-start::before{content:"\f251"}.fa-hourglass-1::before{content:"\f251"}.fa-blender-phone::before{content:"\f6b6"}.fa-building-wheat::before{content:"\e4db"}.fa-person-breastfeeding::before{content:"\e53a"}.fa-right-to-bracket::before{content:"\f2f6"}.fa-sign-in-alt::before{content:"\f2f6"}.fa-venus::before{content:"\f221"}.fa-passport::before{content:"\f5ab"}.fa-heart-pulse::before{content:"\f21e"}.fa-heartbeat::before{content:"\f21e"}.fa-people-carry-box::before{content:"\f4ce"}.fa-people-carry::before{content:"\f4ce"}.fa-temperature-high::before{content:"\f769"}.fa-microchip::before{content:"\f2db"}.fa-crown::before{content:"\f521"}.fa-weight-hanging::before{content:"\f5cd"}.fa-xmarks-lines::before{content:"\e59a"}.fa-file-prescription::before{content:"\f572"}.fa-weight-scale::before{content:"\f496"}.fa-weight::before{content:"\f496"}.fa-user-group::before{content:"\f500"}.fa-user-friends::before{content:"\f500"}.fa-arrow-up-a-z::before{content:"\f15e"}.fa-sort-alpha-up::before{content:"\f15e"}.fa-chess-knight::before{content:"\f441"}.fa-face-laugh-squint::before{content:"\f59b"}.fa-laugh-squint::before{content:"\f59b"}.fa-wheelchair::before{content:"\f193"}.fa-circle-arrow-up::before{content:"\f0aa"}.fa-arrow-circle-up::before{content:"\f0aa"}.fa-toggle-on::before{content:"\f205"}.fa-person-walking::before{content:"\f554"}.fa-walking::before{content:"\f554"}.fa-l::before{content:"\4c"}.fa-fire::before{content:"\f06d"}.fa-bed-pulse::before{content:"\f487"}.fa-procedures::before{content:"\f487"}.fa-shuttle-space::before{content:"\f197"}.fa-space-shuttle::before{content:"\f197"}.fa-face-laugh::before{content:"\f599"}.fa-laugh::before{content:"\f599"}.fa-folder-open::before{content:"\f07c"}.fa-heart-circle-plus::before{content:"\e500"}.fa-code-fork::before{content:"\e13b"}.fa-city::before{content:"\f64f"}.fa-microphone-lines::before{content:"\f3c9"}.fa-microphone-alt::before{content:"\f3c9"}.fa-pepper-hot::before{content:"\f816"}.fa-unlock::before{content:"\f09c"}.fa-colon-sign::before{content:"\e140"}.fa-headset::before{content:"\f590"}.fa-store-slash::before{content:"\e071"}.fa-road-circle-xmark::before{content:"\e566"}.fa-user-minus::before{content:"\f503"}.fa-mars-stroke-up::before{content:"\f22a"}.fa-mars-stroke-v::before{content:"\f22a"}.fa-champagne-glasses::before{content:"\f79f"}.fa-glass-cheers::before{content:"\f79f"}.fa-clipboard::before{content:"\f328"}.fa-house-circle-exclamation::before{content:"\e50a"}.fa-file-arrow-up::before{content:"\f574"}.fa-file-upload::before{content:"\f574"}.fa-wifi::before{content:"\f1eb"}.fa-wifi-3::before{content:"\f1eb"}.fa-wifi-strong::before{content:"\f1eb"}.fa-bath::before{content:"\f2cd"}.fa-bathtub::before{content:"\f2cd"}.fa-underline::before{content:"\f0cd"}.fa-user-pen::before{content:"\f4ff"}.fa-user-edit::before{content:"\f4ff"}.fa-signature::before{content:"\f5b7"}.fa-stroopwafel::before{content:"\f551"}.fa-bold::before{content:"\f032"}.fa-anchor-lock::before{content:"\e4ad"}.fa-building-ngo::before{content:"\e4d7"}.fa-manat-sign::before{content:"\e1d5"}.fa-not-equal::before{content:"\f53e"}.fa-border-top-left::before{content:"\f853"}.fa-border-style::before{content:"\f853"}.fa-map-location-dot::before{content:"\f5a0"}.fa-map-marked-alt::before{content:"\f5a0"}.fa-jedi::before{content:"\f669"}.fa-square-poll-vertical::before{content:"\f681"}.fa-poll::before{content:"\f681"}.fa-mug-hot::before{content:"\f7b6"}.fa-car-battery::before{content:"\f5df"}.fa-battery-car::before{content:"\f5df"}.fa-gift::before{content:"\f06b"}.fa-dice-two::before{content:"\f528"}.fa-chess-queen::before{content:"\f445"}.fa-glasses::before{content:"\f530"}.fa-chess-board::before{content:"\f43c"}.fa-building-circle-check::before{content:"\e4d2"}.fa-person-chalkboard::before{content:"\e53d"}.fa-mars-stroke-right::before{content:"\f22b"}.fa-mars-stroke-h::before{content:"\f22b"}.fa-hand-back-fist::before{content:"\f255"}.fa-hand-rock::before{content:"\f255"}.fa-square-caret-up::before{content:"\f151"}.fa-caret-square-up::before{content:"\f151"}.fa-cloud-showers-water::before{content:"\e4e4"}.fa-chart-bar::before{content:"\f080"}.fa-bar-chart::before{content:"\f080"}.fa-hands-bubbles::before{content:"\e05e"}.fa-hands-wash::before{content:"\e05e"}.fa-less-than-equal::before{content:"\f537"}.fa-train::before{content:"\f238"}.fa-eye-low-vision::before{content:"\f2a8"}.fa-low-vision::before{content:"\f2a8"}.fa-crow::before{content:"\f520"}.fa-sailboat::before{content:"\e445"}.fa-window-restore::before{content:"\f2d2"}.fa-square-plus::before{content:"\f0fe"}.fa-plus-square::before{content:"\f0fe"}.fa-torii-gate::before{content:"\f6a1"}.fa-frog::before{content:"\f52e"}.fa-bucket::before{content:"\e4cf"}.fa-image::before{content:"\f03e"}.fa-microphone::before{content:"\f130"}.fa-cow::before{content:"\f6c8"}.fa-caret-up::before{content:"\f0d8"}.fa-screwdriver::before{content:"\f54a"}.fa-folder-closed::before{content:"\e185"}.fa-house-tsunami::before{content:"\e515"}.fa-square-nfi::before{content:"\e576"}.fa-arrow-up-from-ground-water::before{content:"\e4b5"}.fa-martini-glass::before{content:"\f57b"}.fa-glass-martini-alt::before{content:"\f57b"}.fa-rotate-left::before{content:"\f2ea"}.fa-rotate-back::before{content:"\f2ea"}.fa-rotate-backward::before{content:"\f2ea"}.fa-undo-alt::before{content:"\f2ea"}.fa-table-columns::before{content:"\f0db"}.fa-columns::before{content:"\f0db"}.fa-lemon::before{content:"\f094"}.fa-head-side-mask::before{content:"\e063"}.fa-handshake::before{content:"\f2b5"}.fa-gem::before{content:"\f3a5"}.fa-dolly::before{content:"\f472"}.fa-dolly-box::before{content:"\f472"}.fa-smoking::before{content:"\f48d"}.fa-minimize::before{content:"\f78c"}.fa-compress-arrows-alt::before{content:"\f78c"}.fa-monument::before{content:"\f5a6"}.fa-snowplow::before{content:"\f7d2"}.fa-angles-right::before{content:"\f101"}.fa-angle-double-right::before{content:"\f101"}.fa-cannabis::before{content:"\f55f"}.fa-circle-play::before{content:"\f144"}.fa-play-circle::before{content:"\f144"}.fa-tablets::before{content:"\f490"}.fa-ethernet::before{content:"\f796"}.fa-euro-sign::before{content:"\f153"}.fa-eur::before{content:"\f153"}.fa-euro::before{content:"\f153"}.fa-chair::before{content:"\f6c0"}.fa-circle-check::before{content:"\f058"}.fa-check-circle::before{content:"\f058"}.fa-circle-stop::before{content:"\f28d"}.fa-stop-circle::before{content:"\f28d"}.fa-compass-drafting::before{content:"\f568"}.fa-drafting-compass::before{content:"\f568"}.fa-plate-wheat::before{content:"\e55a"}.fa-icicles::before{content:"\f7ad"}.fa-person-shelter::before{content:"\e54f"}.fa-neuter::before{content:"\f22c"}.fa-id-badge::before{content:"\f2c1"}.fa-marker::before{content:"\f5a1"}.fa-face-laugh-beam::before{content:"\f59a"}.fa-laugh-beam::before{content:"\f59a"}.fa-helicopter-symbol::before{content:"\e502"}.fa-universal-access::before{content:"\f29a"}.fa-circle-chevron-up::before{content:"\f139"}.fa-chevron-circle-up::before{content:"\f139"}.fa-lari-sign::before{content:"\e1c8"}.fa-volcano::before{content:"\f770"}.fa-person-walking-dashed-line-arrow-right::before{content:"\e553"}.fa-sterling-sign::before{content:"\f154"}.fa-gbp::before{content:"\f154"}.fa-pound-sign::before{content:"\f154"}.fa-viruses::before{content:"\e076"}.fa-square-person-confined::before{content:"\e577"}.fa-user-tie::before{content:"\f508"}.fa-arrow-down-long::before{content:"\f175"}.fa-long-arrow-down::before{content:"\f175"}.fa-tent-arrow-down-to-line::before{content:"\e57e"}.fa-certificate::before{content:"\f0a3"}.fa-reply-all::before{content:"\f122"}.fa-mail-reply-all::before{content:"\f122"}.fa-suitcase::before{content:"\f0f2"}.fa-person-skating::before{content:"\f7c5"}.fa-skating::before{content:"\f7c5"}.fa-filter-circle-dollar::before{content:"\f662"}.fa-funnel-dollar::before{content:"\f662"}.fa-camera-retro::before{content:"\f083"}.fa-circle-arrow-down::before{content:"\f0ab"}.fa-arrow-circle-down::before{content:"\f0ab"}.fa-file-import::before{content:"\f56f"}.fa-arrow-right-to-file::before{content:"\f56f"}.fa-square-arrow-up-right::before{content:"\f14c"}.fa-external-link-square::before{content:"\f14c"}.fa-box-open::before{content:"\f49e"}.fa-scroll::before{content:"\f70e"}.fa-spa::before{content:"\f5bb"}.fa-location-pin-lock::before{content:"\e51f"}.fa-pause::before{content:"\f04c"}.fa-hill-avalanche::before{content:"\e507"}.fa-temperature-empty::before{content:"\f2cb"}.fa-temperature-0::before{content:"\f2cb"}.fa-thermometer-0::before{content:"\f2cb"}.fa-thermometer-empty::before{content:"\f2cb"}.fa-bomb::before{content:"\f1e2"}.fa-registered::before{content:"\f25d"}.fa-address-card::before{content:"\f2bb"}.fa-contact-card::before{content:"\f2bb"}.fa-vcard::before{content:"\f2bb"}.fa-scale-unbalanced-flip::before{content:"\f516"}.fa-balance-scale-right::before{content:"\f516"}.fa-subscript::before{content:"\f12c"}.fa-diamond-turn-right::before{content:"\f5eb"}.fa-directions::before{content:"\f5eb"}.fa-burst::before{content:"\e4dc"}.fa-house-laptop::before{content:"\e066"}.fa-laptop-house::before{content:"\e066"}.fa-face-tired::before{content:"\f5c8"}.fa-tired::before{content:"\f5c8"}.fa-money-bills::before{content:"\e1f3"}.fa-smog::before{content:"\f75f"}.fa-crutch::before{content:"\f7f7"}.fa-cloud-arrow-up::before{content:"\f0ee"}.fa-cloud-upload::before{content:"\f0ee"}.fa-cloud-upload-alt::before{content:"\f0ee"}.fa-palette::before{content:"\f53f"}.fa-arrows-turn-right::before{content:"\e4c0"}.fa-vest::before{content:"\e085"}.fa-ferry::before{content:"\e4ea"}.fa-arrows-down-to-people::before{content:"\e4b9"}.fa-seedling::before{content:"\f4d8"}.fa-sprout::before{content:"\f4d8"}.fa-left-right::before{content:"\f337"}.fa-arrows-alt-h::before{content:"\f337"}.fa-boxes-packing::before{content:"\e4c7"}.fa-circle-arrow-left::before{content:"\f0a8"}.fa-arrow-circle-left::before{content:"\f0a8"}.fa-group-arrows-rotate::before{content:"\e4f6"}.fa-bowl-food::before{content:"\e4c6"}.fa-candy-cane::before{content:"\f786"}.fa-arrow-down-wide-short::before{content:"\f160"}.fa-sort-amount-asc::before{content:"\f160"}.fa-sort-amount-down::before{content:"\f160"}.fa-cloud-bolt::before{content:"\f76c"}.fa-thunderstorm::before{content:"\f76c"}.fa-text-slash::before{content:"\f87d"}.fa-remove-format::before{content:"\f87d"}.fa-face-smile-wink::before{content:"\f4da"}.fa-smile-wink::before{content:"\f4da"}.fa-file-word::before{content:"\f1c2"}.fa-file-powerpoint::before{content:"\f1c4"}.fa-arrows-left-right::before{content:"\f07e"}.fa-arrows-h::before{content:"\f07e"}.fa-house-lock::before{content:"\e510"}.fa-cloud-arrow-down::before{content:"\f0ed"}.fa-cloud-download::before{content:"\f0ed"}.fa-cloud-download-alt::before{content:"\f0ed"}.fa-children::before{content:"\e4e1"}.fa-chalkboard::before{content:"\f51b"}.fa-blackboard::before{content:"\f51b"}.fa-user-large-slash::before{content:"\f4fa"}.fa-user-alt-slash::before{content:"\f4fa"}.fa-envelope-open::before{content:"\f2b6"}.fa-handshake-simple-slash::before{content:"\e05f"}.fa-handshake-alt-slash::before{content:"\e05f"}.fa-mattress-pillow::before{content:"\e525"}.fa-guarani-sign::before{content:"\e19a"}.fa-arrows-rotate::before{content:"\f021"}.fa-refresh::before{content:"\f021"}.fa-sync::before{content:"\f021"}.fa-fire-extinguisher::before{content:"\f134"}.fa-cruzeiro-sign::before{content:"\e152"}.fa-greater-than-equal::before{content:"\f532"}.fa-shield-halved::before{content:"\f3ed"}.fa-shield-alt::before{content:"\f3ed"}.fa-book-atlas::before{content:"\f558"}.fa-atlas::before{content:"\f558"}.fa-virus::before{content:"\e074"}.fa-envelope-circle-check::before{content:"\e4e8"}.fa-layer-group::before{content:"\f5fd"}.fa-arrows-to-dot::before{content:"\e4be"}.fa-archway::before{content:"\f557"}.fa-heart-circle-check::before{content:"\e4fd"}.fa-house-chimney-crack::before{content:"\f6f1"}.fa-house-damage::before{content:"\f6f1"}.fa-file-zipper::before{content:"\f1c6"}.fa-file-archive::before{content:"\f1c6"}.fa-square::before{content:"\f0c8"}.fa-martini-glass-empty::before{content:"\f000"}.fa-glass-martini::before{content:"\f000"}.fa-couch::before{content:"\f4b8"}.fa-cedi-sign::before{content:"\e0df"}.fa-italic::before{content:"\f033"}.fa-church::before{content:"\f51d"}.fa-comments-dollar::before{content:"\f653"}.fa-democrat::before{content:"\f747"}.fa-z::before{content:"\5a"}.fa-person-skiing::before{content:"\f7c9"}.fa-skiing::before{content:"\f7c9"}.fa-road-lock::before{content:"\e567"}.fa-a::before{content:"\41"}.fa-temperature-arrow-down::before{content:"\e03f"}.fa-temperature-down::before{content:"\e03f"}.fa-feather-pointed::before{content:"\f56b"}.fa-feather-alt::before{content:"\f56b"}.fa-p::before{content:"\50"}.fa-snowflake::before{content:"\f2dc"}.fa-newspaper::before{content:"\f1ea"}.fa-rectangle-ad::before{content:"\f641"}.fa-ad::before{content:"\f641"}.fa-circle-arrow-right::before{content:"\f0a9"}.fa-arrow-circle-right::before{content:"\f0a9"}.fa-filter-circle-xmark::before{content:"\e17b"}.fa-locust::before{content:"\e520"}.fa-sort::before{content:"\f0dc"}.fa-unsorted::before{content:"\f0dc"}.fa-list-ol::before{content:"\f0cb"}.fa-list-1-2::before{content:"\f0cb"}.fa-list-numeric::before{content:"\f0cb"}.fa-person-dress-burst::before{content:"\e544"}.fa-money-check-dollar::before{content:"\f53d"}.fa-money-check-alt::before{content:"\f53d"}.fa-vector-square::before{content:"\f5cb"}.fa-bread-slice::before{content:"\f7ec"}.fa-language::before{content:"\f1ab"}.fa-face-kiss-wink-heart::before{content:"\f598"}.fa-kiss-wink-heart::before{content:"\f598"}.fa-filter::before{content:"\f0b0"}.fa-question::before{content:"\3f"}.fa-file-signature::before{content:"\f573"}.fa-up-down-left-right::before{content:"\f0b2"}.fa-arrows-alt::before{content:"\f0b2"}.fa-house-chimney-user::before{content:"\e065"}.fa-hand-holding-heart::before{content:"\f4be"}.fa-puzzle-piece::before{content:"\f12e"}.fa-money-check::before{content:"\f53c"}.fa-star-half-stroke::before{content:"\f5c0"}.fa-star-half-alt::before{content:"\f5c0"}.fa-code::before{content:"\f121"}.fa-whiskey-glass::before{content:"\f7a0"}.fa-glass-whiskey::before{content:"\f7a0"}.fa-building-circle-exclamation::before{content:"\e4d3"}.fa-magnifying-glass-chart::before{content:"\e522"}.fa-arrow-up-right-from-square::before{content:"\f08e"}.fa-external-link::before{content:"\f08e"}.fa-cubes-stacked::before{content:"\e4e6"}.fa-won-sign::before{content:"\f159"}.fa-krw::before{content:"\f159"}.fa-won::before{content:"\f159"}.fa-virus-covid::before{content:"\e4a8"}.fa-austral-sign::before{content:"\e0a9"}.fa-f::before{content:"\46"}.fa-leaf::before{content:"\f06c"}.fa-road::before{content:"\f018"}.fa-taxi::before{content:"\f1ba"}.fa-cab::before{content:"\f1ba"}.fa-person-circle-plus::before{content:"\e541"}.fa-chart-pie::before{content:"\f200"}.fa-pie-chart::before{content:"\f200"}.fa-bolt-lightning::before{content:"\e0b7"}.fa-sack-xmark::before{content:"\e56a"}.fa-file-excel::before{content:"\f1c3"}.fa-file-contract::before{content:"\f56c"}.fa-fish-fins::before{content:"\e4f2"}.fa-building-flag::before{content:"\e4d5"}.fa-face-grin-beam::before{content:"\f582"}.fa-grin-beam::before{content:"\f582"}.fa-object-ungroup::before{content:"\f248"}.fa-poop::before{content:"\f619"}.fa-location-pin::before{content:"\f041"}.fa-map-marker::before{content:"\f041"}.fa-kaaba::before{content:"\f66b"}.fa-toilet-paper::before{content:"\f71e"}.fa-helmet-safety::before{content:"\f807"}.fa-hard-hat::before{content:"\f807"}.fa-hat-hard::before{content:"\f807"}.fa-eject::before{content:"\f052"}.fa-circle-right::before{content:"\f35a"}.fa-arrow-alt-circle-right::before{content:"\f35a"}.fa-plane-circle-check::before{content:"\e555"}.fa-face-rolling-eyes::before{content:"\f5a5"}.fa-meh-rolling-eyes::before{content:"\f5a5"}.fa-object-group::before{content:"\f247"}.fa-chart-line::before{content:"\f201"}.fa-line-chart::before{content:"\f201"}.fa-mask-ventilator::before{content:"\e524"}.fa-arrow-right::before{content:"\f061"}.fa-signs-post::before{content:"\f277"}.fa-map-signs::before{content:"\f277"}.fa-cash-register::before{content:"\f788"}.fa-person-circle-question::before{content:"\e542"}.fa-h::before{content:"\48"}.fa-tarp::before{content:"\e57b"}.fa-screwdriver-wrench::before{content:"\f7d9"}.fa-tools::before{content:"\f7d9"}.fa-arrows-to-eye::before{content:"\e4bf"}.fa-plug-circle-bolt::before{content:"\e55b"}.fa-heart::before{content:"\f004"}.fa-mars-and-venus::before{content:"\f224"}.fa-house-user::before{content:"\e1b0"}.fa-home-user::before{content:"\e1b0"}.fa-dumpster-fire::before{content:"\f794"}.fa-house-crack::before{content:"\e3b1"}.fa-martini-glass-citrus::before{content:"\f561"}.fa-cocktail::before{content:"\f561"}.fa-face-surprise::before{content:"\f5c2"}.fa-surprise::before{content:"\f5c2"}.fa-bottle-water::before{content:"\e4c5"}.fa-circle-pause::before{content:"\f28b"}.fa-pause-circle::before{content:"\f28b"}.fa-toilet-paper-slash::before{content:"\e072"}.fa-apple-whole::before{content:"\f5d1"}.fa-apple-alt::before{content:"\f5d1"}.fa-kitchen-set::before{content:"\e51a"}.fa-r::before{content:"\52"}.fa-temperature-quarter::before{content:"\f2ca"}.fa-temperature-1::before{content:"\f2ca"}.fa-thermometer-1::before{content:"\f2ca"}.fa-thermometer-quarter::before{content:"\f2ca"}.fa-cube::before{content:"\f1b2"}.fa-bitcoin-sign::before{content:"\e0b4"}.fa-shield-dog::before{content:"\e573"}.fa-solar-panel::before{content:"\f5ba"}.fa-lock-open::before{content:"\f3c1"}.fa-elevator::before{content:"\e16d"}.fa-money-bill-transfer::before{content:"\e528"}.fa-money-bill-trend-up::before{content:"\e529"}.fa-house-flood-water-circle-arrow-right::before{content:"\e50f"}.fa-square-poll-horizontal::before{content:"\f682"}.fa-poll-h::before{content:"\f682"}.fa-circle::before{content:"\f111"}.fa-backward-fast::before{content:"\f049"}.fa-fast-backward::before{content:"\f049"}.fa-recycle::before{content:"\f1b8"}.fa-user-astronaut::before{content:"\f4fb"}.fa-plane-slash::before{content:"\e069"}.fa-trademark::before{content:"\f25c"}.fa-basketball::before{content:"\f434"}.fa-basketball-ball::before{content:"\f434"}.fa-satellite-dish::before{content:"\f7c0"}.fa-circle-up::before{content:"\f35b"}.fa-arrow-alt-circle-up::before{content:"\f35b"}.fa-mobile-screen-button::before{content:"\f3cd"}.fa-mobile-alt::before{content:"\f3cd"}.fa-volume-high::before{content:"\f028"}.fa-volume-up::before{content:"\f028"}.fa-users-rays::before{content:"\e593"}.fa-wallet::before{content:"\f555"}.fa-clipboard-check::before{content:"\f46c"}.fa-file-audio::before{content:"\f1c7"}.fa-burger::before{content:"\f805"}.fa-hamburger::before{content:"\f805"}.fa-wrench::before{content:"\f0ad"}.fa-bugs::before{content:"\e4d0"}.fa-rupee-sign::before{content:"\f156"}.fa-rupee::before{content:"\f156"}.fa-file-image::before{content:"\f1c5"}.fa-circle-question::before{content:"\f059"}.fa-question-circle::before{content:"\f059"}.fa-plane-departure::before{content:"\f5b0"}.fa-handshake-slash::before{content:"\e060"}.fa-book-bookmark::before{content:"\e0bb"}.fa-code-branch::before{content:"\f126"}.fa-hat-cowboy::before{content:"\f8c0"}.fa-bridge::before{content:"\e4c8"}.fa-phone-flip::before{content:"\f879"}.fa-phone-alt::before{content:"\f879"}.fa-truck-front::before{content:"\e2b7"}.fa-cat::before{content:"\f6be"}.fa-anchor-circle-exclamation::before{content:"\e4ab"}.fa-truck-field::before{content:"\e58d"}.fa-route::before{content:"\f4d7"}.fa-clipboard-question::before{content:"\e4e3"}.fa-panorama::before{content:"\e209"}.fa-comment-medical::before{content:"\f7f5"}.fa-teeth-open::before{content:"\f62f"}.fa-file-circle-minus::before{content:"\e4ed"}.fa-tags::before{content:"\f02c"}.fa-wine-glass::before{content:"\f4e3"}.fa-forward-fast::before{content:"\f050"}.fa-fast-forward::before{content:"\f050"}.fa-face-meh-blank::before{content:"\f5a4"}.fa-meh-blank::before{content:"\f5a4"}.fa-square-parking::before{content:"\f540"}.fa-parking::before{content:"\f540"}.fa-house-signal::before{content:"\e012"}.fa-bars-progress::before{content:"\f828"}.fa-tasks-alt::before{content:"\f828"}.fa-faucet-drip::before{content:"\e006"}.fa-cart-flatbed::before{content:"\f474"}.fa-dolly-flatbed::before{content:"\f474"}.fa-ban-smoking::before{content:"\f54d"}.fa-smoking-ban::before{content:"\f54d"}.fa-terminal::before{content:"\f120"}.fa-mobile-button::before{content:"\f10b"}.fa-house-medical-flag::before{content:"\e514"}.fa-basket-shopping::before{content:"\f291"}.fa-shopping-basket::before{content:"\f291"}.fa-tape::before{content:"\f4db"}.fa-bus-simple::before{content:"\f55e"}.fa-bus-alt::before{content:"\f55e"}.fa-eye::before{content:"\f06e"}.fa-face-sad-cry::before{content:"\f5b3"}.fa-sad-cry::before{content:"\f5b3"}.fa-audio-description::before{content:"\f29e"}.fa-person-military-to-person::before{content:"\e54c"}.fa-file-shield::before{content:"\e4f0"}.fa-user-slash::before{content:"\f506"}.fa-pen::before{content:"\f304"}.fa-tower-observation::before{content:"\e586"}.fa-file-code::before{content:"\f1c9"}.fa-signal::before{content:"\f012"}.fa-signal-5::before{content:"\f012"}.fa-signal-perfect::before{content:"\f012"}.fa-bus::before{content:"\f207"}.fa-heart-circle-xmark::before{content:"\e501"}.fa-house-chimney::before{content:"\e3af"}.fa-home-lg::before{content:"\e3af"}.fa-window-maximize::before{content:"\f2d0"}.fa-face-frown::before{content:"\f119"}.fa-frown::before{content:"\f119"}.fa-prescription::before{content:"\f5b1"}.fa-shop::before{content:"\f54f"}.fa-store-alt::before{content:"\f54f"}.fa-floppy-disk::before{content:"\f0c7"}.fa-save::before{content:"\f0c7"}.fa-vihara::before{content:"\f6a7"}.fa-scale-unbalanced::before{content:"\f515"}.fa-balance-scale-left::before{content:"\f515"}.fa-sort-up::before{content:"\f0de"}.fa-sort-asc::before{content:"\f0de"}.fa-comment-dots::before{content:"\f4ad"}.fa-commenting::before{content:"\f4ad"}.fa-plant-wilt::before{content:"\e5aa"}.fa-diamond::before{content:"\f219"}.fa-face-grin-squint::before{content:"\f585"}.fa-grin-squint::before{content:"\f585"}.fa-hand-holding-dollar::before{content:"\f4c0"}.fa-hand-holding-usd::before{content:"\f4c0"}.fa-bacterium::before{content:"\e05a"}.fa-hand-pointer::before{content:"\f25a"}.fa-drum-steelpan::before{content:"\f56a"}.fa-hand-scissors::before{content:"\f257"}.fa-hands-praying::before{content:"\f684"}.fa-praying-hands::before{content:"\f684"}.fa-arrow-rotate-right::before{content:"\f01e"}.fa-arrow-right-rotate::before{content:"\f01e"}.fa-arrow-rotate-forward::before{content:"\f01e"}.fa-redo::before{content:"\f01e"}.fa-biohazard::before{content:"\f780"}.fa-location-crosshairs::before{content:"\f601"}.fa-location::before{content:"\f601"}.fa-mars-double::before{content:"\f227"}.fa-child-dress::before{content:"\e59c"}.fa-users-between-lines::before{content:"\e591"}.fa-lungs-virus::before{content:"\e067"}.fa-face-grin-tears::before{content:"\f588"}.fa-grin-tears::before{content:"\f588"}.fa-phone::before{content:"\f095"}.fa-calendar-xmark::before{content:"\f273"}.fa-calendar-times::before{content:"\f273"}.fa-child-reaching::before{content:"\e59d"}.fa-head-side-virus::before{content:"\e064"}.fa-user-gear::before{content:"\f4fe"}.fa-user-cog::before{content:"\f4fe"}.fa-arrow-up-1-9::before{content:"\f163"}.fa-sort-numeric-up::before{content:"\f163"}.fa-door-closed::before{content:"\f52a"}.fa-shield-virus::before{content:"\e06c"}.fa-dice-six::before{content:"\f526"}.fa-mosquito-net::before{content:"\e52c"}.fa-bridge-water::before{content:"\e4ce"}.fa-person-booth::before{content:"\f756"}.fa-text-width::before{content:"\f035"}.fa-hat-wizard::before{content:"\f6e8"}.fa-pen-fancy::before{content:"\f5ac"}.fa-person-digging::before{content:"\f85e"}.fa-digging::before{content:"\f85e"}.fa-trash::before{content:"\f1f8"}.fa-gauge-simple::before{content:"\f629"}.fa-gauge-simple-med::before{content:"\f629"}.fa-tachometer-average::before{content:"\f629"}.fa-book-medical::before{content:"\f7e6"}.fa-poo::before{content:"\f2fe"}.fa-quote-right::before{content:"\f10e"}.fa-quote-right-alt::before{content:"\f10e"}.fa-shirt::before{content:"\f553"}.fa-t-shirt::before{content:"\f553"}.fa-tshirt::before{content:"\f553"}.fa-cubes::before{content:"\f1b3"}.fa-divide::before{content:"\f529"}.fa-tenge-sign::before{content:"\f7d7"}.fa-tenge::before{content:"\f7d7"}.fa-headphones::before{content:"\f025"}.fa-hands-holding::before{content:"\f4c2"}.fa-hands-clapping::before{content:"\e1a8"}.fa-republican::before{content:"\f75e"}.fa-arrow-left::before{content:"\f060"}.fa-person-circle-xmark::before{content:"\e543"}.fa-ruler::before{content:"\f545"}.fa-align-left::before{content:"\f036"}.fa-dice-d6::before{content:"\f6d1"}.fa-restroom::before{content:"\f7bd"}.fa-j::before{content:"\4a"}.fa-users-viewfinder::before{content:"\e595"}.fa-file-video::before{content:"\f1c8"}.fa-up-right-from-square::before{content:"\f35d"}.fa-external-link-alt::before{content:"\f35d"}.fa-table-cells::before{content:"\f00a"}.fa-th::before{content:"\f00a"}.fa-file-pdf::before{content:"\f1c1"}.fa-book-bible::before{content:"\f647"}.fa-bible::before{content:"\f647"}.fa-o::before{content:"\4f"}.fa-suitcase-medical::before{content:"\f0fa"}.fa-medkit::before{content:"\f0fa"}.fa-user-secret::before{content:"\f21b"}.fa-otter::before{content:"\f700"}.fa-person-dress::before{content:"\f182"}.fa-female::before{content:"\f182"}.fa-comment-dollar::before{content:"\f651"}.fa-business-time::before{content:"\f64a"}.fa-briefcase-clock::before{content:"\f64a"}.fa-table-cells-large::before{content:"\f009"}.fa-th-large::before{content:"\f009"}.fa-book-tanakh::before{content:"\f827"}.fa-tanakh::before{content:"\f827"}.fa-phone-volume::before{content:"\f2a0"}.fa-volume-control-phone::before{content:"\f2a0"}.fa-hat-cowboy-side::before{content:"\f8c1"}.fa-clipboard-user::before{content:"\f7f3"}.fa-child::before{content:"\f1ae"}.fa-lira-sign::before{content:"\f195"}.fa-satellite::before{content:"\f7bf"}.fa-plane-lock::before{content:"\e558"}.fa-tag::before{content:"\f02b"}.fa-comment::before{content:"\f075"}.fa-cake-candles::before{content:"\f1fd"}.fa-birthday-cake::before{content:"\f1fd"}.fa-cake::before{content:"\f1fd"}.fa-envelope::before{content:"\f0e0"}.fa-angles-up::before{content:"\f102"}.fa-angle-double-up::before{content:"\f102"}.fa-paperclip::before{content:"\f0c6"}.fa-arrow-right-to-city::before{content:"\e4b3"}.fa-ribbon::before{content:"\f4d6"}.fa-lungs::before{content:"\f604"}.fa-arrow-up-9-1::before{content:"\f887"}.fa-sort-numeric-up-alt::before{content:"\f887"}.fa-litecoin-sign::before{content:"\e1d3"}.fa-border-none::before{content:"\f850"}.fa-circle-nodes::before{content:"\e4e2"}.fa-parachute-box::before{content:"\f4cd"}.fa-indent::before{content:"\f03c"}.fa-truck-field-un::before{content:"\e58e"}.fa-hourglass::before{content:"\f254"}.fa-hourglass-empty::before{content:"\f254"}.fa-mountain::before{content:"\f6fc"}.fa-user-doctor::before{content:"\f0f0"}.fa-user-md::before{content:"\f0f0"}.fa-circle-info::before{content:"\f05a"}.fa-info-circle::before{content:"\f05a"}.fa-cloud-meatball::before{content:"\f73b"}.fa-camera::before{content:"\f030"}.fa-camera-alt::before{content:"\f030"}.fa-square-virus::before{content:"\e578"}.fa-meteor::before{content:"\f753"}.fa-car-on::before{content:"\e4dd"}.fa-sleigh::before{content:"\f7cc"}.fa-arrow-down-1-9::before{content:"\f162"}.fa-sort-numeric-asc::before{content:"\f162"}.fa-sort-numeric-down::before{content:"\f162"}.fa-hand-holding-droplet::before{content:"\f4c1"}.fa-hand-holding-water::before{content:"\f4c1"}.fa-water::before{content:"\f773"}.fa-calendar-check::before{content:"\f274"}.fa-braille::before{content:"\f2a1"}.fa-prescription-bottle-medical::before{content:"\f486"}.fa-prescription-bottle-alt::before{content:"\f486"}.fa-landmark::before{content:"\f66f"}.fa-truck::before{content:"\f0d1"}.fa-crosshairs::before{content:"\f05b"}.fa-person-cane::before{content:"\e53c"}.fa-tent::before{content:"\e57d"}.fa-vest-patches::before{content:"\e086"}.fa-check-double::before{content:"\f560"}.fa-arrow-down-a-z::before{content:"\f15d"}.fa-sort-alpha-asc::before{content:"\f15d"}.fa-sort-alpha-down::before{content:"\f15d"}.fa-money-bill-wheat::before{content:"\e52a"}.fa-cookie::before{content:"\f563"}.fa-arrow-rotate-left::before{content:"\f0e2"}.fa-arrow-left-rotate::before{content:"\f0e2"}.fa-arrow-rotate-back::before{content:"\f0e2"}.fa-arrow-rotate-backward::before{content:"\f0e2"}.fa-undo::before{content:"\f0e2"}.fa-hard-drive::before{content:"\f0a0"}.fa-hdd::before{content:"\f0a0"}.fa-face-grin-squint-tears::before{content:"\f586"}.fa-grin-squint-tears::before{content:"\f586"}.fa-dumbbell::before{content:"\f44b"}.fa-rectangle-list::before{content:"\f022"}.fa-list-alt::before{content:"\f022"}.fa-tarp-droplet::before{content:"\e57c"}.fa-house-medical-circle-check::before{content:"\e511"}.fa-person-skiing-nordic::before{content:"\f7ca"}.fa-skiing-nordic::before{content:"\f7ca"}.fa-calendar-plus::before{content:"\f271"}.fa-plane-arrival::before{content:"\f5af"}.fa-circle-left::before{content:"\f359"}.fa-arrow-alt-circle-left::before{content:"\f359"}.fa-train-subway::before{content:"\f239"}.fa-subway::before{content:"\f239"}.fa-chart-gantt::before{content:"\e0e4"}.fa-indian-rupee-sign::before{content:"\e1bc"}.fa-indian-rupee::before{content:"\e1bc"}.fa-inr::before{content:"\e1bc"}.fa-crop-simple::before{content:"\f565"}.fa-crop-alt::before{content:"\f565"}.fa-money-bill-1::before{content:"\f3d1"}.fa-money-bill-alt::before{content:"\f3d1"}.fa-left-long::before{content:"\f30a"}.fa-long-arrow-alt-left::before{content:"\f30a"}.fa-dna::before{content:"\f471"}.fa-virus-slash::before{content:"\e075"}.fa-minus::before{content:"\f068"}.fa-subtract::before{content:"\f068"}.fa-chess::before{content:"\f439"}.fa-arrow-left-long::before{content:"\f177"}.fa-long-arrow-left::before{content:"\f177"}.fa-plug-circle-check::before{content:"\e55c"}.fa-street-view::before{content:"\f21d"}.fa-franc-sign::before{content:"\e18f"}.fa-volume-off::before{content:"\f026"}.fa-hands-asl-interpreting::before{content:"\f2a3"}.fa-american-sign-language-interpreting::before{content:"\f2a3"}.fa-asl-interpreting::before{content:"\f2a3"}.fa-hands-american-sign-language-interpreting::before{content:"\f2a3"}.fa-gear::before{content:"\f013"}.fa-cog::before{content:"\f013"}.fa-droplet-slash::before{content:"\f5c7"}.fa-tint-slash::before{content:"\f5c7"}.fa-mosque::before{content:"\f678"}.fa-mosquito::before{content:"\e52b"}.fa-star-of-david::before{content:"\f69a"}.fa-person-military-rifle::before{content:"\e54b"}.fa-cart-shopping::before{content:"\f07a"}.fa-shopping-cart::before{content:"\f07a"}.fa-vials::before{content:"\f493"}.fa-plug-circle-plus::before{content:"\e55f"}.fa-place-of-worship::before{content:"\f67f"}.fa-grip-vertical::before{content:"\f58e"}.fa-arrow-turn-up::before{content:"\f148"}.fa-level-up::before{content:"\f148"}.fa-u::before{content:"\55"}.fa-square-root-variable::before{content:"\f698"}.fa-square-root-alt::before{content:"\f698"}.fa-clock::before{content:"\f017"}.fa-clock-four::before{content:"\f017"}.fa-backward-step::before{content:"\f048"}.fa-step-backward::before{content:"\f048"}.fa-pallet::before{content:"\f482"}.fa-faucet::before{content:"\e005"}.fa-baseball-bat-ball::before{content:"\f432"}.fa-s::before{content:"\53"}.fa-timeline::before{content:"\e29c"}.fa-keyboard::before{content:"\f11c"}.fa-caret-down::before{content:"\f0d7"}.fa-house-chimney-medical::before{content:"\f7f2"}.fa-clinic-medical::before{content:"\f7f2"}.fa-temperature-three-quarters::before{content:"\f2c8"}.fa-temperature-3::before{content:"\f2c8"}.fa-thermometer-3::before{content:"\f2c8"}.fa-thermometer-three-quarters::before{content:"\f2c8"}.fa-mobile-screen::before{content:"\f3cf"}.fa-mobile-android-alt::before{content:"\f3cf"}.fa-plane-up::before{content:"\e22d"}.fa-piggy-bank::before{content:"\f4d3"}.fa-battery-half::before{content:"\f242"}.fa-battery-3::before{content:"\f242"}.fa-mountain-city::before{content:"\e52e"}.fa-coins::before{content:"\f51e"}.fa-khanda::before{content:"\f66d"}.fa-sliders::before{content:"\f1de"}.fa-sliders-h::before{content:"\f1de"}.fa-folder-tree::before{content:"\f802"}.fa-network-wired::before{content:"\f6ff"}.fa-map-pin::before{content:"\f276"}.fa-hamsa::before{content:"\f665"}.fa-cent-sign::before{content:"\e3f5"}.fa-flask::before{content:"\f0c3"}.fa-person-pregnant::before{content:"\e31e"}.fa-wand-sparkles::before{content:"\f72b"}.fa-ellipsis-vertical::before{content:"\f142"}.fa-ellipsis-v::before{content:"\f142"}.fa-ticket::before{content:"\f145"}.fa-power-off::before{content:"\f011"}.fa-right-long::before{content:"\f30b"}.fa-long-arrow-alt-right::before{content:"\f30b"}.fa-flag-usa::before{content:"\f74d"}.fa-laptop-file::before{content:"\e51d"}.fa-tty::before{content:"\f1e4"}.fa-teletype::before{content:"\f1e4"}.fa-diagram-next::before{content:"\e476"}.fa-person-rifle::before{content:"\e54e"}.fa-house-medical-circle-exclamation::before{content:"\e512"}.fa-closed-captioning::before{content:"\f20a"}.fa-person-hiking::before{content:"\f6ec"}.fa-hiking::before{content:"\f6ec"}.fa-venus-double::before{content:"\f226"}.fa-images::before{content:"\f302"}.fa-calculator::before{content:"\f1ec"}.fa-people-pulling::before{content:"\e535"}.fa-n::before{content:"\4e"}.fa-cable-car::before{content:"\f7da"}.fa-tram::before{content:"\f7da"}.fa-cloud-rain::before{content:"\f73d"}.fa-building-circle-xmark::before{content:"\e4d4"}.fa-ship::before{content:"\f21a"}.fa-arrows-down-to-line::before{content:"\e4b8"}.fa-download::before{content:"\f019"}.fa-face-grin::before{content:"\f580"}.fa-grin::before{content:"\f580"}.fa-delete-left::before{content:"\f55a"}.fa-backspace::before{content:"\f55a"}.fa-eye-dropper::before{content:"\f1fb"}.fa-eye-dropper-empty::before{content:"\f1fb"}.fa-eyedropper::before{content:"\f1fb"}.fa-file-circle-check::before{content:"\e5a0"}.fa-forward::before{content:"\f04e"}.fa-mobile::before{content:"\f3ce"}.fa-mobile-android::before{content:"\f3ce"}.fa-mobile-phone::before{content:"\f3ce"}.fa-face-meh::before{content:"\f11a"}.fa-meh::before{content:"\f11a"}.fa-align-center::before{content:"\f037"}.fa-book-skull::before{content:"\f6b7"}.fa-book-dead::before{content:"\f6b7"}.fa-id-card::before{content:"\f2c2"}.fa-drivers-license::before{content:"\f2c2"}.fa-outdent::before{content:"\f03b"}.fa-dedent::before{content:"\f03b"}.fa-heart-circle-exclamation::before{content:"\e4fe"}.fa-house::before{content:"\f015"}.fa-home::before{content:"\f015"}.fa-home-alt::before{content:"\f015"}.fa-home-lg-alt::before{content:"\f015"}.fa-calendar-week::before{content:"\f784"}.fa-laptop-medical::before{content:"\f812"}.fa-b::before{content:"\42"}.fa-file-medical::before{content:"\f477"}.fa-dice-one::before{content:"\f525"}.fa-kiwi-bird::before{content:"\f535"}.fa-arrow-right-arrow-left::before{content:"\f0ec"}.fa-exchange::before{content:"\f0ec"}.fa-rotate-right::before{content:"\f2f9"}.fa-redo-alt::before{content:"\f2f9"}.fa-rotate-forward::before{content:"\f2f9"}.fa-utensils::before{content:"\f2e7"}.fa-cutlery::before{content:"\f2e7"}.fa-arrow-up-wide-short::before{content:"\f161"}.fa-sort-amount-up::before{content:"\f161"}.fa-mill-sign::before{content:"\e1ed"}.fa-bowl-rice::before{content:"\e2eb"}.fa-skull::before{content:"\f54c"}.fa-tower-broadcast::before{content:"\f519"}.fa-broadcast-tower::before{content:"\f519"}.fa-truck-pickup::before{content:"\f63c"}.fa-up-long::before{content:"\f30c"}.fa-long-arrow-alt-up::before{content:"\f30c"}.fa-stop::before{content:"\f04d"}.fa-code-merge::before{content:"\f387"}.fa-upload::before{content:"\f093"}.fa-hurricane::before{content:"\f751"}.fa-mound::before{content:"\e52d"}.fa-toilet-portable::before{content:"\e583"}.fa-compact-disc::before{content:"\f51f"}.fa-file-arrow-down::before{content:"\f56d"}.fa-file-download::before{content:"\f56d"}.fa-caravan::before{content:"\f8ff"}.fa-shield-cat::before{content:"\e572"}.fa-bolt::before{content:"\f0e7"}.fa-zap::before{content:"\f0e7"}.fa-glass-water::before{content:"\e4f4"}.fa-oil-well::before{content:"\e532"}.fa-vault::before{content:"\e2c5"}.fa-mars::before{content:"\f222"}.fa-toilet::before{content:"\f7d8"}.fa-plane-circle-xmark::before{content:"\e557"}.fa-yen-sign::before{content:"\f157"}.fa-cny::before{content:"\f157"}.fa-jpy::before{content:"\f157"}.fa-rmb::before{content:"\f157"}.fa-yen::before{content:"\f157"}.fa-ruble-sign::before{content:"\f158"}.fa-rouble::before{content:"\f158"}.fa-rub::before{content:"\f158"}.fa-ruble::before{content:"\f158"}.fa-sun::before{content:"\f185"}.fa-guitar::before{content:"\f7a6"}.fa-face-laugh-wink::before{content:"\f59c"}.fa-laugh-wink::before{content:"\f59c"}.fa-horse-head::before{content:"\f7ab"}.fa-bore-hole::before{content:"\e4c3"}.fa-industry::before{content:"\f275"}.fa-circle-down::before{content:"\f358"}.fa-arrow-alt-circle-down::before{content:"\f358"}.fa-arrows-turn-to-dots::before{content:"\e4c1"}.fa-florin-sign::before{content:"\e184"}.fa-arrow-down-short-wide::before{content:"\f884"}.fa-sort-amount-desc::before{content:"\f884"}.fa-sort-amount-down-alt::before{content:"\f884"}.fa-less-than::before{content:"\3c"}.fa-angle-down::before{content:"\f107"}.fa-car-tunnel::before{content:"\e4de"}.fa-head-side-cough::before{content:"\e061"}.fa-grip-lines::before{content:"\f7a4"}.fa-thumbs-down::before{content:"\f165"}.fa-user-lock::before{content:"\f502"}.fa-arrow-right-long::before{content:"\f178"}.fa-long-arrow-right::before{content:"\f178"}.fa-anchor-circle-xmark::before{content:"\e4ac"}.fa-ellipsis::before{content:"\f141"}.fa-ellipsis-h::before{content:"\f141"}.fa-chess-pawn::before{content:"\f443"}.fa-kit-medical::before{content:"\f479"}.fa-first-aid::before{content:"\f479"}.fa-person-through-window::before{content:"\e5a9"}.fa-toolbox::before{content:"\f552"}.fa-hands-holding-circle::before{content:"\e4fb"}.fa-bug::before{content:"\f188"}.fa-credit-card::before{content:"\f09d"}.fa-credit-card-alt::before{content:"\f09d"}.fa-car::before{content:"\f1b9"}.fa-automobile::before{content:"\f1b9"}.fa-hand-holding-hand::before{content:"\e4f7"}.fa-book-open-reader::before{content:"\f5da"}.fa-book-reader::before{content:"\f5da"}.fa-mountain-sun::before{content:"\e52f"}.fa-arrows-left-right-to-line::before{content:"\e4ba"}.fa-dice-d20::before{content:"\f6cf"}.fa-truck-droplet::before{content:"\e58c"}.fa-file-circle-xmark::before{content:"\e5a1"}.fa-temperature-arrow-up::before{content:"\e040"}.fa-temperature-up::before{content:"\e040"}.fa-medal::before{content:"\f5a2"}.fa-bed::before{content:"\f236"}.fa-square-h::before{content:"\f0fd"}.fa-h-square::before{content:"\f0fd"}.fa-podcast::before{content:"\f2ce"}.fa-temperature-full::before{content:"\f2c7"}.fa-temperature-4::before{content:"\f2c7"}.fa-thermometer-4::before{content:"\f2c7"}.fa-thermometer-full::before{content:"\f2c7"}.fa-bell::before{content:"\f0f3"}.fa-superscript::before{content:"\f12b"}.fa-plug-circle-xmark::before{content:"\e560"}.fa-star-of-life::before{content:"\f621"}.fa-phone-slash::before{content:"\f3dd"}.fa-paint-roller::before{content:"\f5aa"}.fa-handshake-angle::before{content:"\f4c4"}.fa-hands-helping::before{content:"\f4c4"}.fa-location-dot::before{content:"\f3c5"}.fa-map-marker-alt::before{content:"\f3c5"}.fa-file::before{content:"\f15b"}.fa-greater-than::before{content:"\3e"}.fa-person-swimming::before{content:"\f5c4"}.fa-swimmer::before{content:"\f5c4"}.fa-arrow-down::before{content:"\f063"}.fa-droplet::before{content:"\f043"}.fa-tint::before{content:"\f043"}.fa-eraser::before{content:"\f12d"}.fa-earth-americas::before{content:"\f57d"}.fa-earth::before{content:"\f57d"}.fa-earth-america::before{content:"\f57d"}.fa-globe-americas::before{content:"\f57d"}.fa-person-burst::before{content:"\e53b"}.fa-dove::before{content:"\f4ba"}.fa-battery-empty::before{content:"\f244"}.fa-battery-0::before{content:"\f244"}.fa-socks::before{content:"\f696"}.fa-inbox::before{content:"\f01c"}.fa-section::before{content:"\e447"}.fa-gauge-high::before{content:"\f625"}.fa-tachometer-alt::before{content:"\f625"}.fa-tachometer-alt-fast::before{content:"\f625"}.fa-envelope-open-text::before{content:"\f658"}.fa-hospital::before{content:"\f0f8"}.fa-hospital-alt::before{content:"\f0f8"}.fa-hospital-wide::before{content:"\f0f8"}.fa-wine-bottle::before{content:"\f72f"}.fa-chess-rook::before{content:"\f447"}.fa-bars-staggered::before{content:"\f550"}.fa-reorder::before{content:"\f550"}.fa-stream::before{content:"\f550"}.fa-dharmachakra::before{content:"\f655"}.fa-hotdog::before{content:"\f80f"}.fa-person-walking-with-cane::before{content:"\f29d"}.fa-blind::before{content:"\f29d"}.fa-drum::before{content:"\f569"}.fa-ice-cream::before{content:"\f810"}.fa-heart-circle-bolt::before{content:"\e4fc"}.fa-fax::before{content:"\f1ac"}.fa-paragraph::before{content:"\f1dd"}.fa-check-to-slot::before{content:"\f772"}.fa-vote-yea::before{content:"\f772"}.fa-star-half::before{content:"\f089"}.fa-boxes-stacked::before{content:"\f468"}.fa-boxes::before{content:"\f468"}.fa-boxes-alt::before{content:"\f468"}.fa-link::before{content:"\f0c1"}.fa-chain::before{content:"\f0c1"}.fa-ear-listen::before{content:"\f2a2"}.fa-assistive-listening-systems::before{content:"\f2a2"}.fa-tree-city::before{content:"\e587"}.fa-play::before{content:"\f04b"}.fa-font::before{content:"\f031"}.fa-rupiah-sign::before{content:"\e23d"}.fa-magnifying-glass::before{content:"\f002"}.fa-search::before{content:"\f002"}.fa-table-tennis-paddle-ball::before{content:"\f45d"}.fa-ping-pong-paddle-ball::before{content:"\f45d"}.fa-table-tennis::before{content:"\f45d"}.fa-person-dots-from-line::before{content:"\f470"}.fa-diagnoses::before{content:"\f470"}.fa-trash-can-arrow-up::before{content:"\f82a"}.fa-trash-restore-alt::before{content:"\f82a"}.fa-naira-sign::before{content:"\e1f6"}.fa-cart-arrow-down::before{content:"\f218"}.fa-walkie-talkie::before{content:"\f8ef"}.fa-file-pen::before{content:"\f31c"}.fa-file-edit::before{content:"\f31c"}.fa-receipt::before{content:"\f543"}.fa-square-pen::before{content:"\f14b"}.fa-pen-square::before{content:"\f14b"}.fa-pencil-square::before{content:"\f14b"}.fa-suitcase-rolling::before{content:"\f5c1"}.fa-person-circle-exclamation::before{content:"\e53f"}.fa-chevron-down::before{content:"\f078"}.fa-battery-full::before{content:"\f240"}.fa-battery::before{content:"\f240"}.fa-battery-5::before{content:"\f240"}.fa-skull-crossbones::before{content:"\f714"}.fa-code-compare::before{content:"\e13a"}.fa-list-ul::before{content:"\f0ca"}.fa-list-dots::before{content:"\f0ca"}.fa-school-lock::before{content:"\e56f"}.fa-tower-cell::before{content:"\e585"}.fa-down-long::before{content:"\f309"}.fa-long-arrow-alt-down::before{content:"\f309"}.fa-ranking-star::before{content:"\e561"}.fa-chess-king::before{content:"\f43f"}.fa-person-harassing::before{content:"\e549"}.fa-brazilian-real-sign::before{content:"\e46c"}.fa-landmark-dome::before{content:"\f752"}.fa-landmark-alt::before{content:"\f752"}.fa-arrow-up::before{content:"\f062"}.fa-tv::before{content:"\f26c"}.fa-television::before{content:"\f26c"}.fa-tv-alt::before{content:"\f26c"}.fa-shrimp::before{content:"\e448"}.fa-list-check::before{content:"\f0ae"}.fa-tasks::before{content:"\f0ae"}.fa-jug-detergent::before{content:"\e519"}.fa-circle-user::before{content:"\f2bd"}.fa-user-circle::before{content:"\f2bd"}.fa-user-shield::before{content:"\f505"}.fa-wind::before{content:"\f72e"}.fa-car-burst::before{content:"\f5e1"}.fa-car-crash::before{content:"\f5e1"}.fa-y::before{content:"\59"}.fa-person-snowboarding::before{content:"\f7ce"}.fa-snowboarding::before{content:"\f7ce"}.fa-truck-fast::before{content:"\f48b"}.fa-shipping-fast::before{content:"\f48b"}.fa-fish::before{content:"\f578"}.fa-user-graduate::before{content:"\f501"}.fa-circle-half-stroke::before{content:"\f042"}.fa-adjust::before{content:"\f042"}.fa-clapperboard::before{content:"\e131"}.fa-circle-radiation::before{content:"\f7ba"}.fa-radiation-alt::before{content:"\f7ba"}.fa-baseball::before{content:"\f433"}.fa-baseball-ball::before{content:"\f433"}.fa-jet-fighter-up::before{content:"\e518"}.fa-diagram-project::before{content:"\f542"}.fa-project-diagram::before{content:"\f542"}.fa-copy::before{content:"\f0c5"}.fa-volume-xmark::before{content:"\f6a9"}.fa-volume-mute::before{content:"\f6a9"}.fa-volume-times::before{content:"\f6a9"}.fa-hand-sparkles::before{content:"\e05d"}.fa-grip::before{content:"\f58d"}.fa-grip-horizontal::before{content:"\f58d"}.fa-share-from-square::before{content:"\f14d"}.fa-share-square::before{content:"\f14d"}.fa-child-combatant::before{content:"\e4e0"}.fa-child-rifle::before{content:"\e4e0"}.fa-gun::before{content:"\e19b"}.fa-square-phone::before{content:"\f098"}.fa-phone-square::before{content:"\f098"}.fa-plus::before{content:"\2b"}.fa-add::before{content:"\2b"}.fa-expand::before{content:"\f065"}.fa-computer::before{content:"\e4e5"}.fa-xmark::before{content:"\f00d"}.fa-close::before{content:"\f00d"}.fa-multiply::before{content:"\f00d"}.fa-remove::before{content:"\f00d"}.fa-times::before{content:"\f00d"}.fa-arrows-up-down-left-right::before{content:"\f047"}.fa-arrows::before{content:"\f047"}.fa-chalkboard-user::before{content:"\f51c"}.fa-chalkboard-teacher::before{content:"\f51c"}.fa-peso-sign::before{content:"\e222"}.fa-building-shield::before{content:"\e4d8"}.fa-baby::before{content:"\f77c"}.fa-users-line::before{content:"\e592"}.fa-quote-left::before{content:"\f10d"}.fa-quote-left-alt::before{content:"\f10d"}.fa-tractor::before{content:"\f722"}.fa-trash-arrow-up::before{content:"\f829"}.fa-trash-restore::before{content:"\f829"}.fa-arrow-down-up-lock::before{content:"\e4b0"}.fa-lines-leaning::before{content:"\e51e"}.fa-ruler-combined::before{content:"\f546"}.fa-copyright::before{content:"\f1f9"}.fa-equals::before{content:"\3d"}.fa-blender::before{content:"\f517"}.fa-teeth::before{content:"\f62e"}.fa-shekel-sign::before{content:"\f20b"}.fa-ils::before{content:"\f20b"}.fa-shekel::before{content:"\f20b"}.fa-sheqel::before{content:"\f20b"}.fa-sheqel-sign::before{content:"\f20b"}.fa-map::before{content:"\f279"}.fa-rocket::before{content:"\f135"}.fa-photo-film::before{content:"\f87c"}.fa-photo-video::before{content:"\f87c"}.fa-folder-minus::before{content:"\f65d"}.fa-store::before{content:"\f54e"}.fa-arrow-trend-up::before{content:"\e098"}.fa-plug-circle-minus::before{content:"\e55e"}.fa-sign-hanging::before{content:"\f4d9"}.fa-sign::before{content:"\f4d9"}.fa-bezier-curve::before{content:"\f55b"}.fa-bell-slash::before{content:"\f1f6"}.fa-tablet::before{content:"\f3fb"}.fa-tablet-android::before{content:"\f3fb"}.fa-school-flag::before{content:"\e56e"}.fa-fill::before{content:"\f575"}.fa-angle-up::before{content:"\f106"}.fa-drumstick-bite::before{content:"\f6d7"}.fa-holly-berry::before{content:"\f7aa"}.fa-chevron-left::before{content:"\f053"}.fa-bacteria::before{content:"\e059"}.fa-hand-lizard::before{content:"\f258"}.fa-notdef::before{content:"\e1fe"}.fa-disease::before{content:"\f7fa"}.fa-briefcase-medical::before{content:"\f469"}.fa-genderless::before{content:"\f22d"}.fa-chevron-right::before{content:"\f054"}.fa-retweet::before{content:"\f079"}.fa-car-rear::before{content:"\f5de"}.fa-car-alt::before{content:"\f5de"}.fa-pump-soap::before{content:"\e06b"}.fa-video-slash::before{content:"\f4e2"}.fa-battery-quarter::before{content:"\f243"}.fa-battery-2::before{content:"\f243"}.fa-radio::before{content:"\f8d7"}.fa-baby-carriage::before{content:"\f77d"}.fa-carriage-baby::before{content:"\f77d"}.fa-traffic-light::before{content:"\f637"}.fa-thermometer::before{content:"\f491"}.fa-vr-cardboard::before{content:"\f729"}.fa-hand-middle-finger::before{content:"\f806"}.fa-percent::before{content:"\25"}.fa-percentage::before{content:"\25"}.fa-truck-moving::before{content:"\f4df"}.fa-glass-water-droplet::before{content:"\e4f5"}.fa-display::before{content:"\e163"}.fa-face-smile::before{content:"\f118"}.fa-smile::before{content:"\f118"}.fa-thumbtack::before{content:"\f08d"}.fa-thumb-tack::before{content:"\f08d"}.fa-trophy::before{content:"\f091"}.fa-person-praying::before{content:"\f683"}.fa-pray::before{content:"\f683"}.fa-hammer::before{content:"\f6e3"}.fa-hand-peace::before{content:"\f25b"}.fa-rotate::before{content:"\f2f1"}.fa-sync-alt::before{content:"\f2f1"}.fa-spinner::before{content:"\f110"}.fa-robot::before{content:"\f544"}.fa-peace::before{content:"\f67c"}.fa-gears::before{content:"\f085"}.fa-cogs::before{content:"\f085"}.fa-warehouse::before{content:"\f494"}.fa-arrow-up-right-dots::before{content:"\e4b7"}.fa-splotch::before{content:"\f5bc"}.fa-face-grin-hearts::before{content:"\f584"}.fa-grin-hearts::before{content:"\f584"}.fa-dice-four::before{content:"\f524"}.fa-sim-card::before{content:"\f7c4"}.fa-transgender::before{content:"\f225"}.fa-transgender-alt::before{content:"\f225"}.fa-mercury::before{content:"\f223"}.fa-arrow-turn-down::before{content:"\f149"}.fa-level-down::before{content:"\f149"}.fa-person-falling-burst::before{content:"\e547"}.fa-award::before{content:"\f559"}.fa-ticket-simple::before{content:"\f3ff"}.fa-ticket-alt::before{content:"\f3ff"}.fa-building::before{content:"\f1ad"}.fa-angles-left::before{content:"\f100"}.fa-angle-double-left::before{content:"\f100"}.fa-qrcode::before{content:"\f029"}.fa-clock-rotate-left::before{content:"\f1da"}.fa-history::before{content:"\f1da"}.fa-face-grin-beam-sweat::before{content:"\f583"}.fa-grin-beam-sweat::before{content:"\f583"}.fa-file-export::before{content:"\f56e"}.fa-arrow-right-from-file::before{content:"\f56e"}.fa-shield::before{content:"\f132"}.fa-shield-blank::before{content:"\f132"}.fa-arrow-up-short-wide::before{content:"\f885"}.fa-sort-amount-up-alt::before{content:"\f885"}.fa-house-medical::before{content:"\e3b2"}.fa-golf-ball-tee::before{content:"\f450"}.fa-golf-ball::before{content:"\f450"}.fa-circle-chevron-left::before{content:"\f137"}.fa-chevron-circle-left::before{content:"\f137"}.fa-house-chimney-window::before{content:"\e00d"}.fa-pen-nib::before{content:"\f5ad"}.fa-tent-arrow-turn-left::before{content:"\e580"}.fa-tents::before{content:"\e582"}.fa-wand-magic::before{content:"\f0d0"}.fa-magic::before{content:"\f0d0"}.fa-dog::before{content:"\f6d3"}.fa-carrot::before{content:"\f787"}.fa-moon::before{content:"\f186"}.fa-wine-glass-empty::before{content:"\f5ce"}.fa-wine-glass-alt::before{content:"\f5ce"}.fa-cheese::before{content:"\f7ef"}.fa-yin-yang::before{content:"\f6ad"}.fa-music::before{content:"\f001"}.fa-code-commit::before{content:"\f386"}.fa-temperature-low::before{content:"\f76b"}.fa-person-biking::before{content:"\f84a"}.fa-biking::before{content:"\f84a"}.fa-broom::before{content:"\f51a"}.fa-shield-heart::before{content:"\e574"}.fa-gopuram::before{content:"\f664"}.fa-earth-oceania::before{content:"\e47b"}.fa-globe-oceania::before{content:"\e47b"}.fa-square-xmark::before{content:"\f2d3"}.fa-times-square::before{content:"\f2d3"}.fa-xmark-square::before{content:"\f2d3"}.fa-hashtag::before{content:"\23"}.fa-up-right-and-down-left-from-center::before{content:"\f424"}.fa-expand-alt::before{content:"\f424"}.fa-oil-can::before{content:"\f613"}.fa-t::before{content:"\54"}.fa-hippo::before{content:"\f6ed"}.fa-chart-column::before{content:"\e0e3"}.fa-infinity::before{content:"\f534"}.fa-vial-circle-check::before{content:"\e596"}.fa-person-arrow-down-to-line::before{content:"\e538"}.fa-voicemail::before{content:"\f897"}.fa-fan::before{content:"\f863"}.fa-person-walking-luggage::before{content:"\e554"}.fa-up-down::before{content:"\f338"}.fa-arrows-alt-v::before{content:"\f338"}.fa-cloud-moon-rain::before{content:"\f73c"}.fa-calendar::before{content:"\f133"}.fa-trailer::before{content:"\e041"}.fa-bahai::before{content:"\f666"}.fa-haykal::before{content:"\f666"}.fa-sd-card::before{content:"\f7c2"}.fa-dragon::before{content:"\f6d5"}.fa-shoe-prints::before{content:"\f54b"}.fa-circle-plus::before{content:"\f055"}.fa-plus-circle::before{content:"\f055"}.fa-face-grin-tongue-wink::before{content:"\f58b"}.fa-grin-tongue-wink::before{content:"\f58b"}.fa-hand-holding::before{content:"\f4bd"}.fa-plug-circle-exclamation::before{content:"\e55d"}.fa-link-slash::before{content:"\f127"}.fa-chain-broken::before{content:"\f127"}.fa-chain-slash::before{content:"\f127"}.fa-unlink::before{content:"\f127"}.fa-clone::before{content:"\f24d"}.fa-person-walking-arrow-loop-left::before{content:"\e551"}.fa-arrow-up-z-a::before{content:"\f882"}.fa-sort-alpha-up-alt::before{content:"\f882"}.fa-fire-flame-curved::before{content:"\f7e4"}.fa-fire-alt::before{content:"\f7e4"}.fa-tornado::before{content:"\f76f"}.fa-file-circle-plus::before{content:"\e494"}.fa-book-quran::before{content:"\f687"}.fa-quran::before{content:"\f687"}.fa-anchor::before{content:"\f13d"}.fa-border-all::before{content:"\f84c"}.fa-face-angry::before{content:"\f556"}.fa-angry::before{content:"\f556"}.fa-cookie-bite::before{content:"\f564"}.fa-arrow-trend-down::before{content:"\e097"}.fa-rss::before{content:"\f09e"}.fa-feed::before{content:"\f09e"}.fa-draw-polygon::before{content:"\f5ee"}.fa-scale-balanced::before{content:"\f24e"}.fa-balance-scale::before{content:"\f24e"}.fa-gauge-simple-high::before{content:"\f62a"}.fa-tachometer::before{content:"\f62a"}.fa-tachometer-fast::before{content:"\f62a"}.fa-shower::before{content:"\f2cc"}.fa-desktop::before{content:"\f390"}.fa-desktop-alt::before{content:"\f390"}.fa-m::before{content:"\4d"}.fa-table-list::before{content:"\f00b"}.fa-th-list::before{content:"\f00b"}.fa-comment-sms::before{content:"\f7cd"}.fa-sms::before{content:"\f7cd"}.fa-book::before{content:"\f02d"}.fa-user-plus::before{content:"\f234"}.fa-check::before{content:"\f00c"}.fa-battery-three-quarters::before{content:"\f241"}.fa-battery-4::before{content:"\f241"}.fa-house-circle-check::before{content:"\e509"}.fa-angle-left::before{content:"\f104"}.fa-diagram-successor::before{content:"\e47a"}.fa-truck-arrow-right::before{content:"\e58b"}.fa-arrows-split-up-and-left::before{content:"\e4bc"}.fa-hand-fist::before{content:"\f6de"}.fa-fist-raised::before{content:"\f6de"}.fa-cloud-moon::before{content:"\f6c3"}.fa-briefcase::before{content:"\f0b1"}.fa-person-falling::before{content:"\e546"}.fa-image-portrait::before{content:"\f3e0"}.fa-portrait::before{content:"\f3e0"}.fa-user-tag::before{content:"\f507"}.fa-rug::before{content:"\e569"}.fa-earth-europe::before{content:"\f7a2"}.fa-globe-europe::before{content:"\f7a2"}.fa-cart-flatbed-suitcase::before{content:"\f59d"}.fa-luggage-cart::before{content:"\f59d"}.fa-rectangle-xmark::before{content:"\f410"}.fa-rectangle-times::before{content:"\f410"}.fa-times-rectangle::before{content:"\f410"}.fa-window-close::before{content:"\f410"}.fa-baht-sign::before{content:"\e0ac"}.fa-book-open::before{content:"\f518"}.fa-book-journal-whills::before{content:"\f66a"}.fa-journal-whills::before{content:"\f66a"}.fa-handcuffs::before{content:"\e4f8"}.fa-triangle-exclamation::before{content:"\f071"}.fa-exclamation-triangle::before{content:"\f071"}.fa-warning::before{content:"\f071"}.fa-database::before{content:"\f1c0"}.fa-share::before{content:"\f064"}.fa-arrow-turn-right::before{content:"\f064"}.fa-mail-forward::before{content:"\f064"}.fa-bottle-droplet::before{content:"\e4c4"}.fa-mask-face::before{content:"\e1d7"}.fa-hill-rockslide::before{content:"\e508"}.fa-right-left::before{content:"\f362"}.fa-exchange-alt::before{content:"\f362"}.fa-paper-plane::before{content:"\f1d8"}.fa-road-circle-exclamation::before{content:"\e565"}.fa-dungeon::before{content:"\f6d9"}.fa-align-right::before{content:"\f038"}.fa-money-bill-1-wave::before{content:"\f53b"}.fa-money-bill-wave-alt::before{content:"\f53b"}.fa-life-ring::before{content:"\f1cd"}.fa-hands::before{content:"\f2a7"}.fa-sign-language::before{content:"\f2a7"}.fa-signing::before{content:"\f2a7"}.fa-calendar-day::before{content:"\f783"}.fa-water-ladder::before{content:"\f5c5"}.fa-ladder-water::before{content:"\f5c5"}.fa-swimming-pool::before{content:"\f5c5"}.fa-arrows-up-down::before{content:"\f07d"}.fa-arrows-v::before{content:"\f07d"}.fa-face-grimace::before{content:"\f57f"}.fa-grimace::before{content:"\f57f"}.fa-wheelchair-move::before{content:"\e2ce"}.fa-wheelchair-alt::before{content:"\e2ce"}.fa-turn-down::before{content:"\f3be"}.fa-level-down-alt::before{content:"\f3be"}.fa-person-walking-arrow-right::before{content:"\e552"}.fa-square-envelope::before{content:"\f199"}.fa-envelope-square::before{content:"\f199"}.fa-dice::before{content:"\f522"}.fa-bowling-ball::before{content:"\f436"}.fa-brain::before{content:"\f5dc"}.fa-bandage::before{content:"\f462"}.fa-band-aid::before{content:"\f462"}.fa-calendar-minus::before{content:"\f272"}.fa-circle-xmark::before{content:"\f057"}.fa-times-circle::before{content:"\f057"}.fa-xmark-circle::before{content:"\f057"}.fa-gifts::before{content:"\f79c"}.fa-hotel::before{content:"\f594"}.fa-earth-asia::before{content:"\f57e"}.fa-globe-asia::before{content:"\f57e"}.fa-id-card-clip::before{content:"\f47f"}.fa-id-card-alt::before{content:"\f47f"}.fa-magnifying-glass-plus::before{content:"\f00e"}.fa-search-plus::before{content:"\f00e"}.fa-thumbs-up::before{content:"\f164"}.fa-user-clock::before{content:"\f4fd"}.fa-hand-dots::before{content:"\f461"}.fa-allergies::before{content:"\f461"}.fa-file-invoice::before{content:"\f570"}.fa-window-minimize::before{content:"\f2d1"}.fa-mug-saucer::before{content:"\f0f4"}.fa-coffee::before{content:"\f0f4"}.fa-brush::before{content:"\f55d"}.fa-mask::before{content:"\f6fa"}.fa-magnifying-glass-minus::before{content:"\f010"}.fa-search-minus::before{content:"\f010"}.fa-ruler-vertical::before{content:"\f548"}.fa-user-large::before{content:"\f406"}.fa-user-alt::before{content:"\f406"}.fa-train-tram::before{content:"\e5b4"}.fa-user-nurse::before{content:"\f82f"}.fa-syringe::before{content:"\f48e"}.fa-cloud-sun::before{content:"\f6c4"}.fa-stopwatch-20::before{content:"\e06f"}.fa-square-full::before{content:"\f45c"}.fa-magnet::before{content:"\f076"}.fa-jar::before{content:"\e516"}.fa-note-sticky::before{content:"\f249"}.fa-sticky-note::before{content:"\f249"}.fa-bug-slash::before{content:"\e490"}.fa-arrow-up-from-water-pump::before{content:"\e4b6"}.fa-bone::before{content:"\f5d7"}.fa-user-injured::before{content:"\f728"}.fa-face-sad-tear::before{content:"\f5b4"}.fa-sad-tear::before{content:"\f5b4"}.fa-plane::before{content:"\f072"}.fa-tent-arrows-down::before{content:"\e581"}.fa-exclamation::before{content:"\21"}.fa-arrows-spin::before{content:"\e4bb"}.fa-print::before{content:"\f02f"}.fa-turkish-lira-sign::before{content:"\e2bb"}.fa-try::before{content:"\e2bb"}.fa-turkish-lira::before{content:"\e2bb"}.fa-dollar-sign::before{content:"\24"}.fa-dollar::before{content:"\24"}.fa-usd::before{content:"\24"}.fa-x::before{content:"\58"}.fa-magnifying-glass-dollar::before{content:"\f688"}.fa-search-dollar::before{content:"\f688"}.fa-users-gear::before{content:"\f509"}.fa-users-cog::before{content:"\f509"}.fa-person-military-pointing::before{content:"\e54a"}.fa-building-columns::before{content:"\f19c"}.fa-bank::before{content:"\f19c"}.fa-institution::before{content:"\f19c"}.fa-museum::before{content:"\f19c"}.fa-university::before{content:"\f19c"}.fa-umbrella::before{content:"\f0e9"}.fa-trowel::before{content:"\e589"}.fa-d::before{content:"\44"}.fa-stapler::before{content:"\e5af"}.fa-masks-theater::before{content:"\f630"}.fa-theater-masks::before{content:"\f630"}.fa-kip-sign::before{content:"\e1c4"}.fa-hand-point-left::before{content:"\f0a5"}.fa-handshake-simple::before{content:"\f4c6"}.fa-handshake-alt::before{content:"\f4c6"}.fa-jet-fighter::before{content:"\f0fb"}.fa-fighter-jet::before{content:"\f0fb"}.fa-square-share-nodes::before{content:"\f1e1"}.fa-share-alt-square::before{content:"\f1e1"}.fa-barcode::before{content:"\f02a"}.fa-plus-minus::before{content:"\e43c"}.fa-video::before{content:"\f03d"}.fa-video-camera::before{content:"\f03d"}.fa-graduation-cap::before{content:"\f19d"}.fa-mortar-board::before{content:"\f19d"}.fa-hand-holding-medical::before{content:"\e05c"}.fa-person-circle-check::before{content:"\e53e"}.fa-turn-up::before{content:"\f3bf"}.fa-level-up-alt::before{content:"\f3bf"}.sr-only,.fa-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.sr-only-focusable:not(:focus),.fa-sr-only-focusable:not(:focus){position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}/*!* Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com -* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) -* Copyright 2023 Fonticons, Inc.*/:root,:host{--fa-style-family-classic:'Font Awesome 6 Free';--fa-font-solid:normal 900 1em/1 'Font Awesome 6 Free'}@font-face{font-family:'font awesome 6 free';font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2)format("woff2"),url(../webfonts/fa-solid-900.ttf)format("truetype")}.fas,.td-offline-search-results__close-button:after,.fa-solid{font-weight:900}/*!* Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com -* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) -* Copyright 2023 Fonticons, Inc.*/:root,:host{--fa-style-family-brands:'Font Awesome 6 Brands';--fa-font-brands:normal 400 1em/1 'Font Awesome 6 Brands'}@font-face{font-family:'font awesome 6 brands';font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-brands-400.woff2)format("woff2"),url(../webfonts/fa-brands-400.ttf)format("truetype")}.fab,.fa-brands{font-weight:400}.fa-monero:before{content:"\f3d0"}.fa-hooli:before{content:"\f427"}.fa-yelp:before{content:"\f1e9"}.fa-cc-visa:before{content:"\f1f0"}.fa-lastfm:before{content:"\f202"}.fa-shopware:before{content:"\f5b5"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-aws:before{content:"\f375"}.fa-redhat:before{content:"\f7bc"}.fa-yoast:before{content:"\f2b1"}.fa-cloudflare:before{content:"\e07d"}.fa-ups:before{content:"\f7e0"}.fa-wpexplorer:before{content:"\f2de"}.fa-dyalog:before{content:"\f399"}.fa-bity:before{content:"\f37a"}.fa-stackpath:before{content:"\f842"}.fa-buysellads:before{content:"\f20d"}.fa-first-order:before{content:"\f2b0"}.fa-modx:before{content:"\f285"}.fa-guilded:before{content:"\e07e"}.fa-vnv:before{content:"\f40b"}.fa-square-js:before{content:"\f3b9"}.fa-js-square:before{content:"\f3b9"}.fa-microsoft:before{content:"\f3ca"}.fa-qq:before{content:"\f1d6"}.fa-orcid:before{content:"\f8d2"}.fa-java:before{content:"\f4e4"}.fa-invision:before{content:"\f7b0"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-centercode:before{content:"\f380"}.fa-glide-g:before{content:"\f2a6"}.fa-drupal:before{content:"\f1a9"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-unity:before{content:"\e049"}.fa-whmcs:before{content:"\f40d"}.fa-rocketchat:before{content:"\f3e8"}.fa-vk:before{content:"\f189"}.fa-untappd:before{content:"\f405"}.fa-mailchimp:before{content:"\f59e"}.fa-css3-alt:before{content:"\f38b"}.fa-square-reddit:before{content:"\f1a2"}.fa-reddit-square:before{content:"\f1a2"}.fa-vimeo-v:before{content:"\f27d"}.fa-contao:before{content:"\f26d"}.fa-square-font-awesome:before{content:"\e5ad"}.fa-deskpro:before{content:"\f38f"}.fa-sistrix:before{content:"\f3ee"}.fa-square-instagram:before{content:"\e055"}.fa-instagram-square:before{content:"\e055"}.fa-battle-net:before{content:"\f835"}.fa-the-red-yeti:before{content:"\f69d"}.fa-square-hacker-news:before{content:"\f3af"}.fa-hacker-news-square:before{content:"\f3af"}.fa-edge:before{content:"\f282"}.fa-napster:before{content:"\f3d2"}.fa-square-snapchat:before{content:"\f2ad"}.fa-snapchat-square:before{content:"\f2ad"}.fa-google-plus-g:before{content:"\f0d5"}.fa-artstation:before{content:"\f77a"}.fa-markdown:before{content:"\f60f"}.fa-sourcetree:before{content:"\f7d3"}.fa-google-plus:before{content:"\f2b3"}.fa-diaspora:before{content:"\f791"}.fa-foursquare:before{content:"\f180"}.fa-stack-overflow:before{content:"\f16c"}.fa-github-alt:before{content:"\f113"}.fa-phoenix-squadron:before{content:"\f511"}.fa-pagelines:before{content:"\f18c"}.fa-algolia:before{content:"\f36c"}.fa-red-river:before{content:"\f3e3"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-safari:before{content:"\f267"}.fa-google:before{content:"\f1a0"}.fa-square-font-awesome-stroke:before{content:"\f35c"}.fa-font-awesome-alt:before{content:"\f35c"}.fa-atlassian:before{content:"\f77b"}.fa-linkedin-in:before{content:"\f0e1"}.fa-digital-ocean:before{content:"\f391"}.fa-nimblr:before{content:"\f5a8"}.fa-chromecast:before{content:"\f838"}.fa-evernote:before{content:"\f839"}.fa-hacker-news:before{content:"\f1d4"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-adversal:before{content:"\f36a"}.fa-creative-commons:before{content:"\f25e"}.fa-watchman-monitoring:before{content:"\e087"}.fa-fonticons:before{content:"\f280"}.fa-weixin:before{content:"\f1d7"}.fa-shirtsinbulk:before{content:"\f214"}.fa-codepen:before{content:"\f1cb"}.fa-git-alt:before{content:"\f841"}.fa-lyft:before{content:"\f3c3"}.fa-rev:before{content:"\f5b2"}.fa-windows:before{content:"\f17a"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-square-viadeo:before{content:"\f2aa"}.fa-viadeo-square:before{content:"\f2aa"}.fa-meetup:before{content:"\f2e0"}.fa-centos:before{content:"\f789"}.fa-adn:before{content:"\f170"}.fa-cloudsmith:before{content:"\f384"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-square-dribbble:before{content:"\f397"}.fa-dribbble-square:before{content:"\f397"}.fa-codiepie:before{content:"\f284"}.fa-node:before{content:"\f419"}.fa-mix:before{content:"\f3cb"}.fa-steam:before{content:"\f1b6"}.fa-cc-apple-pay:before{content:"\f416"}.fa-scribd:before{content:"\f28a"}.fa-openid:before{content:"\f19b"}.fa-instalod:before{content:"\e081"}.fa-expeditedssl:before{content:"\f23e"}.fa-sellcast:before{content:"\f2da"}.fa-square-twitter:before{content:"\f081"}.fa-twitter-square:before{content:"\f081"}.fa-r-project:before{content:"\f4f7"}.fa-delicious:before{content:"\f1a5"}.fa-freebsd:before{content:"\f3a4"}.fa-vuejs:before{content:"\f41f"}.fa-accusoft:before{content:"\f369"}.fa-ioxhost:before{content:"\f208"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-app-store:before{content:"\f36f"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-itunes-note:before{content:"\f3b5"}.fa-golang:before{content:"\e40f"}.fa-kickstarter:before{content:"\f3bb"}.fa-grav:before{content:"\f2d6"}.fa-weibo:before{content:"\f18a"}.fa-uncharted:before{content:"\e084"}.fa-firstdraft:before{content:"\f3a1"}.fa-square-youtube:before{content:"\f431"}.fa-youtube-square:before{content:"\f431"}.fa-wikipedia-w:before{content:"\f266"}.fa-wpressr:before{content:"\f3e4"}.fa-rendact:before{content:"\f3e4"}.fa-angellist:before{content:"\f209"}.fa-galactic-republic:before{content:"\f50c"}.fa-nfc-directional:before{content:"\e530"}.fa-skype:before{content:"\f17e"}.fa-joget:before{content:"\f3b7"}.fa-fedora:before{content:"\f798"}.fa-stripe-s:before{content:"\f42a"}.fa-meta:before{content:"\e49b"}.fa-laravel:before{content:"\f3bd"}.fa-hotjar:before{content:"\f3b1"}.fa-bluetooth-b:before{content:"\f294"}.fa-sticker-mule:before{content:"\f3f7"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-hips:before{content:"\f452"}.fa-behance:before{content:"\f1b4"}.fa-reddit:before{content:"\f1a1"}.fa-discord:before{content:"\f392"}.fa-chrome:before{content:"\f268"}.fa-app-store-ios:before{content:"\f370"}.fa-cc-discover:before{content:"\f1f2"}.fa-wpbeginner:before{content:"\f297"}.fa-confluence:before{content:"\f78d"}.fa-mdb:before{content:"\f8ca"}.fa-dochub:before{content:"\f394"}.fa-accessible-icon:before{content:"\f368"}.fa-ebay:before{content:"\f4f4"}.fa-amazon:before{content:"\f270"}.fa-unsplash:before{content:"\e07c"}.fa-yarn:before{content:"\f7e3"}.fa-square-steam:before{content:"\f1b7"}.fa-steam-square:before{content:"\f1b7"}.fa-500px:before{content:"\f26e"}.fa-square-vimeo:before{content:"\f194"}.fa-vimeo-square:before{content:"\f194"}.fa-asymmetrik:before{content:"\f372"}.fa-font-awesome:before{content:"\f2b4"}.fa-font-awesome-flag:before{content:"\f2b4"}.fa-font-awesome-logo-full:before{content:"\f2b4"}.fa-gratipay:before{content:"\f184"}.fa-apple:before{content:"\f179"}.fa-hive:before{content:"\e07f"}.fa-gitkraken:before{content:"\f3a6"}.fa-keybase:before{content:"\f4f5"}.fa-apple-pay:before{content:"\f415"}.fa-padlet:before{content:"\e4a0"}.fa-amazon-pay:before{content:"\f42c"}.fa-square-github:before{content:"\f092"}.fa-github-square:before{content:"\f092"}.fa-stumbleupon:before{content:"\f1a4"}.fa-fedex:before{content:"\f797"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-shopify:before{content:"\e057"}.fa-neos:before{content:"\f612"}.fa-hackerrank:before{content:"\f5f7"}.fa-researchgate:before{content:"\f4f8"}.fa-swift:before{content:"\f8e1"}.fa-angular:before{content:"\f420"}.fa-speakap:before{content:"\f3f3"}.fa-angrycreative:before{content:"\f36e"}.fa-y-combinator:before{content:"\f23b"}.fa-empire:before{content:"\f1d1"}.fa-envira:before{content:"\f299"}.fa-square-gitlab:before{content:"\e5ae"}.fa-gitlab-square:before{content:"\e5ae"}.fa-studiovinari:before{content:"\f3f8"}.fa-pied-piper:before{content:"\f2ae"}.fa-wordpress:before{content:"\f19a"}.fa-product-hunt:before{content:"\f288"}.fa-firefox:before{content:"\f269"}.fa-linode:before{content:"\f2b8"}.fa-goodreads:before{content:"\f3a8"}.fa-square-odnoklassniki:before{content:"\f264"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-jsfiddle:before{content:"\f1cc"}.fa-sith:before{content:"\f512"}.fa-themeisle:before{content:"\f2b2"}.fa-page4:before{content:"\f3d7"}.fa-hashnode:before{content:"\e499"}.fa-react:before{content:"\f41b"}.fa-cc-paypal:before{content:"\f1f4"}.fa-squarespace:before{content:"\f5be"}.fa-cc-stripe:before{content:"\f1f5"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-bitcoin:before{content:"\f379"}.fa-keycdn:before{content:"\f3ba"}.fa-opera:before{content:"\f26a"}.fa-itch-io:before{content:"\f83a"}.fa-umbraco:before{content:"\f8e8"}.fa-galactic-senate:before{content:"\f50d"}.fa-ubuntu:before{content:"\f7df"}.fa-draft2digital:before{content:"\f396"}.fa-stripe:before{content:"\f429"}.fa-houzz:before{content:"\f27c"}.fa-gg:before{content:"\f260"}.fa-dhl:before{content:"\f790"}.fa-square-pinterest:before{content:"\f0d3"}.fa-pinterest-square:before{content:"\f0d3"}.fa-xing:before{content:"\f168"}.fa-blackberry:before{content:"\f37b"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-playstation:before{content:"\f3df"}.fa-quinscape:before{content:"\f459"}.fa-less:before{content:"\f41d"}.fa-blogger-b:before{content:"\f37d"}.fa-opencart:before{content:"\f23d"}.fa-vine:before{content:"\f1ca"}.fa-paypal:before{content:"\f1ed"}.fa-gitlab:before{content:"\f296"}.fa-typo3:before{content:"\f42b"}.fa-reddit-alien:before{content:"\f281"}.fa-yahoo:before{content:"\f19e"}.fa-dailymotion:before{content:"\e052"}.fa-affiliatetheme:before{content:"\f36b"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-bootstrap:before{content:"\f836"}.fa-odnoklassniki:before{content:"\f263"}.fa-nfc-symbol:before{content:"\e531"}.fa-ethereum:before{content:"\f42e"}.fa-speaker-deck:before{content:"\f83c"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-patreon:before{content:"\f3d9"}.fa-avianex:before{content:"\f374"}.fa-ello:before{content:"\f5f1"}.fa-gofore:before{content:"\f3a7"}.fa-bimobject:before{content:"\f378"}.fa-facebook-f:before{content:"\f39e"}.fa-square-google-plus:before{content:"\f0d4"}.fa-google-plus-square:before{content:"\f0d4"}.fa-mandalorian:before{content:"\f50f"}.fa-first-order-alt:before{content:"\f50a"}.fa-osi:before{content:"\f41a"}.fa-google-wallet:before{content:"\f1ee"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-periscope:before{content:"\f3da"}.fa-fulcrum:before{content:"\f50b"}.fa-cloudscale:before{content:"\f383"}.fa-forumbee:before{content:"\f211"}.fa-mizuni:before{content:"\f3cc"}.fa-schlix:before{content:"\f3ea"}.fa-square-xing:before{content:"\f169"}.fa-xing-square:before{content:"\f169"}.fa-bandcamp:before{content:"\f2d5"}.fa-wpforms:before{content:"\f298"}.fa-cloudversify:before{content:"\f385"}.fa-usps:before{content:"\f7e1"}.fa-megaport:before{content:"\f5a3"}.fa-magento:before{content:"\f3c4"}.fa-spotify:before{content:"\f1bc"}.fa-optin-monster:before{content:"\f23c"}.fa-fly:before{content:"\f417"}.fa-aviato:before{content:"\f421"}.fa-itunes:before{content:"\f3b4"}.fa-cuttlefish:before{content:"\f38c"}.fa-blogger:before{content:"\f37c"}.fa-flickr:before{content:"\f16e"}.fa-viber:before{content:"\f409"}.fa-soundcloud:before{content:"\f1be"}.fa-digg:before{content:"\f1a6"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-symfony:before{content:"\f83d"}.fa-maxcdn:before{content:"\f136"}.fa-etsy:before{content:"\f2d7"}.fa-facebook-messenger:before{content:"\f39f"}.fa-audible:before{content:"\f373"}.fa-think-peaks:before{content:"\f731"}.fa-bilibili:before{content:"\e3d9"}.fa-erlang:before{content:"\f39d"}.fa-cotton-bureau:before{content:"\f89e"}.fa-dashcube:before{content:"\f210"}.fa-42-group:before{content:"\e080"}.fa-innosoft:before{content:"\e080"}.fa-stack-exchange:before{content:"\f18d"}.fa-elementor:before{content:"\f430"}.fa-square-pied-piper:before{content:"\e01e"}.fa-pied-piper-square:before{content:"\e01e"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-palfed:before{content:"\f3d8"}.fa-superpowers:before{content:"\f2dd"}.fa-resolving:before{content:"\f3e7"}.fa-xbox:before{content:"\f412"}.fa-searchengin:before{content:"\f3eb"}.fa-tiktok:before{content:"\e07b"}.fa-square-facebook:before{content:"\f082"}.fa-facebook-square:before{content:"\f082"}.fa-renren:before{content:"\f18b"}.fa-linux:before{content:"\f17c"}.fa-glide:before{content:"\f2a5"}.fa-linkedin:before{content:"\f08c"}.fa-hubspot:before{content:"\f3b2"}.fa-deploydog:before{content:"\f38e"}.fa-twitch:before{content:"\f1e8"}.fa-ravelry:before{content:"\f2d9"}.fa-mixer:before{content:"\e056"}.fa-square-lastfm:before{content:"\f203"}.fa-lastfm-square:before{content:"\f203"}.fa-vimeo:before{content:"\f40a"}.fa-mendeley:before{content:"\f7b3"}.fa-uniregistry:before{content:"\f404"}.fa-figma:before{content:"\f799"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-dropbox:before{content:"\f16b"}.fa-instagram:before{content:"\f16d"}.fa-cmplid:before{content:"\e360"}.fa-facebook:before{content:"\f09a"}.fa-gripfire:before{content:"\f3ac"}.fa-jedi-order:before{content:"\f50e"}.fa-uikit:before{content:"\f403"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-phabricator:before{content:"\f3db"}.fa-ussunnah:before{content:"\f407"}.fa-earlybirds:before{content:"\f39a"}.fa-trade-federation:before{content:"\f513"}.fa-autoprefixer:before{content:"\f41c"}.fa-whatsapp:before{content:"\f232"}.fa-slideshare:before{content:"\f1e7"}.fa-google-play:before{content:"\f3ab"}.fa-viadeo:before{content:"\f2a9"}.fa-line:before{content:"\f3c0"}.fa-google-drive:before{content:"\f3aa"}.fa-servicestack:before{content:"\f3ec"}.fa-simplybuilt:before{content:"\f215"}.fa-bitbucket:before{content:"\f171"}.fa-imdb:before{content:"\f2d8"}.fa-deezer:before{content:"\e077"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-jira:before{content:"\f7b1"}.fa-docker:before{content:"\f395"}.fa-screenpal:before{content:"\e570"}.fa-bluetooth:before{content:"\f293"}.fa-gitter:before{content:"\f426"}.fa-d-and-d:before{content:"\f38d"}.fa-microblog:before{content:"\e01a"}.fa-cc-diners-club:before{content:"\f24c"}.fa-gg-circle:before{content:"\f261"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-yandex:before{content:"\f413"}.fa-readme:before{content:"\f4d5"}.fa-html5:before{content:"\f13b"}.fa-sellsy:before{content:"\f213"}.fa-sass:before{content:"\f41e"}.fa-wirsindhandwerk:before{content:"\e2d0"}.fa-wsh:before{content:"\e2d0"}.fa-buromobelexperte:before{content:"\f37f"}.fa-salesforce:before{content:"\f83b"}.fa-octopus-deploy:before{content:"\e082"}.fa-medapps:before{content:"\f3c6"}.fa-ns8:before{content:"\f3d5"}.fa-pinterest-p:before{content:"\f231"}.fa-apper:before{content:"\f371"}.fa-fort-awesome:before{content:"\f286"}.fa-waze:before{content:"\f83f"}.fa-cc-jcb:before{content:"\f24b"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ab"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-rust:before{content:"\e07a"}.fa-wix:before{content:"\f5cf"}.fa-square-behance:before{content:"\f1b5"}.fa-behance-square:before{content:"\f1b5"}.fa-supple:before{content:"\f3f9"}.fa-rebel:before{content:"\f1d0"}.fa-css3:before{content:"\f13c"}.fa-staylinked:before{content:"\f3f5"}.fa-kaggle:before{content:"\f5fa"}.fa-space-awesome:before{content:"\e5ac"}.fa-deviantart:before{content:"\f1bd"}.fa-cpanel:before{content:"\f388"}.fa-goodreads-g:before{content:"\f3a9"}.fa-square-git:before{content:"\f1d2"}.fa-git-square:before{content:"\f1d2"}.fa-square-tumblr:before{content:"\f174"}.fa-tumblr-square:before{content:"\f174"}.fa-trello:before{content:"\f181"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-get-pocket:before{content:"\f265"}.fa-perbyte:before{content:"\e083"}.fa-grunt:before{content:"\f3ad"}.fa-weebly:before{content:"\f5cc"}.fa-connectdevelop:before{content:"\f20e"}.fa-leanpub:before{content:"\f212"}.fa-black-tie:before{content:"\f27e"}.fa-themeco:before{content:"\f5c6"}.fa-python:before{content:"\f3e2"}.fa-android:before{content:"\f17b"}.fa-bots:before{content:"\e340"}.fa-free-code-camp:before{content:"\f2c5"}.fa-hornbill:before{content:"\f592"}.fa-js:before{content:"\f3b8"}.fa-ideal:before{content:"\e013"}.fa-git:before{content:"\f1d3"}.fa-dev:before{content:"\f6cc"}.fa-sketch:before{content:"\f7c6"}.fa-yandex-international:before{content:"\f414"}.fa-cc-amex:before{content:"\f1f3"}.fa-uber:before{content:"\f402"}.fa-github:before{content:"\f09b"}.fa-php:before{content:"\f457"}.fa-alipay:before{content:"\f642"}.fa-youtube:before{content:"\f167"}.fa-skyatlas:before{content:"\f216"}.fa-firefox-browser:before{content:"\e007"}.fa-replyd:before{content:"\f3e6"}.fa-suse:before{content:"\f7d6"}.fa-jenkins:before{content:"\f3b6"}.fa-twitter:before{content:"\f099"}.fa-rockrms:before{content:"\f3e9"}.fa-pinterest:before{content:"\f0d2"}.fa-buffer:before{content:"\f837"}.fa-npm:before{content:"\f3d4"}.fa-yammer:before{content:"\f840"}.fa-btc:before{content:"\f15a"}.fa-dribbble:before{content:"\f17d"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-internet-explorer:before{content:"\f26b"}.fa-stubber:before{content:"\e5c7"}.fa-telegram:before{content:"\f2c6"}.fa-telegram-plane:before{content:"\f2c6"}.fa-old-republic:before{content:"\f510"}.fa-odysee:before{content:"\e5c6"}.fa-square-whatsapp:before{content:"\f40c"}.fa-whatsapp-square:before{content:"\f40c"}.fa-node-js:before{content:"\f3d3"}.fa-edge-legacy:before{content:"\e078"}.fa-slack:before{content:"\f198"}.fa-slack-hash:before{content:"\f198"}.fa-medrt:before{content:"\f3c8"}.fa-usb:before{content:"\f287"}.fa-tumblr:before{content:"\f173"}.fa-vaadin:before{content:"\f408"}.fa-quora:before{content:"\f2c4"}.fa-reacteurope:before{content:"\f75d"}.fa-medium:before{content:"\f23a"}.fa-medium-m:before{content:"\f23a"}.fa-amilia:before{content:"\f36d"}.fa-mixcloud:before{content:"\f289"}.fa-flipboard:before{content:"\f44d"}.fa-viacoin:before{content:"\f237"}.fa-critical-role:before{content:"\f6c9"}.fa-sitrox:before{content:"\e44a"}.fa-discourse:before{content:"\f393"}.fa-joomla:before{content:"\f1aa"}.fa-mastodon:before{content:"\f4f6"}.fa-airbnb:before{content:"\f834"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-buy-n-large:before{content:"\f8a6"}.fa-gulp:before{content:"\f3ae"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-strava:before{content:"\f428"}.fa-ember:before{content:"\f423"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-teamspeak:before{content:"\f4f9"}.fa-pushed:before{content:"\f3e1"}.fa-wordpress-simple:before{content:"\f411"}.fa-nutritionix:before{content:"\f3d6"}.fa-wodu:before{content:"\e088"}.fa-google-pay:before{content:"\e079"}.fa-intercom:before{content:"\f7af"}.fa-zhihu:before{content:"\f63f"}.fa-korvue:before{content:"\f42f"}.fa-pix:before{content:"\e43a"}.fa-steam-symbol:before{content:"\f3f6"}.td-border-top{border:none;border-top:1px solid #eee}.td-border-none{border:none}.td-block-padding,.td-default main section{padding-top:4rem;padding-bottom:4rem}@media(min-width:768px){.td-block-padding,.td-default main section{padding-top:5rem;padding-bottom:5rem}}.td-overlay{position:relative}.td-overlay::after{content:"";position:absolute;top:0;right:0;bottom:0;left:0}.td-overlay--dark::after{background-color:rgba(51,63,71,.3)}.td-overlay--light::after{background-color:rgba(211,243,238,.3)}.td-overlay__inner{position:relative;z-index:1}@media(min-width:992px){.td-max-width-on-larger-screens,.td-card.card,.td-content .td-card.highlight,.td-card-group.card-group,.td-content .footnotes,.td-content>.alert,.td-content>.highlight,.td-content>.lead,.td-content>.td-table,.td-box .td-content>table,.td-content>table,.td-content>blockquote,.td-content>dl dd,.td-content>h1,.td-content>.h1,.td-content>h2,.td-content>.h2,.td-content>ol,.td-content>p,.td-content>pre,.td-content>ul{max-width:80%}}.-bg-blue{color:#fff;background-color:#0d6efd}.-bg-blue p>a{color:#81b3fe}.-bg-blue p>a:hover{color:#094db1}.-text-blue{color:#0d6efd}.-bg-indigo{color:#fff;background-color:#6610f2}.-bg-indigo p>a{color:#85b6fe}.-bg-indigo p>a:hover{color:#094db1}.-text-indigo{color:#6610f2}.-bg-purple{color:#fff;background-color:#6f42c1}.-bg-purple p>a{color:#84b5fe}.-bg-purple p>a:hover{color:#094db1}.-text-purple{color:#6f42c1}.-bg-pink{color:#fff;background-color:#d63384}.-bg-pink p>a{color:#81b4fe}.-bg-pink p>a:hover{color:#094db1}.-text-pink{color:#d63384}.-bg-red{color:#fff;background-color:#dc3545}.-bg-red p>a{color:#7db1fe}.-bg-red p>a:hover{color:#094db1}.-text-red{color:#dc3545}.-bg-orange{color:#000;background-color:#fd7e14}.-bg-orange p>a{color:#073b87}.-bg-orange p>a:hover{color:#094db1}.-text-orange{color:#fd7e14}.-bg-yellow{color:#000;background-color:#ffc107}.-bg-yellow p>a{color:#073982}.-bg-yellow p>a:hover{color:#094db1}.-text-yellow{color:#ffc107}.-bg-green{color:#fff;background-color:#198754}.-bg-green p>a{color:#b3d2fe}.-bg-green p>a:hover{color:#094db1}.-text-green{color:#198754}.-bg-teal{color:#000;background-color:#20c997}.-bg-teal p>a{color:#063274}.-bg-teal p>a:hover{color:#094db1}.-text-teal{color:#20c997}.-bg-cyan{color:#000;background-color:#0dcaf0}.-bg-cyan p>a{color:#06377e}.-bg-cyan p>a:hover{color:#094db1}.-text-cyan{color:#0dcaf0}.-bg-black{color:#fff;background-color:#000}.-bg-black p>a{color:#fff}.-bg-black p>a:hover{color:#094db1}.-text-black{color:#000}.-bg-white{color:#000;background-color:#fff}.-bg-white p>a{color:#0d6efd}.-bg-white p>a:hover{color:#094db1}.-text-white{color:#fff}.-bg-gray{color:#fff;background-color:#6c757d}.-bg-gray p>a{color:#90bdfe}.-bg-gray p>a:hover{color:#094db1}.-text-gray{color:#6c757d}.-bg-gray-dark{color:#fff;background-color:#343a40}.-bg-gray-dark p>a{color:#c8deff}.-bg-gray-dark p>a:hover{color:#094db1}.-text-gray-dark{color:#343a40}.-bg-primary{color:#fff;background-color:#5f7681}.-bg-primary p>a{color:#95bffe}.-bg-primary p>a:hover{color:#094db1}.-text-primary{color:#5f7681}.-bg-secondary{color:#000;background-color:#f58301}.-bg-secondary p>a{color:#06357a}.-bg-secondary p>a:hover{color:#094db1}.-text-secondary{color:#f58301}.-bg-success{color:#000;background-color:#3772ff}.-bg-success p>a{color:#08439a}.-bg-success p>a:hover{color:#094db1}.-text-success{color:#3772ff}.-bg-info{color:#fff;background-color:#007ac0}.-bg-info p>a{color:#a4c8fe}.-bg-info p>a:hover{color:#094db1}.-text-info{color:#007ac0}.-bg-warning{color:#000;background-color:#e33030}.-bg-warning p>a{color:#073b88}.-bg-warning p>a:hover{color:#094db1}.-text-warning{color:#e33030}.-bg-danger{color:#000;background-color:#ed6a5a}.-bg-danger p>a{color:#0847a2}.-bg-danger p>a:hover{color:#094db1}.-text-danger{color:#ed6a5a}.-bg-light{color:#000;background-color:#d3f3ee}.-bg-light p>a{color:#0c62e1}.-bg-light p>a:hover{color:#094db1}.-text-light{color:#d3f3ee}.-bg-dark{color:#fff;background-color:#333f47}.-bg-dark p>a{color:#c5dcff}.-bg-dark p>a:hover{color:#094db1}.-text-dark{color:#333f47}.-bg-100{color:#000;background-color:#f8f9fa}.-bg-100 p>a{color:#0d6bf7}.-bg-100 p>a:hover{color:#094db1}.-text-100{color:#f8f9fa}.-bg-200{color:#000;background-color:#e9ecef}.-bg-200 p>a{color:#0c66ea}.-bg-200 p>a:hover{color:#094db1}.-text-200{color:#e9ecef}.-bg-300{color:#000;background-color:#dee2e6}.-bg-300 p>a{color:#0c61e0}.-bg-300 p>a:hover{color:#094db1}.-text-300{color:#dee2e6}.-bg-400{color:#000;background-color:#ced4da}.-bg-400 p>a{color:#0b5bd2}.-bg-400 p>a:hover{color:#094db1}.-text-400{color:#ced4da}.-bg-500{color:#000;background-color:#adb5bd}.-bg-500 p>a{color:#094eb4}.-bg-500 p>a:hover{color:#094db1}.-text-500{color:#adb5bd}.-bg-600{color:#fff;background-color:#6c757d}.-bg-600 p>a{color:#90bdfe}.-bg-600 p>a:hover{color:#094db1}.-text-600{color:#6c757d}.-bg-700{color:#fff;background-color:#495057}.-bg-700 p>a{color:#b3d2fe}.-bg-700 p>a:hover{color:#094db1}.-text-700{color:#495057}.-bg-800{color:#fff;background-color:#343a40}.-bg-800 p>a{color:#c8deff}.-bg-800 p>a:hover{color:#094db1}.-text-800{color:#343a40}.-bg-900{color:#fff;background-color:#212529}.-bg-900 p>a{color:#dceaff}.-bg-900 p>a:hover{color:#094db1}.-text-900{color:#212529}.-bg-0{color:#fff;background-color:#403f4c}.-bg-0 p>a{color:#bdd7fe}.-bg-0 p>a:hover{color:#094db1}.-text-0{color:#403f4c}.-bg-1{color:#fff;background-color:#30638e}.-bg-1 p>a{color:#a5c9fe}.-bg-1 p>a:hover{color:#094db1}.-text-1{color:#30638e}.-bg-2{color:#000;background-color:#ffa630}.-bg-2 p>a{color:#084196}.-bg-2 p>a:hover{color:#094db1}.-text-2{color:#ffa630}.-bg-3{color:#000;background-color:#c0e0de}.-bg-3 p>a{color:#0b5ace}.-bg-3 p>a:hover{color:#094db1}.-text-3{color:#c0e0de}.-bg-4{color:#000;background-color:#fff}.-bg-4 p>a{color:#0d6efd}.-bg-4 p>a:hover{color:#094db1}.-text-4{color:#fff}.-bg-5{color:#fff;background-color:#6c757d}.-bg-5 p>a{color:#90bdfe}.-bg-5 p>a:hover{color:#094db1}.-text-5{color:#6c757d}.-bg-6{color:#000;background-color:#3772ff}.-bg-6 p>a{color:#08439a}.-bg-6 p>a:hover{color:#094db1}.-text-6{color:#3772ff}.-bg-7{color:#000;background-color:#ed6a5a}.-bg-7 p>a{color:#0847a2}.-bg-7 p>a:hover{color:#094db1}.-text-7{color:#ed6a5a}.-bg-8{color:#fff;background-color:#403f4c}.-bg-8 p>a{color:#bdd7fe}.-bg-8 p>a:hover{color:#094db1}.-text-8{color:#403f4c}.-bg-9{color:#000;background-color:#ed6a5a}.-bg-9 p>a{color:#0847a2}.-bg-9 p>a:hover{color:#094db1}.-text-9{color:#ed6a5a}.-bg-10{color:#fff;background-color:#30638e}.-bg-10 p>a{color:#a5c9fe}.-bg-10 p>a:hover{color:#094db1}.-text-10{color:#30638e}.-bg-11{color:#000;background-color:#ffa630}.-bg-11 p>a{color:#084196}.-bg-11 p>a:hover{color:#094db1}.-text-11{color:#ffa630}.-bg-12{color:#000;background-color:#c0e0de}.-bg-12 p>a{color:#0b5ace}.-bg-12 p>a:hover{color:#094db1}.-text-12{color:#c0e0de}.td-table:not(.td-initial),.td-content table:not(.td-initial),.td-box table:not(.td-initial){display:block}.td-box--height-min{min-height:300px}.td-box--height-med{min-height:400px}.td-box--height-max{min-height:500px}.td-box--height-full{min-height:100vh}@media(min-width:768px){.td-box--height-min{min-height:450px}.td-box--height-med{min-height:500px}.td-box--height-max{min-height:650px}}.td-box .row{padding-left:5vw;padding-right:5vw}.td-box.linkbox{padding:5vh 5vw}.td-box--0{color:#fff;background-color:#403f4c}.td-box--0 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#403f4c transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--0 p>a,.td-box--0 span>a{color:#bdd7fe}.td-box--0 p>a:hover,.td-box--0 span>a:hover{color:#d1e3fe}.td-box--1{color:#fff;background-color:#30638e}.td-box--1 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#30638e transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--1 p>a,.td-box--1 span>a{color:#a5c9fe}.td-box--1 p>a:hover,.td-box--1 span>a:hover{color:#c0d9fe}.td-box--2{color:#000;background-color:#ffa630}.td-box--2 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#ffa630 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--2 p>a,.td-box--2 span>a{color:#084196}.td-box--2 p>a:hover,.td-box--2 span>a:hover{color:#062e69}.td-box--3{color:#000;background-color:#c0e0de}.td-box--3 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#c0e0de transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--3 p>a,.td-box--3 span>a{color:#0b5ace}.td-box--3 p>a:hover,.td-box--3 span>a:hover{color:#083f90}.td-box--4{color:#000;background-color:#fff}.td-box--4 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#fff transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--4 p>a,.td-box--4 span>a{color:#0d6efd}.td-box--4 p>a:hover,.td-box--4 span>a:hover{color:#094db1}.td-box--5{color:#fff;background-color:#6c757d}.td-box--5 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#6c757d transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--5 p>a,.td-box--5 span>a{color:#90bdfe}.td-box--5 p>a:hover,.td-box--5 span>a:hover{color:#b1d1fe}.td-box--6{color:#000;background-color:#3772ff}.td-box--6 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#3772ff transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--6 p>a,.td-box--6 span>a{color:#08439a}.td-box--6 p>a:hover,.td-box--6 span>a:hover{color:#062f6c}.td-box--7{color:#000;background-color:#ed6a5a}.td-box--7 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#ed6a5a transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--7 p>a,.td-box--7 span>a{color:#0847a2}.td-box--7 p>a:hover,.td-box--7 span>a:hover{color:#063271}.td-box--8{color:#fff;background-color:#403f4c}.td-box--8 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#403f4c transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--8 p>a,.td-box--8 span>a{color:#bdd7fe}.td-box--8 p>a:hover,.td-box--8 span>a:hover{color:#d1e3fe}.td-box--9{color:#000;background-color:#ed6a5a}.td-box--9 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#ed6a5a transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--9 p>a,.td-box--9 span>a{color:#0847a2}.td-box--9 p>a:hover,.td-box--9 span>a:hover{color:#063271}.td-box--10{color:#fff;background-color:#30638e}.td-box--10 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#30638e transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--10 p>a,.td-box--10 span>a{color:#a5c9fe}.td-box--10 p>a:hover,.td-box--10 span>a:hover{color:#c0d9fe}.td-box--11{color:#000;background-color:#ffa630}.td-box--11 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#ffa630 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--11 p>a,.td-box--11 span>a{color:#084196}.td-box--11 p>a:hover,.td-box--11 span>a:hover{color:#062e69}.td-box--12{color:#000;background-color:#c0e0de}.td-box--12 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#c0e0de transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--12 p>a,.td-box--12 span>a{color:#0b5ace}.td-box--12 p>a:hover,.td-box--12 span>a:hover{color:#083f90}.td-box--blue{color:#fff;background-color:#0d6efd}.td-box--blue .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#0d6efd transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--blue p>a,.td-box--blue span>a{color:#81b3fe}.td-box--blue p>a:hover,.td-box--blue span>a:hover{color:#a7cafe}.td-box--indigo{color:#fff;background-color:#6610f2}.td-box--indigo .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#6610f2 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--indigo p>a,.td-box--indigo span>a{color:#85b6fe}.td-box--indigo p>a:hover,.td-box--indigo span>a:hover{color:#aaccfe}.td-box--purple{color:#fff;background-color:#6f42c1}.td-box--purple .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#6f42c1 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--purple p>a,.td-box--purple span>a{color:#84b5fe}.td-box--purple p>a:hover,.td-box--purple span>a:hover{color:#a9cbfe}.td-box--pink{color:#fff;background-color:#d63384}.td-box--pink .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#d63384 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--pink p>a,.td-box--pink span>a{color:#81b4fe}.td-box--pink p>a:hover,.td-box--pink span>a:hover{color:#a7cbfe}.td-box--red{color:#fff;background-color:#dc3545}.td-box--red .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#dc3545 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--red p>a,.td-box--red span>a{color:#7db1fe}.td-box--red p>a:hover,.td-box--red span>a:hover{color:#a4c8fe}.td-box--orange{color:#000;background-color:#fd7e14}.td-box--orange .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#fd7e14 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--orange p>a,.td-box--orange span>a{color:#073b87}.td-box--orange p>a:hover,.td-box--orange span>a:hover{color:#05295f}.td-box--yellow{color:#000;background-color:#ffc107}.td-box--yellow .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#ffc107 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--yellow p>a,.td-box--yellow span>a{color:#073982}.td-box--yellow p>a:hover,.td-box--yellow span>a:hover{color:#05285b}.td-box--green{color:#fff;background-color:#198754}.td-box--green .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#198754 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--green p>a,.td-box--green span>a{color:#b3d2fe}.td-box--green p>a:hover,.td-box--green span>a:hover{color:#cae0fe}.td-box--teal{color:#000;background-color:#20c997}.td-box--teal .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#20c997 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--teal p>a,.td-box--teal span>a{color:#063274}.td-box--teal p>a:hover,.td-box--teal span>a:hover{color:#042351}.td-box--cyan{color:#000;background-color:#0dcaf0}.td-box--cyan .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#0dcaf0 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--cyan p>a,.td-box--cyan span>a{color:#06377e}.td-box--cyan p>a:hover,.td-box--cyan span>a:hover{color:#042758}.td-box--black{color:#fff;background-color:#000}.td-box--black .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#000 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--black p>a,.td-box--black span>a{color:#fff}.td-box--black p>a:hover,.td-box--black span>a:hover{color:#fff}.td-box--white{color:#000;background-color:#fff}.td-box--white .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#fff transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--white p>a,.td-box--white span>a{color:#0d6efd}.td-box--white p>a:hover,.td-box--white span>a:hover{color:#094db1}.td-box--gray{color:#fff;background-color:#6c757d}.td-box--gray .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#6c757d transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--gray p>a,.td-box--gray span>a{color:#90bdfe}.td-box--gray p>a:hover,.td-box--gray span>a:hover{color:#b1d1fe}.td-box--gray-dark{color:#fff;background-color:#343a40}.td-box--gray-dark .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#343a40 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--gray-dark p>a,.td-box--gray-dark span>a{color:#c8deff}.td-box--gray-dark p>a:hover,.td-box--gray-dark span>a:hover{color:#d9e8ff}.td-box--primary{color:#fff;background-color:#5f7681}.td-box--primary .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#5f7681 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--primary p>a,.td-box--primary span>a{color:#95bffe}.td-box--primary p>a:hover,.td-box--primary span>a:hover{color:#b5d2fe}.td-box--secondary{color:#000;background-color:#f58301}.td-box--secondary .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#f58301 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--secondary p>a,.td-box--secondary span>a{color:#06357a}.td-box--secondary p>a:hover,.td-box--secondary span>a:hover{color:#042555}.td-box--success{color:#000;background-color:#3772ff}.td-box--success .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#3772ff transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--success p>a,.td-box--success span>a{color:#08439a}.td-box--success p>a:hover,.td-box--success span>a:hover{color:#062f6c}.td-box--info{color:#fff;background-color:#007ac0}.td-box--info .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#007ac0 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--info p>a,.td-box--info span>a{color:#a4c8fe}.td-box--info p>a:hover,.td-box--info span>a:hover{color:#bfd9fe}.td-box--warning{color:#000;background-color:#e33030}.td-box--warning .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#e33030 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--warning p>a,.td-box--warning span>a{color:#073b88}.td-box--warning p>a:hover,.td-box--warning span>a:hover{color:#05295f}.td-box--danger{color:#000;background-color:#ed6a5a}.td-box--danger .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#ed6a5a transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--danger p>a,.td-box--danger span>a{color:#0847a2}.td-box--danger p>a:hover,.td-box--danger span>a:hover{color:#063271}.td-box--light{color:#000;background-color:#d3f3ee}.td-box--light .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#d3f3ee transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--light p>a,.td-box--light span>a{color:#0c62e1}.td-box--light p>a:hover,.td-box--light span>a:hover{color:#08459e}.td-box--dark,.td-footer{color:#fff;background-color:#333f47}.td-box--dark .td-arrow-down::before,.td-footer .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#333f47 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--dark p>a,.td-footer p>a,.td-box--dark span>a,.td-footer span>a{color:#c5dcff}.td-box--dark p>a:hover,.td-footer p>a:hover,.td-box--dark span>a:hover,.td-footer span>a:hover{color:#d6e7ff}.td-box--100{color:#000;background-color:#f8f9fa}.td-box--100 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#f8f9fa transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--100 p>a,.td-box--100 span>a{color:#0d6bf7}.td-box--100 p>a:hover,.td-box--100 span>a:hover{color:#094bad}.td-box--200{color:#000;background-color:#e9ecef}.td-box--200 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#e9ecef transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--200 p>a,.td-box--200 span>a{color:#0c66ea}.td-box--200 p>a:hover,.td-box--200 span>a:hover{color:#0847a4}.td-box--300{color:#000;background-color:#dee2e6}.td-box--300 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#dee2e6 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--300 p>a,.td-box--300 span>a{color:#0c61e0}.td-box--300 p>a:hover,.td-box--300 span>a:hover{color:#08449d}.td-box--400{color:#000;background-color:#ced4da}.td-box--400 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#ced4da transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--400 p>a,.td-box--400 span>a{color:#0b5bd2}.td-box--400 p>a:hover,.td-box--400 span>a:hover{color:#084093}.td-box--500{color:#000;background-color:#adb5bd}.td-box--500 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#adb5bd transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--500 p>a,.td-box--500 span>a{color:#094eb4}.td-box--500 p>a:hover,.td-box--500 span>a:hover{color:#06377e}.td-box--600{color:#fff;background-color:#6c757d}.td-box--600 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#6c757d transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--600 p>a,.td-box--600 span>a{color:#90bdfe}.td-box--600 p>a:hover,.td-box--600 span>a:hover{color:#b1d1fe}.td-box--700{color:#fff;background-color:#495057}.td-box--700 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#495057 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--700 p>a,.td-box--700 span>a{color:#b3d2fe}.td-box--700 p>a:hover,.td-box--700 span>a:hover{color:#cae0fe}.td-box--800{color:#fff;background-color:#343a40}.td-box--800 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#343a40 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--800 p>a,.td-box--800 span>a{color:#c8deff}.td-box--800 p>a:hover,.td-box--800 span>a:hover{color:#d9e8ff}.td-box--900{color:#fff;background-color:#212529}.td-box--900 .td-arrow-down::before{left:50%;margin-left:-30px;bottom:-25px;border-style:solid;border-width:25px 30px 0;border-color:#212529 transparent transparent transparent;z-index:3;position:absolute;content:""}.td-box--900 p>a,.td-box--900 span>a{color:#dceaff}.td-box--900 p>a:hover,.td-box--900 span>a:hover{color:#e7f0ff}.td-blog .td-rss-button{float:right;display:none}.td-content .highlight{margin:2rem 0;padding:0;position:relative}.td-content .highlight .click-to-copy{display:block;text-align:right}.td-content .highlight pre{margin:0;padding:1rem}.td-content .highlight pre button.td-click-to-copy{position:absolute;color:#ced4da;border-radius:3px;border-width:0;background-color:inherit;box-shadow:1px 1px #ced4da;right:4px;top:2px}.td-content .highlight pre button.td-click-to-copy:hover{color:#333f47;background-color:#ced4da}.td-content .highlight pre button.td-click-to-copy:active{color:#333f47;background-color:#ced4da;transform:translateY(2px)}.td-content p code,.td-content li>code,.td-content table code{color:inherit;padding:.2em .4em;margin:0;font-size:85%;word-break:normal;background-color:rgba(0,0,0,5%);border-radius:.375rem}.td-content p code br,.td-content li>code br,.td-content table code br{display:none}.td-content pre{word-wrap:normal;background-color:#f8f9fa;padding:1rem}.td-content pre>code{background-color:inherit!important;padding:0;margin:0;font-size:100%;word-break:normal;white-space:pre;border:0}.td-content pre.mermaid{background-color:inherit;font-size:0;padding:0}@media(min-width:768px){.td-navbar-cover{background:0 0!important}.td-navbar-cover .nav-link{text-shadow:1px 1px 2px #333f47}}.td-navbar-cover.navbar-bg-onscroll .nav-link{text-shadow:none}.navbar-bg-onscroll{background:#5f7681!important;opacity:inherit}.td-navbar{background:#5f7681;min-height:4rem;margin:0;z-index:32}.td-navbar .navbar-brand{text-transform:none}.td-navbar .navbar-brand__name{font-weight:700}.td-navbar .navbar-brand svg{display:inline-block;margin:0 10px;height:30px}.td-navbar .navbar-nav{padding-top:.5rem;white-space:nowrap}.td-navbar .nav-link{text-transform:none;font-weight:700}.td-navbar .dropdown{min-width:100px}@media(min-width:768px){.td-navbar{position:fixed;top:0;width:100%}.td-navbar .nav-item{padding-inline-end:.5rem}.td-navbar .navbar-nav{padding-top:0!important}}@media(max-width:991.98px){.td-navbar .td-navbar-nav-scroll{max-width:100%;height:2.5rem;overflow:hidden;font-size:.9rem}.td-navbar .navbar-brand{margin-right:0}.td-navbar .navbar-nav{padding-bottom:2rem;overflow-x:auto}}#main_navbar li i{padding-right:.5em}#main_navbar li i:before{display:inline-block;text-align:center;min-width:1em}#main_navbar .alert{background-color:inherit;padding:0;color:#f58301;border:0;font-weight:inherit}#main_navbar .alert:before{display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;-webkit-font-smoothing:antialiased;font-family:"font awesome 6 free";font-weight:900;content:"\f0d9";padding-left:.5em;padding-right:.5em}nav.foldable-nav#td-section-nav{position:relative}nav.foldable-nav#td-section-nav label{margin-bottom:0;width:100%}nav.foldable-nav .td-sidebar-nav__section,nav.foldable-nav .with-child ul{list-style:none;padding:0;margin:0}nav.foldable-nav .ul-1>li{padding-left:1.5em}nav.foldable-nav ul.foldable{display:none}nav.foldable-nav input:checked~ul.foldable{display:block}nav.foldable-nav input[type=checkbox]{display:none}nav.foldable-nav .with-child,nav.foldable-nav .without-child{position:relative;padding-left:1.5em}nav.foldable-nav .ul-1 .with-child>label:before{display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;-webkit-font-smoothing:antialiased;font-family:"font awesome 6 free";font-weight:900;content:"\f0da";position:absolute;left:.1em;padding-left:.4em;padding-right:.4em;font-size:1em;color:#212529;transition:all .5s}nav.foldable-nav .ul-1 .with-child>label:before:hover{transform:rotate(90deg)}nav.foldable-nav .ul-1 .with-child>input:checked~label:before{color:#5f7681;transform:rotate(90deg);transition:transform .5s}nav.foldable-nav .with-child ul{margin-top:.1em}@media(hover:hover) and (pointer:fine){nav.foldable-nav .ul-1 .with-child>label:hover:before{color:#5f7681;transition:color .3s}nav.foldable-nav .ul-1 .with-child>input:checked~label:hover:before{color:#5f7681;transition:color .3s}}.td-sidebar-nav{padding-right:.5rem;margin-right:-15px;margin-left:-15px}@media(min-width:768px){@supports(position:sticky){.td-sidebar-nav{max-height:calc(100vh - 10rem);overflow-y:auto}}}@media(min-width:768px){.td-sidebar-nav{display:block!important}}.td-sidebar-nav__section{padding-left:0}.td-sidebar-nav__section li{list-style:none}.td-sidebar-nav__section ul{padding:0;margin:0}@media(min-width:768px){.td-sidebar-nav__section .ul-1 ul{padding-left:1.5em}}.td-sidebar-nav__section-title{display:block;font-weight:500}.td-sidebar-nav__section-title .active{font-weight:700}.td-sidebar-nav__section-title a{color:#212529}.td-sidebar-nav .td-sidebar-link{display:block;padding-bottom:.375rem}.td-sidebar-nav .td-sidebar-link__page{color:#495057;font-weight:300}.td-sidebar-nav a:hover{color:#0d6efd;text-decoration:none}.td-sidebar-nav a.active{font-weight:700}.td-sidebar-nav .dropdown a{color:#495057}.td-sidebar-nav .dropdown .nav-link{padding:0 0 1rem}.td-sidebar-nav>.td-sidebar-nav__section{padding-top:.5rem;padding-left:1.5rem}.td-sidebar-nav li i{padding-right:.5em}.td-sidebar-nav li i:before{display:inline-block;text-align:center;min-width:1em}.td-sidebar-nav .td-sidebar-link.tree-root{font-weight:700;color:#30638e;border-bottom:1px #30638e solid;margin-bottom:1rem}.td-sidebar{padding-bottom:1rem}@media(min-width:768px){.td-sidebar{padding-top:4rem;background-color:rgba(48,99,142,3%);padding-right:1rem;border-right:1px solid #dee2e6}}.td-sidebar__toggle{line-height:1;color:#212529;margin:1rem}.td-sidebar__search{padding:1rem 15px;margin-right:-15px;margin-left:-15px}.td-sidebar__inner{order:0}@media(min-width:768px){@supports(position:sticky){.td-sidebar__inner{position:sticky;top:4rem;z-index:10;height:calc(100vh - 6rem)}}}@media(min-width:1200px){.td-sidebar__inner{flex:0 1 320px}}.td-sidebar__inner .td-search-box{width:100%}.td-sidebar #content-desktop{display:block}.td-sidebar #content-mobile{display:none}@media(max-width:991.98px){.td-sidebar #content-desktop{display:none}.td-sidebar #content-mobile{display:block}}.td-sidebar-toc{border-left:1px solid #dee2e6;order:2;padding-top:.75rem;padding-bottom:1.5rem;vertical-align:top}@supports(position:sticky){.td-sidebar-toc{position:sticky;top:4rem;height:calc(100vh - 4rem);overflow-y:auto}}.td-page-meta a{display:block;font-weight:500}.td-toc a{display:block;font-weight:300;padding-bottom:.25rem}.td-toc li{list-style:none;display:block}.td-toc li li{margin-left:.5rem}.td-toc #TableOfContents a{color:#6c757d}.td-toc #TableOfContents a:hover{color:#0d6efd;text-decoration:none}.td-toc ul{padding-left:0}.btn,div.drawio button,.td-blog .td-rss-button{border-radius:1rem}.btn-lg,.btn-group-lg>.btn,div.drawio .btn-group-lg>button,.td-blog .td-rss-button{border-radius:2rem}.btn-sm,.btn-group-sm>.btn,div.drawio .btn-group-sm>button,.td-blog .btn-group-sm>.td-rss-button{border-radius:1rem}@media print{.td-breadcrumbs{display:none!important}}.td-breadcrumbs .breadcrumb{background:inherit;padding-left:0;padding-top:0}.alert{font-weight:500;background:#fff;color:inherit;border-radius:0}.alert-primary{border-style:solid;border-color:#5f7681;border-width:0 0 0 4px}.alert-primary .alert-heading{color:#5f7681}.alert-secondary{border-style:solid;border-color:#f58301;border-width:0 0 0 4px}.alert-secondary .alert-heading{color:#f58301}.alert-success{border-style:solid;border-color:#3772ff;border-width:0 0 0 4px}.alert-success .alert-heading{color:#3772ff}.alert-info{border-style:solid;border-color:#007ac0;border-width:0 0 0 4px}.alert-info .alert-heading{color:#007ac0}.alert-warning{border-style:solid;border-color:#e33030;border-width:0 0 0 4px}.alert-warning .alert-heading{color:#e33030}.alert-danger{border-style:solid;border-color:#ed6a5a;border-width:0 0 0 4px}.alert-danger .alert-heading{color:#ed6a5a}.alert-light{border-style:solid;border-color:#d3f3ee;border-width:0 0 0 4px}.alert-light .alert-heading{color:#d3f3ee}.alert-dark{border-style:solid;border-color:#333f47;border-width:0 0 0 4px}.alert-dark .alert-heading{color:#333f47}.td-content{order:1}.td-content p,.td-content li,.td-content td{font-weight:400}.td-content>h1,.td-content>.h1{font-weight:700;margin-bottom:1rem}.td-content>h2,.td-content>.h2{margin-bottom:1rem}.td-content>h2:not(:first-child),.td-content>.h2:not(:first-child){margin-top:3rem}.td-content>h2+h3,.td-content>.h2+h3,.td-content>h2+.h3,.td-content>h2+.td-footer__links-item,.td-content>.h2+.h3,.td-content>.h2+.td-footer__links-item{margin-top:1rem}.td-content>h3,.td-content>.h3,.td-content>.td-footer__links-item,.td-content>h4,.td-content>.h4,.td-content>h5,.td-content>.h5,.td-content>h6,.td-content>.h6{margin-bottom:1rem;margin-top:2rem}.td-content blockquote{padding:0 0 0 1rem;margin-bottom:1rem;color:#6c757d;border-left:6px solid #f58301}.td-content ul li,.td-content ol li{margin-bottom:.25rem}.td-content strong{font-weight:700}.td-content .alert:not(:first-child){margin-top:2rem;margin-bottom:2rem}.td-content .lead{margin-bottom:1.5rem}.td-title{margin-top:1rem;margin-bottom:.5rem}@media(min-width:576px){.td-title{font-size:3rem}}.td-search{background:0 0;position:relative;width:100%}.td-search__icon{display:flex;align-items:center;height:100%;position:absolute;left:.75em;pointer-events:none}.td-search__icon:before{content:"\f002"}.td-navbar .td-search__icon{color:rgba(255,255,255,.75)}.td-search__input{width:100%;text-indent:1.25em;border-radius:1rem}.td-search__input:not(:focus){background:0 0}.td-search__input.form-control:focus{border-color:#f7f8f9;box-shadow:0 0 0 2px #9fadb3;color:inherit}.td-navbar .td-search__input{border:none;color:rgba(255,255,255,.75)}.td-navbar .td-search__input::-webkit-input-placeholder{color:rgba(255,255,255,.75)}.td-navbar .td-search__input:-moz-placeholder{color:rgba(255,255,255,.75)}.td-navbar .td-search__input::-moz-placeholder{color:rgba(255,255,255,.75)}.td-navbar .td-search__input:-ms-input-placeholder{color:rgba(255,255,255,.75)}.td-search:focus-within .td-search__icon{display:none}.td-search:focus-within .td-search-input{text-indent:0}.td-search:not(:focus-within){color:#6c757d}.td-search--offline:focus-within .td-search__icon{display:flex;color:#6c757d}.td-offline-search-results{max-width:90%}.td-offline-search-results .card,.td-offline-search-results .td-content .highlight,.td-content .td-offline-search-results .highlight{margin-bottom:.5rem}.td-offline-search-results .card .card-header,.td-offline-search-results .td-content .highlight .card-header,.td-content .td-offline-search-results .highlight .card-header{font-weight:700}.td-offline-search-results__close-button{float:right}.td-offline-search-results__close-button:after{content:"\f00d"}.td-outer{display:flex;flex-direction:column;min-height:100vh}@media(min-width:768px){.td-default main>section:first-of-type{padding-top:8rem}}.td-main{flex-grow:1}.td-404 main,.td-main main{padding-top:1.5rem;padding-bottom:2rem}@media(min-width:768px){.td-404 main,.td-main main{padding-top:5.5rem}}.td-cover-block--height-min{min-height:300px}.td-cover-block--height-med{min-height:400px}.td-cover-block--height-max{min-height:500px}.td-cover-block--height-full{min-height:100vh}@media(min-width:768px){.td-cover-block--height-min{min-height:450px}.td-cover-block--height-med{min-height:500px}.td-cover-block--height-max{min-height:650px}}.td-cover-logo{margin-right:.5em}.td-cover-block{position:relative;padding-top:5rem;padding-bottom:5rem;background-repeat:no-repeat;background-position:50% 0;background-size:cover}.td-cover-block>.byline{position:absolute;bottom:2px;right:4px}.td-bg-arrow-wrapper{position:relative}.section-index .entry{padding:.75rem}.section-index h5,.section-index .h5{margin-bottom:0}.section-index h5 a,.section-index .h5 a{font-weight:700}.section-index p{margin-top:0}.pageinfo{font-weight:500;background:#f8f9fa;color:inherit;border-radius:0;margin:2rem;padding:1.5rem;padding-bottom:.5rem}.pageinfo-primary{border-style:solid;border-color:#5f7681}.pageinfo-secondary{border-style:solid;border-color:#f58301}.pageinfo-success{border-style:solid;border-color:#3772ff}.pageinfo-info{border-style:solid;border-color:#007ac0}.pageinfo-warning{border-style:solid;border-color:#e33030}.pageinfo-danger{border-style:solid;border-color:#ed6a5a}.pageinfo-light{border-style:solid;border-color:#d3f3ee}.pageinfo-dark{border-style:solid;border-color:#333f47}.taxonomy-terms-article{width:100%;clear:both;font-size:.8rem}.taxonomy-terms-article .taxonomy-title{display:inline;font-size:1.25em;height:1em;line-height:1em;margin-right:.5em;padding:0}.taxonomy-terms-cloud{width:100%;clear:both;font-size:.8rem}.taxonomy-terms-cloud .taxonomy-title{display:inline-block;width:100%;font-size:1rem;font-weight:700;color:#5f7681;border-bottom:1px #5f7681 solid;margin-bottom:1em;padding-bottom:.375rem;margin-top:1em}.taxonomy-terms-page{max-width:800px;margin:auto}.taxonomy-terms-page h1,.taxonomy-terms-page .h1{margin-bottom:1em}.taxonomy-terms-page .taxonomy-terms-cloud{font-size:1em}.taxonomy-terms-page .taxonomy-terms-cloud li{display:block}.taxonomy-terms-page .taxo-text-tags li+li::before{content:none}.taxonomy-terms-page .taxo-fruits .taxonomy-count,.taxonomy-terms-page .taxo-fruits .taxonomy-label{display:inherit;font-size:1rem;margin:0;padding:0;padding-right:.5em}.taxonomy-terms-page .taxo-fruits .taxonomy-count::before{content:"("}.taxonomy-terms-page .taxo-fruits .taxonomy-count::after{content:")"}.taxonomy-terms{list-style:none;margin:0;overflow:hidden;padding:0;display:inline}.taxonomy-terms li{display:inline;overflow-wrap:break-word;word-wrap:break-word;-ms-word-break:break-all;word-break:break-all;word-break:break-word;-webkit-hyphens:auto;hyphens:auto}.taxonomy-count{font-size:.8em;line-height:1.25em;display:inline-block;padding-left:.6em;padding-right:.6em;margin-left:.6em;text-align:center;border-radius:1em;background-color:#fff}.taxonomy-term{background:#e9ecef;border-width:0;border-radius:0 3px 3px 0;color:#6c757d;display:inline-block;font-size:1em;line-height:1.5em;min-height:1.5em;max-width:100%;padding:0 .5em 0 1em;position:relative;margin:0 .5em .2em 0;text-decoration:none;-webkit-transition:color .2s;-webkit-clip-path:polygon(100% 0,100% 100%,.8em 100%,0 50%,.8em 0);clip-path:polygon(100% 0,100% 100%,.8em 100%,0 50%,.8em 0)}.taxonomy-term:hover{background-color:#5f7681;color:#fff}.taxonomy-term:hover .taxonomy-count{color:#333f47!important}.taxonomy-term:hover::before{background:#5f7681}.taxo-text-tags .taxonomy-term{background:0 0;border-width:0;border-radius:0;color:#6c757d;font-size:1em;line-height:1.5em;min-height:1.5em;max-width:100%;padding:0;position:relative;margin:0;text-decoration:none;-webkit-clip-path:none;clip-path:none}.taxo-text-tags .taxonomy-term:hover{background:0 0;color:#0d6efd}.taxo-text-tags .taxonomy-term:hover .taxonomy-count{color:#333f47!important}.taxo-text-tags .taxonomy-term:hover::before{background:0 0}.taxo-text-tags li+li::before{content:"|";color:#6c757d;margin-right:.2em}.taxo-text-tags .taxonomy-count{font-size:1em;line-height:1.25em;display:inline-block;padding:0;margin:0;text-align:center;border-radius:0;background:0 0;vertical-align:super;font-size:.75em}.taxo-text-tags .taxonomy-term:hover .taxonomy-count{color:#0d6efd!important}.taxo-fruits .taxonomy-term[data-taxonomy-term]::before{font-style:normal;font-variant:normal;text-rendering:auto;-webkit-font-smoothing:antialiased;font-family:"font awesome 6 free";padding-right:.5em;font-size:2em;min-width:1.5em;display:inline-block}.taxo-fruits .taxonomy-term[data-taxonomy-term=apple]::before{content:"\f5d1";color:red}.taxo-fruits .taxonomy-term[data-taxonomy-term=carrot]::before{content:"\f787";color:orange}.taxo-fruits .taxonomy-term[data-taxonomy-term=lemon]::before{content:"\f094";color:#32cd32}.taxo-fruits .taxonomy-term[data-taxonomy-term=pepper]::before{content:"\f816";color:darkred}.taxo-fruits .taxonomy-term{background:0 0;border-width:0;border-radius:0;color:#6c757d;font-size:1em;line-height:2.5em;max-width:100%;padding:0;position:relative;margin:0;text-decoration:none;-webkit-clip-path:none;clip-path:none}.taxo-fruits .taxonomy-term:hover{background:0 0;color:#0d6efd}.taxo-fruits .taxonomy-term:hover .taxonomy-count{color:#333f47!important}.taxo-fruits .taxonomy-term:hover::before{background:0 0;text-shadow:0 0 3px #212529}.taxo-fruits .taxonomy-count,.taxo-fruits .taxonomy-label{display:none}.taxo-fruits.taxonomy-terms-article{margin-bottom:1rem}.taxo-fruits.taxonomy-terms-article .taxonomy-title{display:none}.taxonomy-taxonomy-page{max-width:800px;margin:auto}.taxonomy-taxonomy-page h1,.taxonomy-taxonomy-page .h1{margin-bottom:1em}.article-meta{margin-bottom:1.5rem}.article-teaser.article-type-docs h3 a:before,.article-teaser.article-type-docs .h3 a:before,.article-teaser.article-type-docs .td-footer__links-item a:before{display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;-webkit-font-smoothing:antialiased;font-family:"font awesome 6 free";content:"\f02d";padding-right:.5em}.article-teaser.article-type-blog h3 a:before,.article-teaser.article-type-blog .h3 a:before,.article-teaser.article-type-blog .td-footer__links-item a:before{display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;-webkit-font-smoothing:antialiased;font-family:"font awesome 6 free";content:"\f781";padding-right:.5em}.all-taxonomy-terms{font-weight:500;line-height:1.2;font-size:1.5rem}.all-taxonomy-terms:before{display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;-webkit-font-smoothing:antialiased;font-family:"font awesome 6 free";content:"\f122";padding-right:.5em}.article-teaser.card,.td-content .article-teaser.highlight{padding:1em;margin-bottom:1.5em}.article-teaser .breadcrumb{margin-bottom:0;font-size:.85rem}.article-teaser .article-meta{margin-bottom:0}div.drawio{display:inline-block;position:relative}div.drawio button{position:absolute;bottom:5px;right:5px;padding:.4em .5em;font-size:.8em;display:none}div.drawio:hover button{display:inline}div.drawioframe{position:fixed;height:100%;width:100%;top:0;left:0;z-index:1000;background:#000b;border:0}div.drawioframe iframe{position:absolute;height:90%;width:90%;top:5%;left:5%;z-index:1010}.tab-content .tab-pane pre{margin:0 0}.tab-content .tab-pane{margin-top:0;margin-bottom:1.5rem;max-width:80%;border-left:1px solid rgba(0,0,0,.125);border-right:1px solid rgba(0,0,0,.125);border-bottom:1px solid rgba(0,0,0,.125)}.tab-content .tab-pane .highlight{margin:0 0;border:none;max-width:100%}.tab-body{font-weight:500;background:#f8f9fa;color:inherit;border-radius:0;padding:1.5rem}.tab-body-primary{border-style:solid;border-color:#5f7681}.tab-body-secondary{border-style:solid;border-color:#f58301}.tab-body-success{border-style:solid;border-color:#3772ff}.tab-body-info{border-style:solid;border-color:#007ac0}.tab-body-warning{border-style:solid;border-color:#e33030}.tab-body-danger{border-style:solid;border-color:#ed6a5a}.tab-body-light{border-style:solid;border-color:#d3f3ee}.tab-body-dark{border-style:solid;border-color:#333f47}.td-card.card .highlight,.td-content .td-card.highlight .highlight{border:none;margin:0}.td-card .card-body.code{background-color:#f8f9fa;padding:0 0 0 1ex}.td-card .card-body pre{margin:0;padding:0 1rem 1rem}.td-footer{min-height:150px;padding-top:3rem}@media(max-width:991.98px){.td-footer{min-height:200px}}.td-footer__about{font-size:initial}.td-footer__links-list{margin-bottom:0}.td-footer__links-item a{color:inherit!important}@media(min-width:768px){.td-offset-anchor:target{display:block;position:relative;top:-4rem;visibility:hidden}h2[id]:before,[id].h2:before,h3[id]:before,[id].h3:before,[id].td-footer__links-item:before,h4[id]:before,[id].h4:before,h5[id]:before,[id].h5:before{display:block;content:" ";margin-top:-5rem;height:5rem;visibility:hidden}} \ No newline at end of file diff --git a/search/index.html b/search/index.html index 69e14ac8f8..707a458d2c 100644 --- a/search/index.html +++ b/search/index.html @@ -1,5 +1,5 @@ -Search Results | Community Health Toolkit -

    Search Results

    \ No newline at end of file + Create project issue

    Search Results

    \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml index 8e5253daa1..7d3afeabc5 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -1 +1 @@ -https://docs.communityhealthtoolkit.org/apps/concepts/access/2024-02-07T09:01:15-08:00https://docs.communityhealthtoolkit.org/apps/features/integrations/android/2024-05-09T16:20:33+02:00https://docs.communityhealthtoolkit.org/apps/reference/api/2024-07-01T08:10:53-05:00https://docs.communityhealthtoolkit.org/core/overview/architecture/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/contribute/code/core/dev-environment/2024-06-25T22:34:06-07:00https://docs.communityhealthtoolkit.org/core/overview/watchdog/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/design/personas/chw-janet/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/contribute/code/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/continuous-discovery-overview/2023-02-22T11:12:03+00:00https://docs.communityhealthtoolkit.org/apps/features/integrations/custom/2022-01-13T15:14:41-05:00https://docs.communityhealthtoolkit.org/apps/features/integrations/dhis2/2021-04-19T17:24:37-04:00https://docs.communityhealthtoolkit.org/2024-04-17T08:14:28+01:00https://docs.communityhealthtoolkit.org/contribute/docs/2020-06-22T21:46:00-04:00https://docs.communityhealthtoolkit.org/contribute/code/releasing/feature_releases/2023-06-02T12:58:08-07:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/focused-groups/2024-07-16T10:19:17-06:00https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/2020-05-30T19:36:05-04:00https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/design/personas/partners/implementers/2020-06-01T11:01:54-07:00https://docs.communityhealthtoolkit.org/apps/guides/data/analytics/introduction/2024-03-08T16:16:00+00:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/key-concepts/2023-05-16T13:11:09-07:00https://docs.communityhealthtoolkit.org/apps/examples/anc/2023-04-12T17:35:29+00:00https://docs.communityhealthtoolkit.org/design/icons/people_and_places/2020-05-30T19:36:05-04:00https://docs.communityhealthtoolkit.org/design/personas/2020-05-30T19:36:05-04:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/2023-02-22T11:12:03+00:00https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/product-team/2024-01-12T18:57:07+02:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/product-trio/product-trio-activities/2024-06-28T09:32:39-07:00https://docs.communityhealthtoolkit.org/hosting/requirements/2024-06-26T23:11:13-07:00https://docs.communityhealthtoolkit.org/running-programs/training/2024-07-03T11:24:10+03:00https://docs.communityhealthtoolkit.org/design/guides/designing-interviews/2022-04-28T12:11:31+03:00https://docs.communityhealthtoolkit.org/why-the-cht/2022-12-02T21:41:51-05:00https://docs.communityhealthtoolkit.org/contribute/docs/workflow/2023-04-12T10:00:06+00:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/about-interviews/2023-05-16T13:11:09-07:00https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/all-the-things/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/contribute/code/android/development-setup/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/design/best-practices/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/contribute/code/core/build-commands/2023-11-28T15:41:06-06:00https://docs.communityhealthtoolkit.org/core/overview/cht-conf/2024-07-23T08:50:07-06:00https://docs.communityhealthtoolkit.org/running-programs/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/core/overview/cht-sync/2024-07-23T08:50:07-06:00https://docs.communityhealthtoolkit.org/design/personas/chw-supervisor-ann/2020-05-30T19:36:05-04:00https://docs.communityhealthtoolkit.org/apps/concepts/2020-12-15T09:40:02-06:00https://docs.communityhealthtoolkit.org/apps/tutorials/couch2pg-setup/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/core/overview/db-schema/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/contribute/code/core/deploy-on-eks/2024-04-26T13:14:46+03:00https://docs.communityhealthtoolkit.org/apps/guides/data/hydration/2021-03-24T06:41:04+02:00https://docs.communityhealthtoolkit.org/apps/reference/forms/2020-10-19T08:47:55-07:00https://docs.communityhealthtoolkit.org/apps/guides/data/analytics/setup/2024-06-21T08:58:03+03:00https://docs.communityhealthtoolkit.org/design/personas/partners/local-governments/2020-06-01T11:01:54-07:00https://docs.communityhealthtoolkit.org/apps/concepts/navigation/2023-12-04T10:15:05+03:00https://docs.communityhealthtoolkit.org/core/overview/offline-first/2022-02-24T07:18:33+13:00https://docs.communityhealthtoolkit.org/apps/features/integrations/openmrs/2022-02-04T18:14:48-05:00https://docs.communityhealthtoolkit.org/core/overview/2020-05-30T19:36:05-04:00https://docs.communityhealthtoolkit.org/contribute/medic/product-core-competencies/2023-05-03T09:56:16+00:00https://docs.communityhealthtoolkit.org/core/overview/pwa/2022-04-08T10:38:39+12:00https://docs.communityhealthtoolkit.org/core/releases/2024-06-26T10:42:58+07:00https://docs.communityhealthtoolkit.org/apps/reference/resources/2021-07-16T08:15:33-05:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/schedule-of-events/2023-02-22T11:12:03+00:00https://docs.communityhealthtoolkit.org/apps/reference/translations/2023-05-16T17:55:12+02:00https://docs.communityhealthtoolkit.org/hosting/vertical-vs-horizontal/2024-06-21T07:57:58-07:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/adding-nuggets/2023-05-16T13:11:09-07:00https://docs.communityhealthtoolkit.org/apps/2023-11-21T01:04:00-08:00https://docs.communityhealthtoolkit.org/design/components/2023-05-19T14:14:18+00:00https://docs.communityhealthtoolkit.org/apps/tutorials/contact-and-users-1/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/core/overview/data-flows-for-analytics/2024-03-08T16:16:00+00:00https://docs.communityhealthtoolkit.org/design/guides/empathy-map/2022-04-19T07:42:28+03:00https://docs.communityhealthtoolkit.org/apps/features/2020-05-30T19:36:05-04:00https://docs.communityhealthtoolkit.org/apps/concepts/forms/2021-07-16T08:15:33-05:00https://docs.communityhealthtoolkit.org/hosting/kubernetes-vs-docker/2024-06-21T07:57:58-07:00https://docs.communityhealthtoolkit.org/design/personas/partners/national-governments/2020-06-01T11:01:54-07:00https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/2023-05-03T09:56:16+00:00https://docs.communityhealthtoolkit.org/apps/features/integrations/oppia/2020-12-17T15:05:43-08:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/product-trio/2023-02-22T11:12:03+00:00https://docs.communityhealthtoolkit.org/apps/guides/data/analytics/production/2024-05-20T16:48:06+00:00https://docs.communityhealthtoolkit.org/apps/features/integrations/rapidpro/2020-08-20T16:45:11-04:00https://docs.communityhealthtoolkit.org/design/personas/regional-manager-christina/2023-02-19T20:45:44+03:00https://docs.communityhealthtoolkit.org/contribute/code/android/releasing/2024-07-04T13:47:36+05:45https://docs.communityhealthtoolkit.org/contribute/tech-radar/2024-01-25T08:20:26+00:00https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/technical-resources/2024-06-20T19:03:33+03:00https://docs.communityhealthtoolkit.org/hosting/3.x/2024-06-21T07:57:58-07:00https://docs.communityhealthtoolkit.org/apps/concepts/care-guides/2020-09-16T20:06:56-07:00https://docs.communityhealthtoolkit.org/core/2023-11-21T01:04:00-08:00https://docs.communityhealthtoolkit.org/contribute/code-of-conduct/2023-09-27T11:58:54+03:00https://docs.communityhealthtoolkit.org/apps/features/contacts/2022-09-15T09:07:15+07:00https://docs.communityhealthtoolkit.org/apps/tutorials/contact-and-users-2/2023-03-29T07:55:19-05:00https://docs.communityhealthtoolkit.org/apps/guides/data/analytics/environment-variables/2024-06-20T19:03:33+03:00https://docs.communityhealthtoolkit.org/apps/examples/2020-09-22T16:00:38-05:00https://docs.communityhealthtoolkit.org/design/icons/2021-07-16T08:15:33-05:00https://docs.communityhealthtoolkit.org/design/personas/nurse-mary/2020-05-30T19:36:05-04:00https://docs.communityhealthtoolkit.org/design/guides/problem-statement/2022-04-19T07:42:28+03:00https://docs.communityhealthtoolkit.org/apps/guides/android/publishing/2024-06-25T22:34:06-07:00https://docs.communityhealthtoolkit.org/contribute/code/releasing/publish-docker-image/2023-05-16T10:46:37-05:00https://docs.communityhealthtoolkit.org/core/overview/transitions/2023-06-14T09:53:43+03:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/synthesizing-nuggets/2023-05-16T13:11:09-07:00https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/team-meetings/2024-04-16T10:19:29+00:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/2023-02-22T11:12:03+00:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/accept_case_reports/2020-06-27T01:28:28-04:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/assetlinks/2024-05-09T16:20:33+02:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/hierarchy/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/dhis2/2020-07-09T16:39:14-07:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/forms/2022-05-03T07:56:17+05:45https://docs.communityhealthtoolkit.org/apps/reference/app-settings/header_tabs/2020-07-03T11:25:34+03:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/outbound/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/patient_reports/2021-03-24T06:41:04+02:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-permissions/2024-06-21T12:15:34-04:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/registrations/2021-11-24T07:30:29+05:45https://docs.communityhealthtoolkit.org/apps/reference/app-settings/reminders/2020-07-14T15:02:10+12:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/replication_depth/2020-07-13T16:54:46+03:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/replications/2020-10-02T12:20:59-04:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-roles/2020-06-04T15:06:42-04:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/schedules/2023-10-25T18:04:48-04:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/sms/2022-11-24T11:08:37+13:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/token_login/2020-07-28T02:48:35+03:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/transitions/2024-05-30T15:35:13-04:00https://docs.communityhealthtoolkit.org/hosting/4.x/2024-06-21T07:57:58-07:00https://docs.communityhealthtoolkit.org/apps/reference/forms/app/2024-07-15T09:08:01+02:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/contribute/medic/2023-09-27T11:58:54+03:00https://docs.communityhealthtoolkit.org/apps/reference/forms/collect/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/reference/forms/contact/2024-01-15T13:28:55+02:00https://docs.communityhealthtoolkit.org/apps/reference/contact-page/2024-04-24T13:18:24+03:00https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/daily-updates/2024-06-19T08:39:53-06:00https://docs.communityhealthtoolkit.org/design/personas/data-manager-paul/2020-05-30T19:36:05-04:00https://docs.communityhealthtoolkit.org/design/2020-06-01T11:16:07-07:00https://docs.communityhealthtoolkit.org/apps/reference/extension-libs/2023-05-08T06:38:08+12:00https://docs.communityhealthtoolkit.org/design/guides/mapping-hierarchy/2022-04-19T07:42:28+03:00https://docs.communityhealthtoolkit.org/apps/features/messaging/2020-09-21T15:56:39-07:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/publishing-insights/2023-05-16T13:11:09-07:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/quality-assistance/2023-02-22T11:12:03+00:00https://docs.communityhealthtoolkit.org/design/guides/2021-04-15T13:04:40+03:00https://docs.communityhealthtoolkit.org/apps/tutorials/sms-forms/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/reference/targets/2022-12-06T01:01:56-05:00https://docs.communityhealthtoolkit.org/apps/reference/tasks/2023-04-20T11:51:49+12:00https://docs.communityhealthtoolkit.org/core/overview/translations/2024-07-09T09:26:15+02:00https://docs.communityhealthtoolkit.org/apps/tutorials/2020-06-03T10:27:10-07:00https://docs.communityhealthtoolkit.org/apps/guides/forms/uhc-mode/2024-07-26T10:34:32-04:00https://docs.communityhealthtoolkit.org/apps/concepts/workflows/2020-12-10T15:03:59-05:00https://docs.communityhealthtoolkit.org/design/personas/app-builder/2020-05-30T19:36:05-04:00https://docs.communityhealthtoolkit.org/apps/tutorials/application-settings/2024-03-28T18:22:13+00:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/code-health/2023-04-04T10:38:58+12:00https://docs.communityhealthtoolkit.org/apps/concepts/hierarchy/2024-05-24T16:44:04+00:00https://docs.communityhealthtoolkit.org/hosting/2024-06-26T23:11:13-07:00https://docs.communityhealthtoolkit.org/hosting/monitoring/2024-06-21T07:57:58-07:00https://docs.communityhealthtoolkit.org/apps/tutorials/multi-facility-users/2024-07-30T14:57:50+03:00https://docs.communityhealthtoolkit.org/apps/guides/2020-06-03T14:28:04-04:00https://docs.communityhealthtoolkit.org/apps/features/reports/2023-04-12T17:35:29+00:00https://docs.communityhealthtoolkit.org/apps/tutorials/sms-schedules/2021-07-16T08:15:33-05:00https://docs.communityhealthtoolkit.org/contribute/code/workflow/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/tutorials/app-forms/2024-01-10T20:47:25+07:00https://docs.communityhealthtoolkit.org/apps/reference/2024-04-17T08:14:28+01:00https://docs.communityhealthtoolkit.org/contribute/code/releasing/2023-04-18T12:42:52+00:00https://docs.communityhealthtoolkit.org/apps/features/supervision/2024-06-21T12:15:34-04:00https://docs.communityhealthtoolkit.org/apps/features/tasks/2022-07-19T14:00:29-04:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/transparency-accountability/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/concepts/users/2022-01-13T15:14:41-05:00https://docs.communityhealthtoolkit.org/contribute/code/repository-checklist/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/contribute/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/tutorials/form-properties/2021-07-16T08:15:33-05:00https://docs.communityhealthtoolkit.org/apps/concepts/interoperability/2024-05-31T15:56:31+03:00https://docs.communityhealthtoolkit.org/design/personas/partners/2020-06-01T11:16:07-07:00https://docs.communityhealthtoolkit.org/apps/features/targets/2023-12-13T07:55:15+03:00https://docs.communityhealthtoolkit.org/contribute/code/core/update-dependencies/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/concepts/prerequisites/2023-05-14T21:33:28+03:00https://docs.communityhealthtoolkit.org/contribute/code/style-guide/2023-11-28T15:41:06-06:00https://docs.communityhealthtoolkit.org/apps/tutorials/tasks-1/2022-04-25T14:22:54+12:00https://docs.communityhealthtoolkit.org/apps/features/training/2023-04-11T06:24:09+07:00https://docs.communityhealthtoolkit.org/hosting/3.x/ec2-setup-guide/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/contribute/code/cht-conf/2024-07-23T08:50:07-06:00https://docs.communityhealthtoolkit.org/contribute/code/core/2022-12-13T09:17:47+13:00https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/2021-11-08T18:53:31-03:00https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/configuration/2022-12-13T09:17:47+13:00https://docs.communityhealthtoolkit.org/hosting/4.x/data-migration/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/glossary/2024-06-13T12:21:44-07:00https://docs.communityhealthtoolkit.org/apps/features/muting/2023-03-09T15:46:54+07:00https://docs.communityhealthtoolkit.org/core/overview/roadmap/2023-05-14T14:06:36-07:00https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/single-node/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/contribute/docs/style-guide/2024-07-09T09:26:15+02:00https://docs.communityhealthtoolkit.org/apps/tutorials/targets/2022-02-08T10:50:49+03:00https://docs.communityhealthtoolkit.org/contribute/code/using-npm/2024-05-16T09:09:00+00:00https://docs.communityhealthtoolkit.org/apps/tutorials/contact-summary/2021-07-16T08:15:33-05:00https://docs.communityhealthtoolkit.org/apps/tutorials/death-reporting/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/contribute/code/static-analysis/2024-07-08T10:32:46+03:00https://docs.communityhealthtoolkit.org/apps/features/uhc-mode/2023-03-09T15:46:54+07:00https://docs.communityhealthtoolkit.org/apps/features/admin/2023-03-09T15:46:54+07:00https://docs.communityhealthtoolkit.org/contribute/code/core/automated-tests/2024-07-29T19:01:09+07:00https://docs.communityhealthtoolkit.org/apps/tutorials/condition-cards/2023-03-09T11:48:26+03:00https://docs.communityhealthtoolkit.org/contribute/code/design-docs/2024-07-17T17:07:07+03:00https://docs.communityhealthtoolkit.org/contribute/code/core/style-guide-automated-e2e-tests/2024-07-19T09:45:38-06:00https://docs.communityhealthtoolkit.org/apps/guides/android/branding/2024-07-26T09:00:42+00:00https://docs.communityhealthtoolkit.org/apps/tutorials/localizing-cht/2024-01-15T13:28:55+02:00https://docs.communityhealthtoolkit.org/apps/tutorials/application-graphics/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/tutorials/application-tests/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/data/users-bulk-load/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/data/impact-metrics/2024-03-08T16:16:00+00:00https://docs.communityhealthtoolkit.org/apps/guides/training/onboarding/2023-03-09T15:46:54+07:00https://docs.communityhealthtoolkit.org/apps/guides/training/training-cards/2023-04-11T06:24:09+07:00https://docs.communityhealthtoolkit.org/apps/guides/training/training-cards-resources/2023-05-12T08:24:25+07:00https://docs.communityhealthtoolkit.org/apps/guides/data/training-instance/2023-03-09T15:46:54+07:00https://docs.communityhealthtoolkit.org/contribute/code/core/using-windows/2023-08-21T14:01:53+12:00https://docs.communityhealthtoolkit.org/apps/guides/data/invalid-reports/2024-03-08T16:16:00+00:00https://docs.communityhealthtoolkit.org/contribute/code/core/run-multiple-chrome-versions/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/data/csv-to-docs/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/africas-talking/2022-07-22T08:43:15+12:00https://docs.communityhealthtoolkit.org/contribute/code/android/2022-12-13T09:17:47+13:00https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/2021-05-18T17:45:42-04:00https://docs.communityhealthtoolkit.org/apps/features/integrations/2023-03-09T15:46:54+07:00https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/multiple-nodes/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/self-hosting-k3s-multinode/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/phones/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/rapidpro/2022-12-08T13:31:35+01:00https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/rapidpro_cht_gateway/2024-04-29T23:46:36+01:00https://docs.communityhealthtoolkit.org/hosting/3.x/self-hosting/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/apps/guides/messaging/sms-states/2021-11-08T18:53:31-03:00https://docs.communityhealthtoolkit.org/apps/tutorials/user-management-tool/2024-07-25T23:05:14-07:00https://docs.communityhealthtoolkit.org/apps/guides/messaging/shortcodes/2021-05-18T17:45:42-04:00https://docs.communityhealthtoolkit.org/hosting/3.x/app-developer/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/hosting/4.x/app-developer/2024-07-15T22:51:14-07:00https://docs.communityhealthtoolkit.org/contribute/code/hall-of-fame/2024-05-30T07:18:29+02:00https://docs.communityhealthtoolkit.org/hosting/3.x/ssl-cert-install/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/messaging/message-loops/2021-05-18T17:45:42-04:00https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/troubleshooting/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/privacy/privacy-policy/2024-05-29T14:32:42+00:00https://docs.communityhealthtoolkit.org/apps/guides/data/2020-06-05T14:27:01-04:00https://docs.communityhealthtoolkit.org/apps/guides/database/2024-04-18T13:46:39+01:00https://docs.communityhealthtoolkit.org/apps/guides/debugging/2022-02-16T12:15:00+13:00https://docs.communityhealthtoolkit.org/apps/guides/forms/2020-06-05T14:27:01-04:00https://docs.communityhealthtoolkit.org/apps/guides/integrations/2022-07-05T04:16:58+03:00https://docs.communityhealthtoolkit.org/hosting/monitoring/introduction/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/apps/guides/messaging/2020-06-05T14:27:01-04:00https://docs.communityhealthtoolkit.org/hosting/3.x/offline/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/apps/guides/performance/2020-06-05T14:27:01-04:00https://docs.communityhealthtoolkit.org/apps/guides/privacy/2021-04-19T17:19:45-07:00https://docs.communityhealthtoolkit.org/apps/guides/security/2020-06-05T14:27:01-04:00https://docs.communityhealthtoolkit.org/hosting/monitoring/setup/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/tasks/2022-01-07T17:06:14-05:00https://docs.communityhealthtoolkit.org/hosting/4.x/adding-tls-certificates/2024-05-06T23:25:28-07:00https://docs.communityhealthtoolkit.org/apps/guides/training/2023-03-09T15:46:54+07:00https://docs.communityhealthtoolkit.org/apps/guides/updates/2020-06-05T14:27:01-04:00https://docs.communityhealthtoolkit.org/apps/guides/android/2022-12-13T09:17:47+13:00https://docs.communityhealthtoolkit.org/apps/guides/data/analytics/2024-03-08T16:16:00+00:00https://docs.communityhealthtoolkit.org/hosting/monitoring/production/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/hosting/4.x/logs/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/hosting/4.x/backups/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/hosting/monitoring/postgres-ingest/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/hosting/monitoring/integration/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/search/2022-07-27T20:41:57-05:00https://docs.communityhealthtoolkit.org/core/releases/0.4.15-and-earlier/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/core/releases/2.10.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.10.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.10.2/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.10.3/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.11.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.11.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.11.2/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.11.3/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.12.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.12.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.12.2/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.12.3/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.12.4/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.12.5/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.13.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.13.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.13.2/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.13.3/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.13.4/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.13.5/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.13.6/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.13.7/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.14.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.14.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.14.2/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.14.3/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.15.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.16.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.16.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.17.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.18.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.18.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.6.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.6.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.6.2/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.6.3/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.7.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.7.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.7.2/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.7.3/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.8.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.8.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.8.2/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.8.3/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.8.4/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.8.5/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.9.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.9.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/3.0.0/2022-02-21T09:32:58-06:00https://docs.communityhealthtoolkit.org/core/releases/3.1.0/2022-02-21T09:32:58-06:00https://docs.communityhealthtoolkit.org/core/releases/3.10.0/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.10.1/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.10.2/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.10.3/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.10.4/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.10.5/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.11.0/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/core/releases/3.11.1/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.11.2/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.11.3/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.12.0/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.12.1/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.13.0/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.14.0/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.14.1/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.14.2/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.15.0/2022-05-05T09:06:44+12:00https://docs.communityhealthtoolkit.org/core/releases/3.16.0/2022-08-10T08:20:54-05:00https://docs.communityhealthtoolkit.org/core/releases/3.16.1/2022-12-02T09:34:55+02:00https://docs.communityhealthtoolkit.org/core/releases/3.17.0/2022-10-11T09:43:26+07:00https://docs.communityhealthtoolkit.org/core/releases/3.17.1/2022-12-02T09:35:08+02:00https://docs.communityhealthtoolkit.org/core/releases/3.17.2/2023-10-10T19:56:20+13:00https://docs.communityhealthtoolkit.org/core/releases/3.2.0/2022-10-31T16:02:09-04:00https://docs.communityhealthtoolkit.org/core/releases/3.2.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/3.3.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/3.4.0/2022-01-14T11:19:07-05:00https://docs.communityhealthtoolkit.org/core/releases/3.4.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/3.5.0/2022-01-14T11:19:07-05:00https://docs.communityhealthtoolkit.org/core/releases/3.6.0/2022-01-14T11:19:07-05:00https://docs.communityhealthtoolkit.org/core/releases/3.6.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/3.6.2/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/3.7.0/2022-08-01T16:02:39+12:00https://docs.communityhealthtoolkit.org/core/releases/3.7.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/3.8.0/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/core/releases/3.8.1/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/core/releases/3.8.2/2022-01-14T11:19:07-05:00https://docs.communityhealthtoolkit.org/core/releases/3.9.0/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/core/releases/3.9.1/2022-01-14T11:19:07-05:00https://docs.communityhealthtoolkit.org/core/releases/3.9.2/2022-01-14T11:19:07-05:00https://docs.communityhealthtoolkit.org/core/releases/4.0.0/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.0.1/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.1.0/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.1.1/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.1.2/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.2.0/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.2.1/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.2.2/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.2.3/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.2.4/2023-10-10T19:56:20+13:00https://docs.communityhealthtoolkit.org/core/releases/4.3.0/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.3.1/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.3.2/2023-10-10T19:56:20+13:00https://docs.communityhealthtoolkit.org/core/releases/4.4.0/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.4.1/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.4.2/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.5.0/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.5.1/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.5.2/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.6.0/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.7.0/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.7.1/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.7.2/2024-06-20T11:15:30+03:00https://docs.communityhealthtoolkit.org/core/releases/4.8.0/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.8.1/2024-06-20T11:15:30+03:00https://docs.communityhealthtoolkit.org/core/releases/4.9.0/2024-07-08T21:49:46-07:00https://docs.communityhealthtoolkit.org/apps/guides/forms/additional-docs/2022-06-17T14:30:45-05:00https://docs.communityhealthtoolkit.org/apps/guides/forms/app-form-sms/2023-06-27T05:11:38+05:45https://docs.communityhealthtoolkit.org/apps/guides/debugging/obtaining-logs/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/apps/reference/_partial_cht_api/2023-04-06T14:38:13-04:00https://docs.communityhealthtoolkit.org/apps/tutorials/tasks-2/2023-04-06T14:38:13-04:00https://docs.communityhealthtoolkit.org/apps/guides/database/muting_in_dashboards/2021-10-08T08:43:53+03:00https://docs.communityhealthtoolkit.org/apps/examples/contact-tracing/2020-09-23T18:04:11-04:00https://docs.communityhealthtoolkit.org/apps/guides/database/couch2pg-oom-errors/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/apps/guides/database/couchdb-authentication/2022-11-22T08:13:16+13:00https://docs.communityhealthtoolkit.org/apps/examples/phc-covid/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/examples/covid-education/2021-01-11T16:12:16-05:00https://docs.communityhealthtoolkit.org/apps/examples/covid-rdt-reference-app/2021-09-27T06:46:22-07:00https://docs.communityhealthtoolkit.org/apps/guides/database/database-conflicts/2024-05-17T10:49:48+05:45https://docs.communityhealthtoolkit.org/apps/guides/integrations/dhis2-aggregate/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/examples/direct-to-client/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/hosting/4.x/_partial_docker_directories/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/apps/examples/ebs/2020-09-23T18:04:11-04:00https://docs.communityhealthtoolkit.org/apps/guides/updates/feature-flags/2023-05-04T10:39:42-04:00https://docs.communityhealthtoolkit.org/apps/guides/forms/google-drive/2021-07-16T08:15:33-05:00https://docs.communityhealthtoolkit.org/apps/guides/forms/form-inputs/2024-04-09T09:47:10-05:00https://docs.communityhealthtoolkit.org/apps/examples/learning-care/2021-03-03T16:27:50-05:00https://docs.communityhealthtoolkit.org/apps/tutorials/_partial_docker_setup/2023-10-06T16:32:03+02:00https://docs.communityhealthtoolkit.org/apps/examples/interoperability/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/privacy/policy/2023-06-29T09:58:29+12:00https://docs.communityhealthtoolkit.org/design/mockups/2023-03-10T03:00:38+03:00https://docs.communityhealthtoolkit.org/apps/guides/updates/moving-contacts/2021-10-22T17:25:03-04:00https://docs.communityhealthtoolkit.org/apps/guides/forms/multimedia/2022-06-17T14:30:45-05:00https://docs.communityhealthtoolkit.org/apps/guides/integrations/openmrs/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/integrations/oppia/2021-07-16T08:15:33-05:00https://docs.communityhealthtoolkit.org/apps/guides/tasks/pass-data-to-form/2023-04-06T14:38:13-04:00https://docs.communityhealthtoolkit.org/apps/examples/pharmacovigilance-reference-app/2024-01-16T12:23:53+03:00https://docs.communityhealthtoolkit.org/apps/examples/pih/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/updates/preparing-for-4/2024-06-25T22:34:06-07:00https://docs.communityhealthtoolkit.org/apps/guides/debugging/replicating-production-data-locally/2023-08-21T14:01:53+12:00https://docs.communityhealthtoolkit.org/apps/guides/performance/purging/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/database/querying_apdex_telemetry/2024-03-29T11:37:33+07:00https://docs.communityhealthtoolkit.org/apps/guides/tasks/query-task-data/2021-11-29T11:05:14-08:00https://docs.communityhealthtoolkit.org/apps/guides/forms/wealth-quintiles/2020-12-16T14:58:49-05:00https://docs.communityhealthtoolkit.org/apps/guides/integrations/rapidpro/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/database/rdbms-from-mac/2024-01-25T09:05:05+03:00https://docs.communityhealthtoolkit.org/apps/guides/database/rdbms-from-windows/2020-08-18T10:29:41-07:00https://docs.communityhealthtoolkit.org/apps/guides/updates/collect-forms-update/2021-07-16T08:15:33-05:00https://docs.communityhealthtoolkit.org/apps/examples/training/2023-03-09T15:46:54+07:00https://docs.communityhealthtoolkit.org/apps/guides/performance/replication/2024-06-21T12:15:34-04:00https://docs.communityhealthtoolkit.org/apps/guides/forms/report-titles/2020-06-05T14:27:01-04:00https://docs.communityhealthtoolkit.org/apps/guides/security/securely-onboarding-users-at-scale/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/debugging/secure-sharing-of-developer-instance/2022-12-13T09:17:47+13:00https://docs.communityhealthtoolkit.org/apps/guides/security/securing-android/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/apps/guides/debugging/sharing-4x-logs/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/apps/examples/stock-monitoring/2022-08-31T13:55:06-07:00https://docs.communityhealthtoolkit.org/apps/examples/supervisor-reference-app/2023-03-18T15:29:21+03:00https://docs.communityhealthtoolkit.org/apps/guides/tasks/task-schema-parameters/2021-11-29T11:05:14-08:00https://docs.communityhealthtoolkit.org/apps/guides/performance/telemetry/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/_partial_upgrade_service/2024-05-24T15:46:35-05:00https://docs.communityhealthtoolkit.org/apps/reference/_partial_utils/2023-04-06T14:38:13-04:00https://docs.communityhealthtoolkit.org/apps/guides/forms/versioning/2022-08-05T10:25:51+12:00 \ No newline at end of file +https://docs.communityhealthtoolkit.org/apps/concepts/access/2024-02-07T09:01:15-08:00https://docs.communityhealthtoolkit.org/apps/features/integrations/android/2024-05-09T16:20:33+02:00https://docs.communityhealthtoolkit.org/apps/reference/api/2024-07-01T08:10:53-05:00https://docs.communityhealthtoolkit.org/core/overview/architecture/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/contribute/code/core/dev-environment/2024-08-02T00:53:01-07:00https://docs.communityhealthtoolkit.org/core/overview/watchdog/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/design/personas/chw-janet/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/contribute/code/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/continuous-discovery-overview/2023-02-22T11:12:03+00:00https://docs.communityhealthtoolkit.org/apps/features/integrations/custom/2022-01-13T15:14:41-05:00https://docs.communityhealthtoolkit.org/apps/features/integrations/dhis2/2021-04-19T17:24:37-04:00https://docs.communityhealthtoolkit.org/2024-08-02T00:53:01-07:00https://docs.communityhealthtoolkit.org/contribute/docs/2020-06-22T21:46:00-04:00https://docs.communityhealthtoolkit.org/contribute/code/releasing/feature_releases/2023-06-02T12:58:08-07:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/focused-groups/2024-07-16T10:19:17-06:00https://docs.communityhealthtoolkit.org/design/icons/forms_tasks_targets/2020-05-30T19:36:05-04:00https://docs.communityhealthtoolkit.org/apps/tutorials/local-setup/2024-08-02T00:53:01-07:00https://docs.communityhealthtoolkit.org/design/personas/partners/implementers/2020-06-01T11:01:54-07:00https://docs.communityhealthtoolkit.org/apps/guides/data/analytics/introduction/2024-03-08T16:16:00+00:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/key-concepts/2023-05-16T13:11:09-07:00https://docs.communityhealthtoolkit.org/apps/examples/anc/2023-04-12T17:35:29+00:00https://docs.communityhealthtoolkit.org/design/icons/people_and_places/2020-05-30T19:36:05-04:00https://docs.communityhealthtoolkit.org/design/personas/2020-05-30T19:36:05-04:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/2023-02-22T11:12:03+00:00https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/product-team/2024-01-12T18:57:07+02:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/product-trio/product-trio-activities/2024-06-28T09:32:39-07:00https://docs.communityhealthtoolkit.org/hosting/requirements/2024-06-26T23:11:13-07:00https://docs.communityhealthtoolkit.org/running-programs/training/2024-07-03T11:24:10+03:00https://docs.communityhealthtoolkit.org/design/guides/designing-interviews/2022-04-28T12:11:31+03:00https://docs.communityhealthtoolkit.org/why-the-cht/2022-12-02T21:41:51-05:00https://docs.communityhealthtoolkit.org/contribute/docs/workflow/2023-04-12T10:00:06+00:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/about-interviews/2023-05-16T13:11:09-07:00https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/all-the-things/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/contribute/code/android/development-setup/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/design/best-practices/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/contribute/code/core/build-commands/2023-11-28T15:41:06-06:00https://docs.communityhealthtoolkit.org/core/overview/cht-conf/2024-07-23T08:50:07-06:00https://docs.communityhealthtoolkit.org/running-programs/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/core/overview/cht-sync/2024-07-23T08:50:07-06:00https://docs.communityhealthtoolkit.org/design/personas/chw-supervisor-ann/2020-05-30T19:36:05-04:00https://docs.communityhealthtoolkit.org/apps/concepts/2020-12-15T09:40:02-06:00https://docs.communityhealthtoolkit.org/apps/tutorials/couch2pg-setup/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/core/overview/db-schema/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/contribute/code/core/deploy-on-eks/2024-04-26T13:14:46+03:00https://docs.communityhealthtoolkit.org/apps/guides/data/hydration/2021-03-24T06:41:04+02:00https://docs.communityhealthtoolkit.org/apps/reference/forms/2020-10-19T08:47:55-07:00https://docs.communityhealthtoolkit.org/apps/guides/data/analytics/setup/2024-06-21T08:58:03+03:00https://docs.communityhealthtoolkit.org/design/personas/partners/local-governments/2020-06-01T11:01:54-07:00https://docs.communityhealthtoolkit.org/apps/concepts/navigation/2023-12-04T10:15:05+03:00https://docs.communityhealthtoolkit.org/core/overview/offline-first/2022-02-24T07:18:33+13:00https://docs.communityhealthtoolkit.org/apps/features/integrations/openmrs/2022-02-04T18:14:48-05:00https://docs.communityhealthtoolkit.org/core/overview/2020-05-30T19:36:05-04:00https://docs.communityhealthtoolkit.org/contribute/medic/product-core-competencies/2023-05-03T09:56:16+00:00https://docs.communityhealthtoolkit.org/core/overview/pwa/2022-04-08T10:38:39+12:00https://docs.communityhealthtoolkit.org/core/releases/2024-06-26T10:42:58+07:00https://docs.communityhealthtoolkit.org/apps/reference/resources/2021-07-16T08:15:33-05:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/schedule-of-events/2023-02-22T11:12:03+00:00https://docs.communityhealthtoolkit.org/apps/reference/translations/2023-05-16T17:55:12+02:00https://docs.communityhealthtoolkit.org/hosting/vertical-vs-horizontal/2024-08-02T00:53:01-07:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/adding-nuggets/2023-05-16T13:11:09-07:00https://docs.communityhealthtoolkit.org/apps/2023-11-21T01:04:00-08:00https://docs.communityhealthtoolkit.org/design/components/2023-05-19T14:14:18+00:00https://docs.communityhealthtoolkit.org/apps/tutorials/contact-and-users-1/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/core/overview/data-flows-for-analytics/2024-03-08T16:16:00+00:00https://docs.communityhealthtoolkit.org/design/guides/empathy-map/2022-04-19T07:42:28+03:00https://docs.communityhealthtoolkit.org/apps/features/2020-05-30T19:36:05-04:00https://docs.communityhealthtoolkit.org/apps/concepts/forms/2021-07-16T08:15:33-05:00https://docs.communityhealthtoolkit.org/hosting/kubernetes-vs-docker/2024-06-21T07:57:58-07:00https://docs.communityhealthtoolkit.org/design/personas/partners/national-governments/2020-06-01T11:01:54-07:00https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/2023-05-03T09:56:16+00:00https://docs.communityhealthtoolkit.org/apps/features/integrations/oppia/2020-12-17T15:05:43-08:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/product-trio/2023-02-22T11:12:03+00:00https://docs.communityhealthtoolkit.org/apps/guides/data/analytics/production/2024-05-20T16:48:06+00:00https://docs.communityhealthtoolkit.org/apps/features/integrations/rapidpro/2020-08-20T16:45:11-04:00https://docs.communityhealthtoolkit.org/design/personas/regional-manager-christina/2023-02-19T20:45:44+03:00https://docs.communityhealthtoolkit.org/contribute/code/android/releasing/2024-07-04T13:47:36+05:45https://docs.communityhealthtoolkit.org/contribute/tech-radar/2024-01-25T08:20:26+00:00https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/technical-resources/2024-06-20T19:03:33+03:00https://docs.communityhealthtoolkit.org/hosting/3.x/2024-06-21T07:57:58-07:00https://docs.communityhealthtoolkit.org/apps/concepts/care-guides/2020-09-16T20:06:56-07:00https://docs.communityhealthtoolkit.org/core/2023-11-21T01:04:00-08:00https://docs.communityhealthtoolkit.org/contribute/code-of-conduct/2023-09-27T11:58:54+03:00https://docs.communityhealthtoolkit.org/apps/features/contacts/2022-09-15T09:07:15+07:00https://docs.communityhealthtoolkit.org/apps/tutorials/contact-and-users-2/2023-03-29T07:55:19-05:00https://docs.communityhealthtoolkit.org/apps/guides/data/analytics/environment-variables/2024-06-20T19:03:33+03:00https://docs.communityhealthtoolkit.org/apps/examples/2020-09-22T16:00:38-05:00https://docs.communityhealthtoolkit.org/design/icons/2021-07-16T08:15:33-05:00https://docs.communityhealthtoolkit.org/design/personas/nurse-mary/2020-05-30T19:36:05-04:00https://docs.communityhealthtoolkit.org/design/guides/problem-statement/2022-04-19T07:42:28+03:00https://docs.communityhealthtoolkit.org/apps/guides/android/publishing/2024-06-25T22:34:06-07:00https://docs.communityhealthtoolkit.org/contribute/code/releasing/publish-docker-image/2023-05-16T10:46:37-05:00https://docs.communityhealthtoolkit.org/core/overview/transitions/2023-06-14T09:53:43+03:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/synthesizing-nuggets/2023-05-16T13:11:09-07:00https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/team-meetings/2024-04-16T10:19:29+00:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/2023-02-22T11:12:03+00:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/accept_case_reports/2020-06-27T01:28:28-04:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/assetlinks/2024-05-09T16:20:33+02:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/hierarchy/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/dhis2/2020-07-09T16:39:14-07:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/forms/2022-05-03T07:56:17+05:45https://docs.communityhealthtoolkit.org/apps/reference/app-settings/header_tabs/2020-07-03T11:25:34+03:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/outbound/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/patient_reports/2021-03-24T06:41:04+02:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-permissions/2024-06-21T12:15:34-04:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/registrations/2021-11-24T07:30:29+05:45https://docs.communityhealthtoolkit.org/apps/reference/app-settings/reminders/2020-07-14T15:02:10+12:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/replication_depth/2020-07-13T16:54:46+03:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/replications/2020-10-02T12:20:59-04:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/user-roles/2020-06-04T15:06:42-04:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/schedules/2023-10-25T18:04:48-04:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/sms/2022-11-24T11:08:37+13:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/token_login/2020-07-28T02:48:35+03:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/transitions/2024-05-30T15:35:13-04:00https://docs.communityhealthtoolkit.org/hosting/4.x/2024-06-21T07:57:58-07:00https://docs.communityhealthtoolkit.org/apps/reference/forms/app/2024-07-15T09:08:01+02:00https://docs.communityhealthtoolkit.org/apps/reference/app-settings/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/contribute/medic/2023-09-27T11:58:54+03:00https://docs.communityhealthtoolkit.org/apps/reference/forms/collect/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/reference/forms/contact/2024-01-15T13:28:55+02:00https://docs.communityhealthtoolkit.org/apps/reference/contact-page/2024-04-24T13:18:24+03:00https://docs.communityhealthtoolkit.org/contribute/medic/onboarding/daily-updates/2024-06-19T08:39:53-06:00https://docs.communityhealthtoolkit.org/design/personas/data-manager-paul/2020-05-30T19:36:05-04:00https://docs.communityhealthtoolkit.org/design/2020-06-01T11:16:07-07:00https://docs.communityhealthtoolkit.org/apps/reference/extension-libs/2023-05-08T06:38:08+12:00https://docs.communityhealthtoolkit.org/design/guides/mapping-hierarchy/2022-04-19T07:42:28+03:00https://docs.communityhealthtoolkit.org/apps/features/messaging/2020-09-21T15:56:39-07:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/ux-research-repo/publishing-insights/2023-05-16T13:11:09-07:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/quality-assistance/2023-02-22T11:12:03+00:00https://docs.communityhealthtoolkit.org/design/guides/2021-04-15T13:04:40+03:00https://docs.communityhealthtoolkit.org/apps/tutorials/sms-forms/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/reference/targets/2022-12-06T01:01:56-05:00https://docs.communityhealthtoolkit.org/apps/reference/tasks/2023-04-20T11:51:49+12:00https://docs.communityhealthtoolkit.org/core/overview/translations/2024-07-09T09:26:15+02:00https://docs.communityhealthtoolkit.org/apps/tutorials/2020-06-03T10:27:10-07:00https://docs.communityhealthtoolkit.org/apps/guides/forms/uhc-mode/2024-07-26T10:34:32-04:00https://docs.communityhealthtoolkit.org/apps/concepts/workflows/2020-12-10T15:03:59-05:00https://docs.communityhealthtoolkit.org/design/personas/app-builder/2020-05-30T19:36:05-04:00https://docs.communityhealthtoolkit.org/apps/tutorials/application-settings/2024-03-28T18:22:13+00:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/code-health/2023-04-04T10:38:58+12:00https://docs.communityhealthtoolkit.org/apps/concepts/hierarchy/2024-05-24T16:44:04+00:00https://docs.communityhealthtoolkit.org/hosting/2024-06-26T23:11:13-07:00https://docs.communityhealthtoolkit.org/hosting/monitoring/2024-06-21T07:57:58-07:00https://docs.communityhealthtoolkit.org/apps/tutorials/multi-facility-users/2024-07-30T14:57:50+03:00https://docs.communityhealthtoolkit.org/apps/guides/2020-06-03T14:28:04-04:00https://docs.communityhealthtoolkit.org/apps/features/reports/2023-04-12T17:35:29+00:00https://docs.communityhealthtoolkit.org/apps/tutorials/sms-schedules/2021-07-16T08:15:33-05:00https://docs.communityhealthtoolkit.org/contribute/code/workflow/2024-08-02T00:53:01-07:00https://docs.communityhealthtoolkit.org/apps/tutorials/app-forms/2024-01-10T20:47:25+07:00https://docs.communityhealthtoolkit.org/apps/reference/2024-04-17T08:14:28+01:00https://docs.communityhealthtoolkit.org/contribute/code/releasing/2023-04-18T12:42:52+00:00https://docs.communityhealthtoolkit.org/apps/features/supervision/2024-06-21T12:15:34-04:00https://docs.communityhealthtoolkit.org/apps/features/tasks/2022-07-19T14:00:29-04:00https://docs.communityhealthtoolkit.org/contribute/medic/product-development-process/transparency-accountability/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/concepts/users/2022-01-13T15:14:41-05:00https://docs.communityhealthtoolkit.org/contribute/code/repository-checklist/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/contribute/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/tutorials/form-properties/2021-07-16T08:15:33-05:00https://docs.communityhealthtoolkit.org/apps/concepts/interoperability/2024-05-31T15:56:31+03:00https://docs.communityhealthtoolkit.org/design/personas/partners/2020-06-01T11:16:07-07:00https://docs.communityhealthtoolkit.org/apps/features/targets/2023-12-13T07:55:15+03:00https://docs.communityhealthtoolkit.org/contribute/code/core/update-dependencies/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/concepts/prerequisites/2023-05-14T21:33:28+03:00https://docs.communityhealthtoolkit.org/contribute/code/style-guide/2023-11-28T15:41:06-06:00https://docs.communityhealthtoolkit.org/apps/tutorials/tasks-1/2022-04-25T14:22:54+12:00https://docs.communityhealthtoolkit.org/apps/features/training/2023-04-11T06:24:09+07:00https://docs.communityhealthtoolkit.org/hosting/3.x/ec2-setup-guide/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/contribute/code/cht-conf/2024-07-23T08:50:07-06:00https://docs.communityhealthtoolkit.org/contribute/code/core/2022-12-13T09:17:47+13:00https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/2021-11-08T18:53:31-03:00https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/configuration/2022-12-13T09:17:47+13:00https://docs.communityhealthtoolkit.org/hosting/4.x/data-migration/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/glossary/2024-06-13T12:21:44-07:00https://docs.communityhealthtoolkit.org/apps/features/muting/2023-03-09T15:46:54+07:00https://docs.communityhealthtoolkit.org/core/overview/roadmap/2023-05-14T14:06:36-07:00https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/single-node/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/contribute/docs/style-guide/2024-07-09T09:26:15+02:00https://docs.communityhealthtoolkit.org/apps/tutorials/targets/2022-02-08T10:50:49+03:00https://docs.communityhealthtoolkit.org/contribute/code/using-npm/2024-05-16T09:09:00+00:00https://docs.communityhealthtoolkit.org/apps/tutorials/contact-summary/2021-07-16T08:15:33-05:00https://docs.communityhealthtoolkit.org/apps/tutorials/death-reporting/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/contribute/code/static-analysis/2024-07-08T10:32:46+03:00https://docs.communityhealthtoolkit.org/apps/features/uhc-mode/2023-03-09T15:46:54+07:00https://docs.communityhealthtoolkit.org/apps/features/admin/2023-03-09T15:46:54+07:00https://docs.communityhealthtoolkit.org/contribute/code/core/automated-tests/2024-07-29T19:01:09+07:00https://docs.communityhealthtoolkit.org/apps/tutorials/condition-cards/2023-03-09T11:48:26+03:00https://docs.communityhealthtoolkit.org/contribute/code/design-docs/2024-07-17T17:07:07+03:00https://docs.communityhealthtoolkit.org/contribute/code/core/style-guide-automated-e2e-tests/2024-07-19T09:45:38-06:00https://docs.communityhealthtoolkit.org/apps/guides/android/branding/2024-07-26T09:00:42+00:00https://docs.communityhealthtoolkit.org/apps/tutorials/localizing-cht/2024-01-15T13:28:55+02:00https://docs.communityhealthtoolkit.org/apps/tutorials/application-graphics/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/tutorials/application-tests/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/data/users-bulk-load/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/data/impact-metrics/2024-03-08T16:16:00+00:00https://docs.communityhealthtoolkit.org/apps/guides/training/onboarding/2023-03-09T15:46:54+07:00https://docs.communityhealthtoolkit.org/apps/guides/training/training-cards/2023-04-11T06:24:09+07:00https://docs.communityhealthtoolkit.org/apps/guides/training/training-cards-resources/2023-05-12T08:24:25+07:00https://docs.communityhealthtoolkit.org/apps/guides/data/training-instance/2023-03-09T15:46:54+07:00https://docs.communityhealthtoolkit.org/contribute/code/core/using-windows/2023-08-21T14:01:53+12:00https://docs.communityhealthtoolkit.org/apps/guides/data/invalid-reports/2024-03-08T16:16:00+00:00https://docs.communityhealthtoolkit.org/contribute/code/core/run-multiple-chrome-versions/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/data/csv-to-docs/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/africas-talking/2022-07-22T08:43:15+12:00https://docs.communityhealthtoolkit.org/contribute/code/android/2022-12-13T09:17:47+13:00https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/2021-05-18T17:45:42-04:00https://docs.communityhealthtoolkit.org/apps/features/integrations/2023-03-09T15:46:54+07:00https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/multiple-nodes/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/self-hosting-k3s-multinode/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/phones/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/rapidpro/2022-12-08T13:31:35+01:00https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/rapidpro_cht_gateway/2024-04-29T23:46:36+01:00https://docs.communityhealthtoolkit.org/hosting/3.x/self-hosting/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/apps/guides/messaging/sms-states/2021-11-08T18:53:31-03:00https://docs.communityhealthtoolkit.org/apps/tutorials/user-management-tool/2024-07-25T23:05:14-07:00https://docs.communityhealthtoolkit.org/apps/guides/messaging/shortcodes/2021-05-18T17:45:42-04:00https://docs.communityhealthtoolkit.org/hosting/3.x/app-developer/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/hosting/4.x/app-developer/2024-07-15T22:51:14-07:00https://docs.communityhealthtoolkit.org/contribute/code/hall-of-fame/2024-05-30T07:18:29+02:00https://docs.communityhealthtoolkit.org/hosting/3.x/ssl-cert-install/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/messaging/message-loops/2021-05-18T17:45:42-04:00https://docs.communityhealthtoolkit.org/apps/guides/messaging/gateways/gateway/troubleshooting/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/privacy/privacy-policy/2024-05-29T14:32:42+00:00https://docs.communityhealthtoolkit.org/apps/guides/data/2020-06-05T14:27:01-04:00https://docs.communityhealthtoolkit.org/apps/guides/database/2024-04-18T13:46:39+01:00https://docs.communityhealthtoolkit.org/apps/guides/debugging/2022-02-16T12:15:00+13:00https://docs.communityhealthtoolkit.org/apps/guides/forms/2020-06-05T14:27:01-04:00https://docs.communityhealthtoolkit.org/apps/guides/integrations/2022-07-05T04:16:58+03:00https://docs.communityhealthtoolkit.org/hosting/monitoring/introduction/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/apps/guides/messaging/2020-06-05T14:27:01-04:00https://docs.communityhealthtoolkit.org/hosting/3.x/offline/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/apps/guides/performance/2020-06-05T14:27:01-04:00https://docs.communityhealthtoolkit.org/apps/guides/privacy/2021-04-19T17:19:45-07:00https://docs.communityhealthtoolkit.org/apps/guides/security/2020-06-05T14:27:01-04:00https://docs.communityhealthtoolkit.org/hosting/monitoring/setup/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/tasks/2022-01-07T17:06:14-05:00https://docs.communityhealthtoolkit.org/hosting/4.x/adding-tls-certificates/2024-05-06T23:25:28-07:00https://docs.communityhealthtoolkit.org/apps/guides/training/2023-03-09T15:46:54+07:00https://docs.communityhealthtoolkit.org/apps/guides/updates/2020-06-05T14:27:01-04:00https://docs.communityhealthtoolkit.org/apps/guides/android/2022-12-13T09:17:47+13:00https://docs.communityhealthtoolkit.org/apps/guides/data/analytics/2024-03-08T16:16:00+00:00https://docs.communityhealthtoolkit.org/hosting/monitoring/production/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/hosting/4.x/logs/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/hosting/4.x/backups/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/hosting/monitoring/postgres-ingest/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/hosting/monitoring/integration/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/search/2022-07-27T20:41:57-05:00https://docs.communityhealthtoolkit.org/core/releases/0.4.15-and-earlier/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/core/releases/2.10.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.10.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.10.2/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.10.3/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.11.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.11.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.11.2/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.11.3/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.12.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.12.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.12.2/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.12.3/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.12.4/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.12.5/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.13.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.13.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.13.2/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.13.3/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.13.4/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.13.5/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.13.6/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.13.7/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.14.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.14.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.14.2/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.14.3/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.15.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.16.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.16.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.17.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.18.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.18.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.6.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.6.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.6.2/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.6.3/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.7.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.7.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.7.2/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.7.3/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.8.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.8.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.8.2/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.8.3/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.8.4/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.8.5/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.9.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/2.9.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/3.0.0/2022-02-21T09:32:58-06:00https://docs.communityhealthtoolkit.org/core/releases/3.1.0/2022-02-21T09:32:58-06:00https://docs.communityhealthtoolkit.org/core/releases/3.10.0/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.10.1/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.10.2/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.10.3/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.10.4/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.10.5/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.11.0/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/core/releases/3.11.1/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.11.2/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.11.3/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.12.0/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.12.1/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.13.0/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.14.0/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.14.1/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.14.2/2022-11-04T11:12:06-07:00https://docs.communityhealthtoolkit.org/core/releases/3.15.0/2022-05-05T09:06:44+12:00https://docs.communityhealthtoolkit.org/core/releases/3.16.0/2022-08-10T08:20:54-05:00https://docs.communityhealthtoolkit.org/core/releases/3.16.1/2022-12-02T09:34:55+02:00https://docs.communityhealthtoolkit.org/core/releases/3.17.0/2022-10-11T09:43:26+07:00https://docs.communityhealthtoolkit.org/core/releases/3.17.1/2022-12-02T09:35:08+02:00https://docs.communityhealthtoolkit.org/core/releases/3.17.2/2023-10-10T19:56:20+13:00https://docs.communityhealthtoolkit.org/core/releases/3.2.0/2022-10-31T16:02:09-04:00https://docs.communityhealthtoolkit.org/core/releases/3.2.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/3.3.0/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/3.4.0/2022-01-14T11:19:07-05:00https://docs.communityhealthtoolkit.org/core/releases/3.4.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/3.5.0/2022-01-14T11:19:07-05:00https://docs.communityhealthtoolkit.org/core/releases/3.6.0/2022-01-14T11:19:07-05:00https://docs.communityhealthtoolkit.org/core/releases/3.6.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/3.6.2/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/3.7.0/2022-08-01T16:02:39+12:00https://docs.communityhealthtoolkit.org/core/releases/3.7.1/2021-11-24T11:32:41+13:00https://docs.communityhealthtoolkit.org/core/releases/3.8.0/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/core/releases/3.8.1/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/core/releases/3.8.2/2022-01-14T11:19:07-05:00https://docs.communityhealthtoolkit.org/core/releases/3.9.0/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/core/releases/3.9.1/2022-01-14T11:19:07-05:00https://docs.communityhealthtoolkit.org/core/releases/3.9.2/2022-01-14T11:19:07-05:00https://docs.communityhealthtoolkit.org/core/releases/4.0.0/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.0.1/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.1.0/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.1.1/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.1.2/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.2.0/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.2.1/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.2.2/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.2.3/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.2.4/2023-10-10T19:56:20+13:00https://docs.communityhealthtoolkit.org/core/releases/4.3.0/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.3.1/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.3.2/2023-10-10T19:56:20+13:00https://docs.communityhealthtoolkit.org/core/releases/4.4.0/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.4.1/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.4.2/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.5.0/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.5.1/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.5.2/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.6.0/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.7.0/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.7.1/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.7.2/2024-06-20T11:15:30+03:00https://docs.communityhealthtoolkit.org/core/releases/4.8.0/2024-06-20T01:18:39-07:00https://docs.communityhealthtoolkit.org/core/releases/4.8.1/2024-06-20T11:15:30+03:00https://docs.communityhealthtoolkit.org/core/releases/4.9.0/2024-07-08T21:49:46-07:00https://docs.communityhealthtoolkit.org/apps/guides/forms/additional-docs/2022-06-17T14:30:45-05:00https://docs.communityhealthtoolkit.org/apps/guides/forms/app-form-sms/2023-06-27T05:11:38+05:45https://docs.communityhealthtoolkit.org/apps/guides/debugging/obtaining-logs/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/apps/reference/_partial_cht_api/2023-04-06T14:38:13-04:00https://docs.communityhealthtoolkit.org/apps/tutorials/tasks-2/2023-04-06T14:38:13-04:00https://docs.communityhealthtoolkit.org/apps/guides/database/muting_in_dashboards/2021-10-08T08:43:53+03:00https://docs.communityhealthtoolkit.org/apps/examples/contact-tracing/2020-09-23T18:04:11-04:00https://docs.communityhealthtoolkit.org/apps/guides/database/couch2pg-oom-errors/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/apps/guides/database/couchdb-authentication/2022-11-22T08:13:16+13:00https://docs.communityhealthtoolkit.org/apps/examples/phc-covid/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/examples/covid-education/2021-01-11T16:12:16-05:00https://docs.communityhealthtoolkit.org/apps/examples/covid-rdt-reference-app/2021-09-27T06:46:22-07:00https://docs.communityhealthtoolkit.org/apps/guides/database/database-conflicts/2024-05-17T10:49:48+05:45https://docs.communityhealthtoolkit.org/apps/guides/integrations/dhis2-aggregate/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/examples/direct-to-client/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/hosting/4.x/_partial_docker_directories/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/apps/examples/ebs/2020-09-23T18:04:11-04:00https://docs.communityhealthtoolkit.org/apps/guides/updates/feature-flags/2023-05-04T10:39:42-04:00https://docs.communityhealthtoolkit.org/apps/guides/forms/google-drive/2021-07-16T08:15:33-05:00https://docs.communityhealthtoolkit.org/apps/guides/forms/form-inputs/2024-04-09T09:47:10-05:00https://docs.communityhealthtoolkit.org/apps/examples/learning-care/2021-03-03T16:27:50-05:00https://docs.communityhealthtoolkit.org/apps/tutorials/_partial_docker_setup/2023-10-06T16:32:03+02:00https://docs.communityhealthtoolkit.org/apps/examples/interoperability/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/privacy/policy/2023-06-29T09:58:29+12:00https://docs.communityhealthtoolkit.org/design/mockups/2023-03-10T03:00:38+03:00https://docs.communityhealthtoolkit.org/apps/guides/updates/moving-contacts/2021-10-22T17:25:03-04:00https://docs.communityhealthtoolkit.org/apps/guides/forms/multimedia/2022-06-17T14:30:45-05:00https://docs.communityhealthtoolkit.org/apps/guides/integrations/openmrs/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/integrations/oppia/2021-07-16T08:15:33-05:00https://docs.communityhealthtoolkit.org/apps/guides/tasks/pass-data-to-form/2023-04-06T14:38:13-04:00https://docs.communityhealthtoolkit.org/apps/examples/pharmacovigilance-reference-app/2024-01-16T12:23:53+03:00https://docs.communityhealthtoolkit.org/apps/examples/pih/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/updates/preparing-for-4/2024-06-25T22:34:06-07:00https://docs.communityhealthtoolkit.org/apps/guides/debugging/replicating-production-data-locally/2023-08-21T14:01:53+12:00https://docs.communityhealthtoolkit.org/apps/guides/performance/purging/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/database/querying_apdex_telemetry/2024-03-29T11:37:33+07:00https://docs.communityhealthtoolkit.org/apps/guides/tasks/query-task-data/2021-11-29T11:05:14-08:00https://docs.communityhealthtoolkit.org/apps/guides/forms/wealth-quintiles/2020-12-16T14:58:49-05:00https://docs.communityhealthtoolkit.org/apps/guides/integrations/rapidpro/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/database/rdbms-from-mac/2024-01-25T09:05:05+03:00https://docs.communityhealthtoolkit.org/apps/guides/database/rdbms-from-windows/2020-08-18T10:29:41-07:00https://docs.communityhealthtoolkit.org/apps/guides/updates/collect-forms-update/2021-07-16T08:15:33-05:00https://docs.communityhealthtoolkit.org/apps/examples/training/2023-03-09T15:46:54+07:00https://docs.communityhealthtoolkit.org/apps/guides/performance/replication/2024-06-21T12:15:34-04:00https://docs.communityhealthtoolkit.org/apps/guides/forms/report-titles/2020-06-05T14:27:01-04:00https://docs.communityhealthtoolkit.org/apps/guides/security/securely-onboarding-users-at-scale/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/apps/guides/debugging/secure-sharing-of-developer-instance/2022-12-13T09:17:47+13:00https://docs.communityhealthtoolkit.org/apps/guides/security/securing-android/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/apps/guides/debugging/sharing-4x-logs/2024-05-02T05:35:26-07:00https://docs.communityhealthtoolkit.org/apps/examples/stock-monitoring/2022-08-31T13:55:06-07:00https://docs.communityhealthtoolkit.org/apps/examples/supervisor-reference-app/2023-03-18T15:29:21+03:00https://docs.communityhealthtoolkit.org/apps/guides/tasks/task-schema-parameters/2021-11-29T11:05:14-08:00https://docs.communityhealthtoolkit.org/apps/guides/performance/telemetry/2024-06-12T09:11:12+02:00https://docs.communityhealthtoolkit.org/hosting/4.x/self-hosting/_partial_upgrade_service/2024-05-24T15:46:35-05:00https://docs.communityhealthtoolkit.org/apps/reference/_partial_utils/2023-04-06T14:38:13-04:00https://docs.communityhealthtoolkit.org/apps/guides/forms/versioning/2022-08-05T10:25:51+12:00 \ No newline at end of file diff --git a/webfonts/fa-brands-400.ttf b/webfonts/fa-brands-400.ttf index 989f323b1e..8c8a402329 100644 Binary files a/webfonts/fa-brands-400.ttf and b/webfonts/fa-brands-400.ttf differ diff --git a/webfonts/fa-brands-400.woff2 b/webfonts/fa-brands-400.woff2 index 19f04b901e..64bf1ea454 100644 Binary files a/webfonts/fa-brands-400.woff2 and b/webfonts/fa-brands-400.woff2 differ diff --git a/webfonts/fa-regular-400.ttf b/webfonts/fa-regular-400.ttf index 201cc58b85..3d041f4d4a 100644 Binary files a/webfonts/fa-regular-400.ttf and b/webfonts/fa-regular-400.ttf differ diff --git a/webfonts/fa-regular-400.woff2 b/webfonts/fa-regular-400.woff2 index a395e91bbf..ed14925f29 100644 Binary files a/webfonts/fa-regular-400.woff2 and b/webfonts/fa-regular-400.woff2 differ diff --git a/webfonts/fa-solid-900.ttf b/webfonts/fa-solid-900.ttf index 1920af1e52..19a4d2bb1a 100644 Binary files a/webfonts/fa-solid-900.ttf and b/webfonts/fa-solid-900.ttf differ diff --git a/webfonts/fa-solid-900.woff2 b/webfonts/fa-solid-900.woff2 index a9f37fd136..1acf070801 100644 Binary files a/webfonts/fa-solid-900.woff2 and b/webfonts/fa-solid-900.woff2 differ diff --git a/webfonts/fa-v4compatibility.ttf b/webfonts/fa-v4compatibility.ttf index 4c4c5b3ee3..0b7ac89fb7 100644 Binary files a/webfonts/fa-v4compatibility.ttf and b/webfonts/fa-v4compatibility.ttf differ diff --git a/webfonts/fa-v4compatibility.woff2 b/webfonts/fa-v4compatibility.woff2 index 507a2ff51c..e7a9381786 100644 Binary files a/webfonts/fa-v4compatibility.woff2 and b/webfonts/fa-v4compatibility.woff2 differ diff --git a/why-the-cht/index.html b/why-the-cht/index.html index d2d82967b2..dd57c1300b 100644 --- a/why-the-cht/index.html +++ b/why-the-cht/index.html @@ -1,9 +1,9 @@ -Why the Community Health Toolkit? | Community Health Toolkit +Why the Community Health Toolkit? | Community Health Toolkit
    \ No newline at end of file